def test_procurementSecurity(self) -> None: """ Once a session is negotiated, it should be the identical object to avoid duplicate work - unless we are using forceInsecure to retrieve the insecure session from a secure request, in which case the result should not be cached. """ sessions = [] mss = MemorySessionStore() router = Klein() @router.route("/") @inlineCallbacks def route(request: IRequest) -> Deferred: sproc = SessionProcurer(mss) sessions.append((yield sproc.procureSession(request))) sessions.append((yield sproc.procureSession(request))) sessions.append((yield sproc.procureSession(request, forceInsecure=True))) returnValue(b"sessioned") treq = StubTreq(router.resource()) self.successResultOf(treq.get("http://unittest.example.com/")) self.assertIs(sessions[0], sessions[1]) self.assertIs(sessions[0], sessions[2]) self.successResultOf(treq.get("https://unittest.example.com/")) self.assertIs(sessions[3], sessions[4]) self.assertIsNot(sessions[3], sessions[5])
def test_handles_invalid_schemes(self): """ Invalid URLs errback with a :obj:`SchemeNotSupported` failure, and does so even after a successful request. """ stub = StubTreq(_StaticTestResource()) self.failureResultOf(stub.get(""), SchemeNotSupported) self.successResultOf(stub.get("http://url.com")) self.failureResultOf(stub.get(""), SchemeNotSupported)
def test_content(self): client = StubTreq(StaticResource(content=b'abcd')) response = self.successResultOf(client.get('http://an.example/')) self.assertEqual(200, response.code) self.assertEqual([b'application/xml'], response.headers.getRawHeaders(b'Content-Type')) body = self.successResultOf(response.content()) self.assertEqual(b'abcd', body)
def test_rendering(self): # type: () -> None """ When a route requires form fields, it renders a form with those fields. """ mem = MemorySessionStore() session = self.successResultOf( mem.newSession(True, SessionMechanism.Header)) stub = StubTreq(TestObject(mem).router.resource()) response = self.successResultOf( stub.get( "https://localhost/render", headers={b"X-Test-Session": session.identifier}, )) self.assertEqual(response.code, 200) self.assertIn( response.headers.getRawHeaders(b"content-type")[0], b"text/html") responseDom = ElementTree.fromstring( self.successResultOf(content(response))) submitButton = responseDom.findall(".//*[@type='submit']") self.assertEqual(len(submitButton), 1) self.assertEqual(submitButton[0].attrib["name"], "__klein_auto_submit__")
def test_customParameterValidation(self): # type: () -> None """ When a custom parameter fails to validate by raising ValueError - for example, a non-number passed to a numeric Field, the request fails with a 400 and the default validation failure handler displays a form which explains the error. """ router, calls = simpleFormRouter() stub = StubTreq(router.resource()) response = self.successResultOf( stub.get(b"https://localhost/getme?" b"name=hello,%20big+world&value=0&custom=not+a+number")) responseForm = self.successResultOf(content(response)) self.assertEqual(response.code, 400) self.assertEqual(calls, []) responseForm = self.successResultOf(content(response)) responseDom = ElementTree.fromstring(responseForm) errors = responseDom.findall( ".//*[@class='klein-form-validation-error']") self.assertEqual(len(errors), 1) errorText = cast(str, errors[0].text) self.assertIsNot(errorText, None) self.assertEqual( errorText, "invalid literal for int() with base 10: 'not a number'", )
def test_renderingFormGlue(self): # type: () -> None """ When a form renderer renders just the glue, none of the rest of the form is included. """ mem = MemorySessionStore() session = self.successResultOf( mem.newSession(True, SessionMechanism.Header)) stub = StubTreq(TestObject(mem).router.resource()) response = self.successResultOf( stub.get( "https://localhost/render-custom", headers={b"X-Test-Session": session.identifier}, )) self.assertEqual(response.code, 200) self.assertIn( response.headers.getRawHeaders(b"content-type")[0], b"text/html") responseDom = ElementTree.fromstring( self.successResultOf(content(response))) submitButton = responseDom.findall(".//*[@type='submit']") self.assertEqual(len(submitButton), 0) protectionField = responseDom.findall( ".//*[@name='__csrf_protection__']") self.assertEqual(protectionField[0].attrib["value"], session.identifier)
def test_renderingEmptyForm(self): # type: () -> None """ When a form renderer specifies a submit button, no automatic submit button is rendered. """ mem = MemorySessionStore() session = self.successResultOf( mem.newSession(True, SessionMechanism.Header)) stub = StubTreq(TestObject(mem).router.resource()) response = self.successResultOf( stub.get( "https://localhost/render-empty", headers={b"X-Test-Session": session.identifier}, )) self.assertEqual(response.code, 200) self.assertIn( response.headers.getRawHeaders(b"content-type")[0], b"text/html") responseDom = ElementTree.fromstring( self.successResultOf(content(response))) submitButton = responseDom.findall(".//*[@type='submit']") self.assertEqual(len(submitButton), 1) self.assertEqual(submitButton[0].attrib["name"], "__klein_auto_submit__") protectionField = responseDom.findall( ".//*[@name='__csrf_protection__']") self.assertEqual(protectionField[0].attrib["value"], session.identifier)
def test_content_type(self): client = StubTreq(StaticResource(content=b'hello', content_type='text/plain')) response = self.successResultOf(client.get('http://an.example/')) self.assertEqual(200, response.code) self.assertEqual([b'text/plain'], response.headers.getRawHeaders(b'Content-Type')) body = self.successResultOf(response.content()) self.assertEqual(b'hello', body)
def test_consume_context_manager_fails_on_remaining_requests(self): """ If the `consume` context manager is used, if there are any remaining expecting requests, the test case will be failed. """ sequence = RequestSequence( [((ANY, ANY, ANY, ANY, ANY), (418, {}, 'body'))] * 2, async_failure_reporter=self.async_failures.append) stub = StubTreq(StringStubbingResource(sequence)) consume_failures = [] with sequence.consume(sync_failure_reporter=consume_failures.append): self.successResultOf(stub.get('https://anything', data='what', headers={'1': '1'})) self.assertEqual(1, len(consume_failures)) self.assertIn( "Not all expected requests were made. Still expecting:", consume_failures[0]) self.assertIn( "{0}(url={0}, params={0}, headers={0}, data={0})".format( repr(ANY)), consume_failures[0]) # no asynchronous failures (mismatches, etc.) self.assertEqual([], self.async_failures)
def test_start_responding(self, token): """ Calling ``start_responding`` makes an appropriate resource available. """ challenge = challenges.HTTP01(token=token) response = challenge.response(RSA_KEY_512) responder = HTTP01Responder() challenge_resource = Resource() challenge_resource.putChild(b'acme-challenge', responder.resource) root = Resource() root.putChild(b'.well-known', challenge_resource) client = StubTreq(root) encoded_token = challenge.encode('token') challenge_url = URL( host=u'example.com', path=[u'.well-known', u'acme-challenge', encoded_token]).asText() self.assertThat(client.get(challenge_url), succeeded(MatchesStructure(code=Equals(404)))) responder.start_responding(u'example.com', challenge, response) self.assertThat( client.get(challenge_url), succeeded( MatchesAll( MatchesStructure(code=Equals(200), headers=AfterPreprocessing( methodcaller('getRawHeaders', b'content-type'), Equals([b'text/plain']))), AfterPreprocessing( methodcaller('content'), succeeded( Equals(response.key_authorization.encode( 'utf-8'))))))) # Starting twice before stopping doesn't break things responder.start_responding(u'example.com', challenge, response) self.assertThat(client.get(challenge_url), succeeded(MatchesStructure(code=Equals(200)))) responder.stop_responding(u'example.com', challenge, response) self.assertThat(client.get(challenge_url), succeeded(MatchesStructure(code=Equals(404))))
def test_content(self): client = StubTreq(StaticResource(content=b"abcd")) response = self.successResultOf(client.get("http://an.example/")) self.assertEqual(200, response.code) self.assertEqual([b"application/xml"], response.headers.getRawHeaders(b"Content-Type")) body = self.successResultOf(response.content()) self.assertEqual(b"abcd", body)
def test_no_content_type(self): """ Unlike `twisted.web.static.Data`, `StaticResource` can generate a response with no ``Content-Type`` header. """ client = StubTreq(StaticResource(content=b"hello", content_type=None)) response = self.successResultOf(client.get("http://an.example/")) self.assertEqual(None, response.headers.getRawHeaders(b"Content-Type"))
def test_start_responding(self): """ Calling ``start_responding`` makes an appropriate resource available. """ token = b'BWYcfxzmOha7-7LoxziqPZIUr99BCz3BfbN9kzSFnrU' challenge = challenges.HTTP01(token=token) response = challenge.response(RSA_KEY_512) responder = HTTP01Responder() challenge_resource = Resource() challenge_resource.putChild(b'acme-challenge', responder.resource) root = Resource() root.putChild(b'.well-known', challenge_resource) client = StubTreq(root) encoded_token = challenge.encode('token') challenge_url = URL( host=u'example.com', path=[u'.well-known', u'acme-challenge', encoded_token]).asText() # We got page not found while the challenge is not yet active. result = yield client.get(challenge_url) self.assertEqual(404, result.code) # Once we enable the response. responder.start_responding(u'example.com', challenge, response) result = yield client.get(challenge_url) self.assertEqual(200, result.code) self.assertEqual(['text/plain'], result.headers.getRawHeaders('content-type')) result = yield result.content() self.assertEqual(response.key_authorization.encode('utf-8'), result) # Starting twice before stopping doesn't break things responder.start_responding(u'example.com', challenge, response) result = yield client.get(challenge_url) self.assertEqual(200, result.code) yield responder.stop_responding(u'example.com', challenge, response) result = yield client.get(challenge_url) self.assertEqual(404, result.code)
def test_content_type(self): client = StubTreq( StaticResource(content=b"hello", content_type="text/plain")) response = self.successResultOf(client.get("http://an.example/")) self.assertEqual(200, response.code) self.assertEqual([b"text/plain"], response.headers.getRawHeaders(b"Content-Type")) body = self.successResultOf(response.content()) self.assertEqual(b"hello", body)
def test_no_match(self): client = StubTreq(StaticEtagResource(b"abcd", b'"abcd"')) response = self.successResultOf(client.get("http://an.example/")) self.assertEqual(200, response.code) self.assertEqual(["application/xml"], response.headers.getRawHeaders("Content-Type")) self.assertEqual(['"abcd"'], response.headers.getRawHeaders("Etag")) body = self.successResultOf(response.content()) self.assertEqual(b"abcd", body)
def test_authorized(self, auth_token, child_segments, content): """ If the correct bearer token is not given in the **Authorization** header of the request then the response code is UNAUTHORIZED. :param bytes auth_token: A bearer token which, when presented, should authorize access to the resource. :param [unicode] child_segments: Additional path segments to add to the request path beneath **v1**. :param bytes content: The bytes we expect to see on a successful request. """ def get_auth_token(): return auth_token # Since we don't want to exercise any real magic-folder application # logic we'll just magic up the child resource being requested. branch = Data( content, b"application/binary", ) segments_remaining = child_segments[:] while segments_remaining: name = segments_remaining.pop() resource = Resource() resource.putChild(name.encode("utf-8"), branch) branch = resource root = magic_folder_resource( get_auth_token, v1_resource=branch, ) treq = StubTreq(root) url = DecodedURL.from_text(u"http://example.invalid./v1").child( *child_segments) encoded_url = url_to_bytes(url) # A request with no token at all or the wrong token should receive an # unauthorized response. headers = { b"Authorization": u"Bearer {}".format(auth_token).encode("ascii"), } self.assertThat( treq.get( encoded_url, headers=headers, ), succeeded( matches_response( code_matcher=Equals(OK), body_matcher=Equals(content), ), ), )
def test_unicode_deferred(self): """ Test a unicode response with a deferred result. """ treq = StubTreq(self.get_resource()) r = yield treq.get("http://localhost:8080/defer-unicode") self.assertEqual(r.code, 200) c = yield r.text() self.assertIsInstance(c, six.text_type) self.assertEqual(c, u"unicode_string")
def test_unicode_decode_raw(self): """ Test a unicode response without decoding. """ treq = StubTreq(self.get_resource()) r = yield treq.get("http://localhost:8080/unicode") self.assertEqual(r.code, 200) c = yield r.content() self.assertIsInstance(c, six.binary_type) self.assertEqual(c, b"unicode_string")
def test_match(self): client = StubTreq(StaticEtagResource(b'abcd', b'"abcd"')) response = self.successResultOf(client.get('http://an.example/', headers={ 'if-none-match': ['"abcd"'], })) self.assertEqual(304, response.code) self.assertEqual(['application/xml'], response.headers.getRawHeaders('Content-Type')) self.assertEqual(['"abcd"'], response.headers.getRawHeaders('Etag')) body = self.successResultOf(response.content()) self.assertEqual(b'', body)
def test_binary_deferred(self): """ Test a binary response with a deferred result. """ treq = StubTreq(self.get_resource()) r = yield treq.get("http://localhost:8080/defer-binary") self.assertEqual(r.code, 200) c = yield r.content() self.assertIsInstance(c, six.binary_type) self.assertEqual(c, b"binary_string")
def test_no_match(self): client = StubTreq(StaticLastModifiedResource( content=b'abcd', last_modified=u'Mon, 6 Feb 2017 00:00:00 GMT', )) response = self.successResultOf(client.get('http://an.example/')) self.assertEqual(200, response.code) self.assertEqual([u'Mon, 6 Feb 2017 00:00:00 GMT'], response.headers.getRawHeaders(u'Last-Modified')) body = self.successResultOf(response.content()) self.assertEqual(b'abcd', body)
class FeedAggregationTests(SynchronousTestCase): def setUp(self): service = StubFeed({ URL.from_text(feed._source).host.encode('ascii'): makeXML(feed) for feed in FEEDS }) treq = StubTreq(service.resource()) urls = [feed._source for feed in FEEDS] retriever = FeedRetrieval(treq) self.client = StubTreq( FeedAggregation(retriever.retrieve, urls).resource()) @defer.inlineCallbacks def get(self, url): response = yield self.client.get(url) self.assertEqual(response.code, 200) content = yield response.content() defer.returnValue(content) def test_renderHTML(self): content = self.successResultOf(self.get("http://test.invalid/")) parsed = html.fromstring(content) self.assertEqual(parsed.xpath('/html/body/div/table/tr/th/a/text()'), ["First feed", "Second feed"]) self.assertEqual(parsed.xpath('/html/body/div/table/tr/th/a/@href'), ["http://feed-1/", "http://feed-2/"]) self.assertEqual(parsed.xpath('/html/body/div/table/tr/td/a/text()'), ["First item", "Second item"]) self.assertEqual(parsed.xpath('/html/body/div/table/tr/td/a/@href'), ["#first", "#second"]) def test_renderJSON(self): content = self.successResultOf( self.get("http://test.invalid/?json=true")) parsed = json.loads(content) self.assertEqual( parsed, { "feeds": [{ "title": "First feed", "link": "http://feed-1/", "items": [{ "title": "First item", "link": "#first" }] }, { "title": "Second feed", "link": "http://feed-2/", "items": [{ "title": "Second item", "link": "#second" }] }] })
def test_start_responding(self, token): """ Calling ``start_responding`` makes an appropriate resource available. """ challenge = challenges.HTTP01(token=token) response = challenge.response(RSA_KEY_512) responder = HTTP01Responder() challenge_resource = Resource() challenge_resource.putChild(b'acme-challenge', responder.resource) root = Resource() root.putChild(b'.well-known', challenge_resource) client = StubTreq(root) encoded_token = challenge.encode('token') challenge_url = URL(host=u'example.com', path=[ u'.well-known', u'acme-challenge', encoded_token]).asText() self.assertThat(client.get(challenge_url), succeeded(MatchesStructure(code=Equals(404)))) responder.start_responding(u'example.com', challenge, response) self.assertThat(client.get(challenge_url), succeeded(MatchesAll( MatchesStructure( code=Equals(200), headers=AfterPreprocessing( methodcaller('getRawHeaders', b'content-type'), Equals([b'text/plain']))), AfterPreprocessing(methodcaller('content'), succeeded( Equals(response.key_authorization.encode('utf-8')))) ))) # Starting twice before stopping doesn't break things responder.start_responding(u'example.com', challenge, response) self.assertThat(client.get(challenge_url), succeeded(MatchesStructure(code=Equals(200)))) responder.stop_responding(u'example.com', challenge, response) self.assertThat(client.get(challenge_url), succeeded(MatchesStructure(code=Equals(404))))
def test_renderingWithNoSessionYet(self) -> None: """ When a route is rendered with no session, it sets a cookie to establish a new session. """ mem = MemorySessionStore() stub = StubTreq(TestObject(mem).router.resource()) response = self.successResultOf(stub.get("https://localhost/render")) self.assertEqual(response.code, 200) setCookie = response.cookies()["Klein-Secure-Session"] expected = f'value="{setCookie}"' actual = self.successResultOf(content(response)).decode("utf-8") self.assertIn(expected, actual)
def test_async_failures_logged(self): """ When no `async_failure_reporter` is passed async failures are logged by default. """ sequence = RequestSequence([]) stub = StubTreq(StringStubbingResource(sequence)) with sequence.consume(self.fail): self.successResultOf(stub.get('https://example.com')) [failure] = self.flushLoggedErrors() self.assertIsInstance(failure.value, AssertionError)
def test_no_match(self): client = StubTreq( StaticLastModifiedResource( content=b"abcd", last_modified="Mon, 6 Feb 2017 00:00:00 GMT", )) response = self.successResultOf(client.get("http://an.example/")) self.assertEqual(200, response.code) self.assertEqual( ["Mon, 6 Feb 2017 00:00:00 GMT"], response.headers.getRawHeaders("Last-Modified"), ) body = self.successResultOf(response.content()) self.assertEqual(b"abcd", body)
def test_unexpected_number_of_request_causes_failure(self): """ If there are no more expected requests, making a request causes a failure. """ sequence = RequestSequence([], async_failure_reporter=self.async_failures.append) stub = StubTreq(StringStubbingResource(sequence)) d = stub.get("https://anything", data=b"what", headers={b"1": b"1"}) resp = self.successResultOf(d) self.assertEqual(500, resp.code) self.assertEqual(1, len(self.async_failures)) self.assertIn("No more requests expected, but request", self.async_failures[0]) # the expected requests have all been made self.assertTrue(sequence.consumed())
def test_handlingGET(self) -> None: """ A GET handler for a Form with Fields receives query parameters matching those field names as input. """ router, calls = simpleFormRouter() stub = StubTreq(router.resource()) response = self.successResultOf( stub.get( b"https://localhost/getme?name=hello,%20big+world&value=4321")) self.assertEqual(response.code, 200) self.assertEqual(self.successResultOf(content(response)), b"got") self.assertEqual(calls, [("hello, big world", 4321)])
def test_renderingWithNoSessionYet(self): # type: () -> None """ When a route is rendered with no session, it sets a cookie to establish a new session. """ mem = MemorySessionStore() stub = StubTreq(TestObject(mem).router.resource()) response = self.successResultOf(stub.get("https://localhost/render")) self.assertEqual(response.code, 200) setCookie = response.cookies()["Klein-Secure-Session"] expected = 'value="{}"'.format(setCookie) actual = self.successResultOf(content(response)) if not isinstance(expected, bytes): # type: ignore[unreachable] actual = actual.decode("utf-8") self.assertIn(expected, actual)
def test_get_response_encoding(self): """ Test L{txwebutils.encutils.get_response_encoding}. """ treq = StubTreq(self.get_resource()) encodings = [b"utf-8", b"latin-1", b"cp500", b"iso-8859-2"] for encoding in encodings: headers = {b"Accept-Charset": encoding} r = yield treq.get("http://localhost:8080/get-encoding", headers=headers) self.assertEqual(r.code, 200) self.assertIn(encoding.decode(u"ascii"), r.headers.getRawHeaders("Content-Type", [None])[0]) c = yield r.text(encoding=encoding) self.assertIsInstance(c, six.text_type) self.assertEqual(c, encoding.decode("ascii"))
def test_unexpected_number_of_request_causes_failure(self): """ If there are no more expected requests, making a request causes a failure. """ sequence = RequestSequence( [], async_failure_reporter=self.async_failures.append) stub = StubTreq(StringStubbingResource(sequence)) d = stub.get('https://anything', data=b'what', headers={b'1': b'1'}) resp = self.successResultOf(d) self.assertEqual(500, resp.code) self.assertEqual(1, len(self.async_failures)) self.assertIn("No more requests expected, but request", self.async_failures[0]) # the expected requests have all been made self.assertTrue(sequence.consumed())
def test_unicode_decode_ex(self): """ Test a unicode response with non-ascii characters and various encodings. """ treq = StubTreq(self.get_resource()) encodings = [b"utf-8", b"latin-1", b"cp500", b"iso-8859-2"] for encoding in encodings: headers = {b"Accept-Charset": encoding} r = yield treq.get("http://localhost:8080/unicode-ex", headers=headers) self.assertEqual(r.code, 200) self.assertIn(encoding.decode(u"ascii"), r.headers.getRawHeaders("Content-Type", [None])[0]) c = yield r.text(encoding=encoding) self.assertIsInstance(c, six.text_type) self.assertEqual(c, u"umlaute: äöüß")
def test_unauthorized(self, good_token, bad_tokens, child_segments): """ If the correct bearer token is not given in the **Authorization** header of the request then the response code is UNAUTHORIZED. :param bytes good_token: A bearer token which, when presented, should authorize access to the resource. :param bad_tokens: A list of bearer token which, when presented all at once, should not authorize access to the resource. If this is empty no tokens are presented at all. If it contains more than one element then it creates a bad request with multiple authorization header values. :param [unicode] child_segments: Additional path segments to add to the request path beneath **v1**. """ # We're trying to test the *unauthorized* case. Don't randomly hit # the authorized case by mistake. assume([good_token] != bad_tokens) def get_auth_token(): return good_token root = magic_folder_resource(get_auth_token, Resource()) treq = StubTreq(root) url = DecodedURL.from_text(u"http://example.invalid./v1").child( *child_segments) encoded_url = url_to_bytes(url) # A request with no token at all or the wrong token should receive an # unauthorized response. headers = {} if bad_tokens: headers[b"Authorization"] = list( u"Bearer {}".format(bad_token).encode("ascii") for bad_token in bad_tokens) self.assertThat( treq.get( encoded_url, headers=headers, ), succeeded(matches_response(code_matcher=Equals(UNAUTHORIZED)), ), )
def test_works_with_mock_any(self): """ :obj:`mock.ANY` can be used with the request parameters. """ sequence = RequestSequence( [((ANY, ANY, ANY, ANY, ANY), (418, {}, 'body'))], async_failure_reporter=self.async_failures.append) stub = StubTreq(StringStubbingResource(sequence)) with sequence.consume(sync_failure_reporter=self.fail): d = stub.get('https://anything', data='what', headers={'1': '1'}) resp = self.successResultOf(d) self.assertEqual(418, resp.code) self.assertEqual('body', self.successResultOf(stub.content(resp))) self.assertEqual([], self.async_failures) # the expected requests have all been made self.assertTrue(sequence.consumed())
def test_works_with_mock_any(self): """ :obj:`mock.ANY` can be used with the request parameters. """ sequence = RequestSequence( [((ANY, ANY, ANY, ANY, ANY), (418, {}, b"body"))], async_failure_reporter=self.async_failures.append ) stub = StubTreq(StringStubbingResource(sequence)) with sequence.consume(sync_failure_reporter=self.fail): d = stub.get("https://anything", data=b"what", headers={b"1": b"1"}) resp = self.successResultOf(d) self.assertEqual(418, resp.code) self.assertEqual(b"body", self.successResultOf(stub.content(resp))) self.assertEqual([], self.async_failures) # the expected requests have all been made self.assertTrue(sequence.consumed())
def test_cookiesTurnedOff(self) -> None: """ If cookies can't be set, then C{procureSession} raises L{NoSuchSession}. """ mss = MemorySessionStore() router = Klein() @router.route("/") @inlineCallbacks def route(request: IRequest) -> Deferred: sproc = SessionProcurer(mss, setCookieOnGET=False) with self.assertRaises(NoSuchSession): yield sproc.procureSession(request) returnValue(b"no session") treq = StubTreq(router.resource()) result = self.successResultOf(treq.get("http://unittest.example.com/")) self.assertEqual(self.successResultOf(result.content()), b"no session")
def test_renderLookupError(self): # type: () -> None """ RenderableForm raises L{MissingRenderMethod} if anything attempst to look up a render method on it. """ mem = MemorySessionStore() session = self.successResultOf( mem.newSession(True, SessionMechanism.Header)) stub = StubTreq(TestObject(mem).router.resource()) response = self.successResultOf( stub.get('https://localhost/render-cascade', headers={b'X-Test-Session': session.identifier})) self.assertEqual(response.code, 200) # print(self.successResultOf(response.content()).decode('utf-8')) failures = self.flushLoggedErrors() self.assertEqual(len(failures), 1) self.assertIn("MissingRenderMethod", str(failures[0]))
class TestMarathonAcmeServer(object): def setup_method(self): self.responder_resource = Resource() self.server = MarathonAcmeServer(self.responder_resource) self.client = StubTreq(self.server.app.resource()) def test_responder_resource_empty(self): """ When a GET request is made to the ACME challenge path, but the responder resource is empty, a 404 response code should be returned. """ response = self.client.get( 'http://localhost/.well-known/acme-challenge/foo') assert_that(response, succeeded(MatchesStructure(code=Equals(404)))) def test_responder_resource_child(self): """ When a GET request is made to the ACME challenge path, and the responder resource has a child resource at the correct path, the value of the resource should be returned. """ self.responder_resource.putChild(b'foo', Data(b'bar', 'text/plain')) response = self.client.get( 'http://localhost/.well-known/acme-challenge/foo') assert_that(response, succeeded(MatchesAll( MatchesStructure( code=Equals(200), headers=HasHeader('Content-Type', ['text/plain'])), After(methodcaller('content'), succeeded(Equals(b'bar'))) ))) # Sanity check that a request to a different subpath does not succeed response = self.client.get( 'http://localhost/.well-known/acme-challenge/baz') assert_that(response, succeeded(MatchesStructure(code=Equals(404)))) def test_acme_challenge_ping(self): """ When a GET request is made to the ACME challenge path ping endpoint, a pong message should be returned. """ response = self.client.get( 'http://localhost/.well-known/acme-challenge/ping') assert_that(response, succeeded(MatchesAll( IsJsonResponseWithCode(200), After(json_content, succeeded(Equals({'message': 'pong'}))) ))) def test_health_healthy(self): """ When a GET request is made to the health endpoint, and the health handler reports that the service is healthy, a 200 status code should be returned together with the JSON message from the handler. """ self.server.set_health_handler( lambda: Health(True, {'message': "I'm 200/OK!"})) response = self.client.get('http://localhost/health') assert_that(response, succeeded(MatchesAll( IsJsonResponseWithCode(200), After(json_content, succeeded(Equals({'message': "I'm 200/OK!"}))) ))) def test_health_unhealthy(self): """ When a GET request is made to the health endpoint, and the health handler reports that the service is unhealthy, a 503 status code should be returned together with the JSON message from the handler. """ self.server.set_health_handler( lambda: Health(False, {'error': "I'm sad :("})) response = self.client.get('http://localhost/health') assert_that(response, succeeded(MatchesAll( IsJsonResponseWithCode(503), After(json_content, succeeded(Equals({'error': "I'm sad :("}))) ))) def test_health_handler_unset(self): """ When a GET request is made to the health endpoint, and the health handler hasn't been set, a 501 status code should be returned together with a JSON message that explains that the handler is not set. """ response = self.client.get('http://localhost/health') assert_that(response, succeeded(MatchesAll( IsJsonResponseWithCode(501), After(json_content, succeeded(Equals({ 'error': 'Cannot determine service health: no handler set' }))) ))) def test_health_handler_unicode(self): """ When a GET request is made to the health endpoint, and the health handler reports that the service is unhealthy, a 503 status code should be returned together with the JSON message from the handler. """ self.server.set_health_handler( lambda: Health(False, {'error': u"I'm sad 🙁"})) response = self.client.get('http://localhost/health') assert_that(response, succeeded(MatchesAll( IsJsonResponseWithCode(503), After(json_content, succeeded(Equals({'error': u"I'm sad 🙁"}))) )))