From b93f0763a280c6e068a27e492764613d93480c4b Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Wed, 1 May 2024 23:13:22 +0000 Subject: [PATCH] refactor: show system enrollment error page if 500 during /token --- .../templates/enrollment/index.html | 6 +++++ benefits/enrollment/urls.py | 1 + benefits/enrollment/views.py | 18 +++++++++++-- tests/pytest/enrollment/test_views.py | 26 +++++++++++++++++++ 4 files changed, 49 insertions(+), 2 deletions(-) diff --git a/benefits/enrollment/templates/enrollment/index.html b/benefits/enrollment/templates/enrollment/index.html index bc231a9f3..c48cf9129 100644 --- a/benefits/enrollment/templates/enrollment/index.html +++ b/benefits/enrollment/templates/enrollment/index.html @@ -41,6 +41,12 @@

$.ajax({ dataType: "script", attrs: { nonce: "{{ request.csp_nonce }}"}, url: "{{ card_tokenize_url }}" }) .done(function() { $.get("{{ access_token_url }}", function(data) { + if (data.redirect) { + // https://stackoverflow.com/a/42469170 + // use 'assign' because 'replace' was giving strange Back button behavior + window.location.assign(data.redirect); + } + $(".loading").remove(); // remove invisible and add back visible, so we aren't left with // a div with an empty class attribute diff --git a/benefits/enrollment/urls.py b/benefits/enrollment/urls.py index 22398bc98..446cf6ae4 100644 --- a/benefits/enrollment/urls.py +++ b/benefits/enrollment/urls.py @@ -14,4 +14,5 @@ path("token", views.token, name="token"), path("retry", views.retry, name="retry"), path("success", views.success, name="success"), + path("system_error", views.system_error, name="system_error"), ] diff --git a/benefits/enrollment/views.py b/benefits/enrollment/views.py index 4c23fa783..00831ffae 100644 --- a/benefits/enrollment/views.py +++ b/benefits/enrollment/views.py @@ -10,6 +10,7 @@ from django.utils.decorators import decorator_from_middleware from littlepay.api.client import Client from requests.exceptions import HTTPError +import sentry_sdk from benefits.core import session from benefits.core.middleware import ( @@ -24,6 +25,7 @@ ROUTE_INDEX = "enrollment:index" ROUTE_RETRY = "enrollment:retry" ROUTE_SUCCESS = "enrollment:success" +ROUTE_SYSTEM_ERROR = "enrollment:system_error" ROUTE_TOKEN = "enrollment:token" TEMPLATE_RETRY = "enrollment/retry.html" @@ -47,8 +49,20 @@ def token(request): audience=payment_processor.audience, ) client.oauth.ensure_active_token(client.token) - response = client.request_card_tokenization_access() - session.update(request, enrollment_token=response.get("access_token"), enrollment_token_exp=response.get("expires_at")) + + try: + response = client.request_card_tokenization_access() + except Exception as e: + if isinstance(e, HTTPError) and e.response.status_code >= 500: + sentry_sdk.capture_exception(e) + data = {"redirect": reverse(ROUTE_SYSTEM_ERROR)} + return JsonResponse(data) + else: + raise e + else: + session.update( + request, enrollment_token=response.get("access_token"), enrollment_token_exp=response.get("expires_at") + ) data = {"token": session.enrollment_token(request)} diff --git a/tests/pytest/enrollment/test_views.py b/tests/pytest/enrollment/test_views.py index f853ecbfc..39d2170d9 100644 --- a/tests/pytest/enrollment/test_views.py +++ b/tests/pytest/enrollment/test_views.py @@ -12,6 +12,7 @@ ROUTE_INDEX, ROUTE_TOKEN, ROUTE_SUCCESS, + ROUTE_SYSTEM_ERROR, ROUTE_RETRY, TEMPLATE_SUCCESS, TEMPLATE_SYSTEM_ERROR, @@ -99,6 +100,31 @@ def test_token_valid(mocker, client): assert data["token"] == "enrollment_token" +@pytest.mark.django_db +@pytest.mark.usefixtures("mocked_session_agency", "mocked_session_eligibility") +def test_token_http_error_500(mocker, client): + mocker.patch("benefits.core.session.enrollment_token_valid", return_value=False) + + mock_client_cls = mocker.patch("benefits.enrollment.views.Client") + mock_client = mock_client_cls.return_value + + mock_error = {"message": "Mock error message"} + mock_error_response = mocker.Mock(status_code=500, **mock_error) + mock_error_response.json.return_value = mock_error + mock_client.request_card_tokenization_access.side_effect = HTTPError( + response=mock_error_response, + ) + + path = reverse(ROUTE_TOKEN) + response = client.get(path) + + assert response.status_code == 200 + data = response.json() + assert "token" not in data + assert "redirect" in data + assert data["redirect"] == reverse(ROUTE_SYSTEM_ERROR) + + @pytest.mark.django_db @pytest.mark.usefixtures("mocked_session_agency", "mocked_session_verifier", "mocked_session_eligibility") def test_index_eligible_get(client, model_EligibilityType):