From baa18596a85b534583e1e2e164cb7011e241ec73 Mon Sep 17 00:00:00 2001 From: Jean-Paul Calderone <exarkun@twistedmatrix.com> Date: Fri, 3 Jul 2020 12:55:33 -0400 Subject: [PATCH] Test controller behavior when the response is not JSON --- src/_zkapauthorizer/controller.py | 9 ++++ src/_zkapauthorizer/tests/test_controller.py | 46 ++++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/src/_zkapauthorizer/controller.py b/src/_zkapauthorizer/controller.py index 3cb4c1d..c2ace39 100644 --- a/src/_zkapauthorizer/controller.py +++ b/src/_zkapauthorizer/controller.py @@ -103,6 +103,15 @@ from .model import ( RETRY_INTERVAL = timedelta(milliseconds=1) +@attr.s +class UnexpectedResponse(Exception): + """ + The issuer responded in an unexpected and unhandled way. + """ + code = attr.ib() + body = attr.ib() + + class AlreadySpent(Exception): """ An attempt was made to redeem a voucher which has already been redeemed. diff --git a/src/_zkapauthorizer/tests/test_controller.py b/src/_zkapauthorizer/tests/test_controller.py index 3e8d6fc..7647440 100644 --- a/src/_zkapauthorizer/tests/test_controller.py +++ b/src/_zkapauthorizer/tests/test_controller.py @@ -50,6 +50,7 @@ from testtools.matchers import ( HasLength, AfterPreprocessing, MatchesStructure, + ContainsDict, ) from testtools.twistedsupport import ( succeeded, @@ -86,6 +87,7 @@ from twisted.web.http_headers import ( from twisted.web.http import ( UNSUPPORTED_MEDIA_TYPE, BAD_REQUEST, + INTERNAL_SERVER_ERROR, ) from treq.testing import ( StubTreq, @@ -111,6 +113,7 @@ from ..controller import ( IndexedRedeemer, RecordingRedeemer, PaymentController, + UnexpectedResponse, AlreadySpent, Unpaid, token_count_for_group, @@ -621,6 +624,39 @@ class RistrettoRedeemerTests(TestCase): ), ) + @given(voucher_objects(), voucher_counters(), integers(min_value=0, max_value=100)) + def test_non_json_response(self, voucher, counter, num_tokens): + """ + If the issuer responds with something that isn't JSON then the response is + logged and the ``Deferred`` fires with a ``Failure`` wrapping + ``UnexpectedResponse``. + """ + issuer = UnexpectedResponseRedemption() + treq = treq_for_loopback_ristretto(issuer) + redeemer = RistrettoRedeemer(treq, NOWHERE) + random_tokens = redeemer.random_tokens_for_voucher(voucher, counter, num_tokens) + + d = redeemer.redeemWithCounter( + voucher, + counter, + random_tokens, + ) + + self.assertThat( + d, + failed( + AfterPreprocessing( + lambda f: f.value, + Equals( + UnexpectedResponse( + INTERNAL_SERVER_ERROR, + b"Sorry, this server does not behave well.", + ), + ), + ), + ), + ) + @given(voucher_objects(), voucher_counters(), integers(min_value=0, max_value=100)) def test_redemption_denied_alreadyspent(self, voucher, counter, extra_tokens): """ @@ -827,6 +863,16 @@ def stub_agent(): return _StubAgent() +class UnexpectedResponseRedemption(Resource): + """ + An ``UnexpectedResponseRedemption`` simulates the Ristretto redemption + server but always returns a non-JSON error response. + """ + def render_POST(self, request): + request.setResponseCode(INTERNAL_SERVER_ERROR) + return b"Sorry, this server does not behave well." + + class AlreadySpentRedemption(Resource): """ An ``AlreadySpentRedemption`` simulates the Ristretto redemption server -- GitLab