Example #1
0
    def test_mismatched_request_causes_failure(self):
        """
        If a request is made that is not expected as the next request,
        causes a failure.
        """
        sequence = RequestSequence(
            [
                (
                    (b"get", "https://anything/", {b"1": [b"2"]}, HasHeaders({b"1": [b"1"]}), b"what"),
                    (418, {}, b"body"),
                ),
                ((b"get", "http://anything", {}, HasHeaders({b"2": [b"1"]}), b"what"), (202, {}, b"deleted")),
            ],
            async_failure_reporter=self.async_failures.append,
        )

        stub = StubTreq(StringStubbingResource(sequence))
        get = partial(stub.get, "https://anything?1=2", data=b"what", headers={b"1": b"1"})

        resp = self.successResultOf(get())

        self.assertEqual(418, resp.code)
        self.assertEqual(b"body", self.successResultOf(stub.content(resp)))
        self.assertEqual([], self.async_failures)

        resp = self.successResultOf(get())
        self.assertEqual(500, resp.code)
        self.assertEqual(1, len(self.async_failures))
        self.assertIn("Expected the next request to be", self.async_failures[0])

        self.assertFalse(sequence.consumed())
Example #2
0
    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)
Example #3
0
 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)
Example #4
0
    def test_providing_resource_to_stub_treq(self):
        """
        The resource provided to StubTreq responds to every request no
        matter what the URI or parameters or data.
        """
        verbs = ("GET", "PUT", "HEAD", "PATCH", "DELETE", "POST")
        urls = (
            "http://supports-http.com",
            "https://supports-https.com",
            "http://this/has/a/path/and/invalid/domain/name",
            "https://supports-https.com:8080",
            "http://supports-http.com:8080",
        )
        params = (None, {}, {b"page": [1]})
        headers = (None, {}, {b"x-random-header": [b"value", b"value2"]})
        data = (None, b"", b"some data", b'{"some": "json"}')

        stub = StubTreq(_StaticTestResource())

        combos = (
            (verb, {"url": url, "params": p, "headers": h, "data": d})
            for verb in verbs
            for url in urls
            for p in params
            for h in headers
            for d in data
        )
        for combo in combos:
            verb, kwargs = combo
            deferreds = (stub.request(verb, **kwargs), getattr(stub, verb.lower())(**kwargs))
            for d in deferreds:
                resp = self.successResultOf(d)
                self.assertEqual(418, resp.code)
                self.assertEqual([b"teapot!"], resp.headers.getRawHeaders(b"x-teapot"))
                self.assertEqual(b"" if verb == "HEAD" else b"I'm a teapot", self.successResultOf(stub.content(resp)))
Example #5
0
    def test_mismatched_request_causes_failure(self):
        """
        If a request is made that is not expected as the next request,
        causes a failure.
        """
        sequence = RequestSequence(
            [(('get', 'https://anything/', {'1': ['2']},
               HasHeaders({'1': ['1']}), 'what'),
              (418, {}, 'body')),
             (('get', 'http://anything', {}, HasHeaders({'2': ['1']}), 'what'),
              (202, {}, 'deleted'))],
            async_failure_reporter=self.async_failures.append)

        stub = StubTreq(StringStubbingResource(sequence))
        get = partial(stub.get, 'https://anything?1=2', data='what',
                      headers={'1': '1'})

        resp = self.successResultOf(get())
        self.assertEqual(418, resp.code)
        self.assertEqual('body', self.successResultOf(stub.content(resp)))
        self.assertEqual([], self.async_failures)

        resp = self.successResultOf(get())
        self.assertEqual(500, resp.code)
        self.assertEqual(1, len(self.async_failures))
        self.assertIn("Expected the next request to be",
                      self.async_failures[0])

        self.assertFalse(sequence.consumed())
Example #6
0
 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)
Example #7
0
 def test_handles_asynchronous_requests(self):
     """
     Handle a resource returning NOT_DONE_YET.
     """
     stub = StubTreq(_NonResponsiveTestResource())
     d = stub.request('method', 'http://url', data="1234")
     self.assertNoResult(d)
     d.cancel()
     self.failureResultOf(d, ResponseFailed)
Example #8
0
 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)
Example #9
0
 def test_handles_failing_asynchronous_requests(self):
     """
     Handle a resource returning NOT_DONE_YET and then canceling the
     request.
     """
     stub = StubTreq(_NonResponsiveTestResource())
     d = stub.request("method", "http://url", data=b"1234")
     self.assertNoResult(d)
     d.cancel()
     self.failureResultOf(d, ResponseFailed)
Example #10
0
 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)
Example #11
0
    def test_handles_successful_asynchronous_requests_with_streaming(self):
        """
        Handle a resource returning NOT_DONE_YET and then streaming data back
        gradually over time.
        """
        rsrc = _EventuallyResponsiveTestResource()
        stub = StubTreq(rsrc)
        d = stub.request("method", "http://example.com/", data="1234")
        self.assertNoResult(d)

        chunks = []
        rsrc.stored_request.write(b"spam ")
        rsrc.stored_request.write(b"eggs")
        stub.flush()
        resp = self.successResultOf(d)
        d = stub.collect(resp, chunks.append)
        self.assertNoResult(d)
        self.assertEqual(b"".join(chunks), b"spam eggs")

        del chunks[:]
        rsrc.stored_request.write(b"eggs\r\nspam\r\n")
        stub.flush()
        self.assertNoResult(d)
        self.assertEqual(b"".join(chunks), b"eggs\r\nspam\r\n")

        rsrc.stored_request.finish()
        stub.flush()
        self.successResultOf(d)
Example #12
0
 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)
Example #13
0
 def test_passing_in_strange_data_is_rejected(self):
     """
     StubTreq rejects data that isn't list/dictionary/tuple/bytes/unicode.
     """
     stub = StubTreq(_StaticTestResource())
     self.assertRaises(AssertionError, stub.request, "method", "http://url", data=object())
     self.successResultOf(stub.request("method", "http://url", data={}))
     self.successResultOf(stub.request("method", "http://url", data=[]))
     self.successResultOf(stub.request("method", "http://url", data=()))
     self.successResultOf(stub.request("method", "http://url", data=binary_type(b"")))
     self.successResultOf(stub.request("method", "http://url", data=text_type("")))
Example #14
0
 def test_handles_successful_asynchronous_requests(self):
     """
     Handle a resource returning NOT_DONE_YET and then later finishing the
     response.
     """
     rsrc = _EventuallyResponsiveTestResource()
     stub = StubTreq(rsrc)
     d = stub.request("method", "http://example.com/", data=b"1234")
     self.assertNoResult(d)
     rsrc.stored_request.finish()
     stub.flush()
     resp = self.successResultOf(d)
     self.assertEqual(resp.code, 200)
Example #15
0
    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)
class FakeMarathonAPI(object):
    app = Klein()

    def __init__(self, marathon):
        self._marathon = marathon
        self.client = StubTreq(self.app.resource())
        self.event_requests = []
        self._called_get_apps = False

    def check_called_get_apps(self):
        """ Check and reset the ``_called_get_apps`` flag. """
        was_called, self._called_get_apps = self._called_get_apps, False
        return was_called

    @app.route('/v2/apps', methods=['GET'])
    def get_apps(self, request):
        self._called_get_apps = True
        response = {
            'apps': self._marathon.get_apps()
        }
        request.setResponseCode(200)
        write_request_json(request, response)

    @app.route('/v2/events', methods=['GET'])
    def get_events(self, request):
        assert (get_single_header(request.requestHeaders, 'Accept') ==
                'text/event-stream')

        request.setResponseCode(200)
        request.setHeader('Content-Type', 'text/event-stream')
        # Push the response headers before any events are written
        request.write(b'')
        self.client.flush()

        def callback(event):
            _write_request_event(request, event)
            self.client.flush()
        self._marathon.attach_event_stream(
            callback, _get_event_types(request.args), request.getClientIP())
        self.event_requests.append(request)

        def finished_errback(failure):
            self._marathon.detach_event_stream(callback, request.getClientIP())
            self.event_requests.remove(request)

        finished = request.notifyFinish()
        finished.addErrback(finished_errback)

        return finished
Example #17
0
    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())
Example #18
0
    def test_interacts_successfully_with_istub(self):
        """
        The :obj:`IStringResponseStubs` is passed the correct parameters with
        which to evaluate the response, and the response is returned.
        """
        resource = StringStubbingResource(
            self._get_response_for(
                (b"DELETE", "http://what/a/thing", {b"page": [b"1"]}, {b"x-header": [b"eh"]}, b"datastr"),
                (418, {b"x-response": b"responseheader"}, b"response body"),
            )
        )

        stub = StubTreq(resource)

        d = stub.delete("http://what/a/thing", headers={b"x-header": b"eh"}, params={b"page": b"1"}, data=b"datastr")
        resp = self.successResultOf(d)
        self.assertEqual(418, resp.code)
        self.assertEqual([b"responseheader"], resp.headers.getRawHeaders(b"x-response"))
        self.assertEqual(b"response body", self.successResultOf(stub.content(resp)))
Example #19
0
    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())
Example #20
0
    def test_interacts_successfully_with_istub(self):
        """
        The :obj:`IStringResponseStubs` is passed the correct parameters with
        which to evaluate the response, and the response is returned.
        """
        resource = StringStubbingResource(self._get_response_for(
            ('DELETE', 'http://what/a/thing', {'page': ['1']},
             {'x-header': ['eh']}, 'datastr'),
            (418, {'x-response': 'responseheader'}, 'response body')))

        stub = StubTreq(resource)

        d = stub.delete('http://what/a/thing', headers={'x-header': 'eh'},
                        params={'page': '1'}, data='datastr')
        resp = self.successResultOf(d)
        self.assertEqual(418, resp.code)
        self.assertEqual(['responseheader'],
                         resp.headers.getRawHeaders('x-response'))
        self.assertEqual('response body',
                         self.successResultOf(stub.content(resp)))
Example #21
0
    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))))
Example #22
0
    def test_providing_resource_to_stub_treq(self):
        """
        The resource provided to StubTreq responds to every request no
        matter what the URI or parameters or data.
        """
        verbs = ('GET', 'PUT', 'HEAD', 'PATCH', 'DELETE', 'POST')
        urls = (
            'http://supports-http.com',
            'https://supports-https.com',
            'http://this/has/a/path/and/invalid/domain/name'
            'https://supports-https.com:8080',
            'http://supports-http.com:8080',
        )
        params = (None, {}, {'page': [1]})
        headers = (None, {}, {'x-random-header': ['value', 'value2']})
        data = (None, "", 'some data', '{"some": "json"}')

        stub = StubTreq(_StaticTestResource())

        combos = (
            (verb, {"url": url, "params": p, "headers": h, "data": d})
            for verb in verbs
            for url in urls
            for p in params
            for h in headers
            for d in data
        )
        for combo in combos:
            verb, kwargs = combo
            deferreds = (stub.request(verb, **kwargs),
                         getattr(stub, verb.lower())(**kwargs))
            for d in deferreds:
                resp = self.successResultOf(d)
                self.assertEqual(418, resp.code)
                self.assertEqual(['teapot!'],
                                 resp.headers.getRawHeaders('x-teapot'))
                self.assertEqual("" if verb == "HEAD" else "I'm a teapot",
                                 self.successResultOf(stub.content(resp)))
Example #23
0
    def test_handles_successful_asynchronous_requests_with_response_data(self):
        """
        Handle a resource returning NOT_DONE_YET and then sending some data in
        the response.
        """
        rsrc = _EventuallyResponsiveTestResource()
        stub = StubTreq(rsrc)
        d = stub.request('method', 'http://example.com/', data=b"1234")
        self.assertNoResult(d)

        chunks = []
        rsrc.stored_request.write(b'spam ')
        rsrc.stored_request.write(b'eggs')
        stub.flush()
        resp = self.successResultOf(d)
        d = stub.collect(resp, chunks.append)
        self.assertNoResult(d)
        self.assertEqual(b''.join(chunks), b'spam eggs')

        rsrc.stored_request.finish()
        stub.flush()
        self.successResultOf(d)
Example #24
0
def app(tmpdir_factory, trained_nlu_model):
    """Use IResource interface of Klein to mock Rasa HTTP server"""

    _, nlu_log_file = tempfile.mkstemp(suffix="_rasa_nlu_logs.json")

    temp_path = tmpdir_factory.mktemp("projects")
    try:
        shutil.copytree(
            trained_nlu_model,
            os.path.join(
                temp_path.strpath,
                "{}/{}".format(KEYWORD_PROJECT_NAME, KEYWORD_MODEL_NAME),
            ),
        )
    except FileExistsError:
        pass

    router = DataRouter(temp_path.strpath)
    rasa = RasaNLU(router, logfile=nlu_log_file, testing=True)
    return StubTreq(rasa.app.resource())
Example #25
0
    def test_updated_only(self):
        """
        An Atom feed which only has entry dates from ``<updated>`` tags is
        interpreted as having articles with the dates according to the
        ``<updated>`` tags.
        """
        feed = FetchFeed()
        xml = resources.read_binary("yarrharr.examples", "updated-only.atom")
        client = StubTreq(StaticResource(xml))

        outcome = self.successResultOf(poll_feed(feed, self.clock, client))

        self.assertIsInstance(outcome, MaybeUpdated)
        self.assertEqual(
            [
                datetime(2017, 3, 17, tzinfo=tz.utc),
                datetime(2013, 1, 1, tzinfo=tz.utc),
            ],
            [article.date for article in outcome.articles],
        )
Example #26
0
def simpleSessionRouter() -> Tuple[Sessions, Errors, str, str, StubTreq]:
    """
    Construct a simple router.
    """
    sessions = []
    exceptions = []
    mss = MemorySessionStore.fromAuthorizers([memoryAuthorizer])
    router = Klein()
    token = "X-Test-Session-Token"
    cookie = "X-Test-Session-Cookie"
    sproc = SessionProcurer(
        mss,
        secureTokenHeader=b"X-Test-Session-Token",
        secureCookie=b"X-Test-Session-Cookie",
    )

    @router.route("/")
    @inlineCallbacks
    def route(request: IRequest) -> Deferred:
        try:
            sessions.append((yield sproc.procureSession(request)))
        except NoSuchSession as nss:
            exceptions.append(nss)
        returnValue(b"ok")

    requirer = Requirer()

    @requirer.prerequisite([ISession])
    def procure(request: IRequest) -> Deferred:
        return sproc.procureSession(request)

    @requirer.require(router.route("/test"), simple=Authorization(ISimpleTest))
    def testRoute(simple: SimpleTest) -> str:
        return "ok: " + str(simple.doTest() + 4)

    @requirer.require(router.route("/denied"), nope=Authorization(IDenyMe))
    def testDenied(nope: IDenyMe) -> str:
        return "bad"

    treq = StubTreq(router.resource())
    return sessions, exceptions, token, cookie, treq
Example #27
0
 def test_passing_in_strange_data_is_rejected(self):
     """
     StubTreq rejects data that isn't list/dictionary/tuple/bytes/unicode.
     """
     stub = StubTreq(_StaticTestResource())
     self.assertRaises(
         AssertionError, stub.request, 'method', 'http://url',
         data=object())
     self.successResultOf(stub.request('method', 'http://url', data={}))
     self.successResultOf(stub.request('method', 'http://url', data=[]))
     self.successResultOf(stub.request('method', 'http://url', data=()))
     self.successResultOf(
         stub.request('method', 'http://url', data=binary_type("")))
     self.successResultOf(
         stub.request('method', 'http://url', data=text_type("")))
Example #28
0
def app(component_builder):
    """Use IResource interface of Klein to mock Rasa HTTP server.

    :param component_builder:
    :return:
    """

    if "TRAVIS_BUILD_DIR" in os.environ:
        root_dir = os.environ["TRAVIS_BUILD_DIR"]
    else:
        root_dir = os.getcwd()

    _, nlu_log_file = tempfile.mkstemp(suffix="_rasa_nlu_logs.json")

    train_models(component_builder,
                 os.path.join(root_dir, "data/examples/rasa/demo-rasa.json"))

    router = DataRouter(os.path.join(root_dir, "test_projects"))
    rasa = RasaNLU(router, logfile=nlu_log_file, testing=True)

    return StubTreq(rasa.app.resource())
Example #29
0
 def test_own_content_type(self):
     """
     If an existing ``Content-Type`` header exists it is used instead of
     ``application/json``.
     """
     def _response_for(method, url, params, headers, data):
         self.assertThat(method, Equals(u'POST'))
         self.assertThat(url, Equals(b'http://example.com/post_json'))
         self.assertThat(
             headers,
             ContainsDict(
                 {b'Accept': Equals([b'application/json']),
                  b'Content-Type': Equals([b'text/plain'])}))
         return 200, {}, json.dumps({u'arst': u'arst'})
     resource = StringStubbingResource(_response_for)
     treq = StubTreq(resource)
     self.assertThat(
         post_json(treq.request, b'http://example.com/post_json',
                   headers={b'Content-Type': b'text/plain'}),
         succeeded(
             Equals({u'arst': u'arst'})))
Example #30
0
    def test_magic_folder_not_ok(self, folder_name, collective_dircap,
                                 upload_dircap, good_token, bad_token):
        """
        If the response to a request for magic folder status does not receive an
        HTTP OK response, ``status`` fails with ``BadResponseCode``.
        """
        assume(collective_dircap != upload_dircap)
        assume(good_token != bad_token)

        tempdir = FilePath(self.mktemp())
        node_directory = tempdir.child(u"node")
        node = self.useFixture(NodeDirectory(node_directory, good_token))

        node.create_magic_folder(
            folder_name,
            collective_dircap,
            upload_dircap,
            tempdir.child(u"folder"),
            60,
        )
        folders = {
            folder_name: StubMagicFolder(),
        }
        resource = magic_folder_uri_hierarchy(
            folders,
            collective_dircap,
            upload_dircap,
            {},
            {},
            bad_token,
        )
        treq = StubTreq(resource)
        self.assertThat(
            status(folder_name, node_directory, treq),
            failed(
                AfterPreprocessing(
                    lambda f: f.value,
                    IsInstance(BadResponseCode),
                ), ),
        )
Example #31
0
    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))))
Example #32
0
    def test_etag_200(self):
        """
        When the ``If-None-Match`` header does not match, a 200 response is
        processed normally.
        """
        feed = FetchFeed(etag=b'"abcd"')
        client = StubTreq(StaticEtagResource(EMPTY_RSS, '"1234"'))

        result = self.successResultOf(poll_feed(feed, self.clock, client))

        self.assertEqual(
            MaybeUpdated(
                feed_title="Empty RSS feed",
                site_url="http://an.example/",
                etag=b'"1234"',
                last_modified=b"",
                digest=mock.ANY,
                articles=[],
                check_time=mock.ANY,
            ),
            result,
        )
Example #33
0
def app(tmpdir_factory):
    """
    This fixture makes use of the IResource interface of the Klein application to mock Rasa HTTP server.
    :param component_builder:
    :return:
    """

    _, nlu_log_file = tempfile.mkstemp(suffix="_rasa_nlu_logs.json")
    _config = {
        'write': nlu_log_file,
        'port': -1,  # unused in test app
        "pipeline": "keyword",
        "path": tmpdir_factory.mktemp("projects").strpath,
        "server_model_dirs": {},
        "data": "./data/demo-restaurants.json",
        "emulate": "wit",
        "max_training_processes": 1
    }

    config = RasaNLUConfig(cmdline_args=_config)
    rasa = RasaNLU(config, testing=True)
    return StubTreq(rasa.app.resource())
Example #34
0
    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)
Example #35
0
 def test_store_content(self):
     """
     Store content in a Documint session.
     """
     def _response_for(method, url, params, headers, data):
         self.assertThat(method, Equals(b'POST'))
         self.assertThat(
             headers,
             ContainsDict({
                 b'Accept': Equals([b'application/json']),
                 b'Content-Type': Equals([b'text/plain'])}))
         self.assertThat(
             url,
             MatchesAll(
                 IsInstance(bytes),
                 Equals(b'http://example.com/store')))
         self.assertThat(
             data,
             MatchesAll(
                 IsInstance(bytes),
                 Equals(b'hello world')))
         return (200,
                 {b'Content-Type': b'application/json'},
                 json.dumps(
                     {u'links':
                      {u'self': u'http://example.com/stored_object'}}))
     resource = StringStubbingResource(_response_for)
     treq = StubTreq(resource)
     session = Session({u'store-content': u'http://example.com/store'},
                       treq.request)
     # XXX: This is not a real file object because `StubTreq` doesn't
     # implement support for that.
     fileobj = b'hello world'
     self.assertThat(
         session.store_content(fileobj, b'text/plain'),
         succeeded(
             Equals(u'http://example.com/stored_object')))
Example #36
0
    def test_error(self):
        """
        Documint errors are parsed into a structured exception.
        """
        def _response_for(method, url, params, headers, data):
            return (400,
                    {b'Content-Type': b'application/json'},
                    json.dumps({u'causes': [
                        {u'type': u'foo',
                         u'reason': 42,
                         u'description': u'nope'},
                        {u'type': u'bar',
                         u'reason': 42,
                         u'description': None},
                        {u'type': u'baz',
                         u'reason': None,
                         u'description': None}]}))
        resource = StringStubbingResource(_response_for)
        treq = StubTreq(resource)
        request = documint_request_factory(treq.request)

        def cause(t, r=None, d=None):
            return MatchesStructure(type=Equals(t),
                                    reason=Equals(r),
                                    description=Equals(d))
        self.assertThat(
            request(b'GET', b'http://example.com/error'),
            failed(
                AfterPreprocessing(
                    lambda f: f.value,
                    MatchesAll(
                        IsInstance(DocumintError),
                        MatchesStructure(
                            causes=MatchesListwise([
                                cause(u'foo', 42, u'nope'),
                                cause(u'bar', 42),
                                cause(u'baz')]))))))
Example #37
0
    def test_handles_successful_asynchronous_requests_with_response_data(self):
        """
        Handle a resource returning NOT_DONE_YET and then sending some data in
        the response.
        """
        rsrc = _EventuallyResponsiveTestResource()
        stub = StubTreq(rsrc)
        d = stub.request('method', 'http://example.com/', data="1234")
        self.assertNoResult(d)

        chunks = []
        rsrc.stored_request.write('spam ')
        rsrc.stored_request.write('eggs')
        stub.flush()
        resp = self.successResultOf(d)
        d = stub.collect(resp, chunks.append)
        self.assertNoResult(d)
        self.assertEqual(''.join(chunks), 'spam eggs')

        rsrc.stored_request.finish()
        stub.flush()
        self.successResultOf(d)
Example #38
0
    def test_status(
        self,
        folder_name,
        collective_dircap,
        upload_dircap,
        token,
        local_file,
        upload_items,
        download_items,
    ):
        """
        ``status`` returns a ``Deferred`` that fires with a ``Status`` instance
        reflecting the status of the identified magic folder.
        """
        assume(collective_dircap != upload_dircap)

        tempdir = FilePath(self.mktemp())
        node_directory = tempdir.child(u"node")
        node = self.useFixture(NodeDirectory(node_directory, token))
        local_folder = tempdir.child(u"folder")
        local_folder.makedirs()
        node.create_magic_folder(
            folder_name,
            collective_dircap,
            upload_dircap,
            local_folder,
            60,
        )

        local_files = {
            u"foo": ["filenode", local_file],
        }
        remote_files = {
            u"participant-name": local_files,
        }
        folders = {
            folder_name:
            StubMagicFolder(
                uploader=StubQueue(upload_items),
                downloader=StubQueue(download_items),
            ),
        }
        treq = StubTreq(
            magic_folder_uri_hierarchy(
                folders,
                collective_dircap,
                upload_dircap,
                local_files,
                remote_files,
                token,
            ))
        self.assertThat(
            status(folder_name, node_directory, treq),
            succeeded(
                Equals(
                    Status(
                        folder_name=folder_name,
                        local_files=local_files,
                        remote_files=remote_files,
                        folder_status=list(
                            status_for_item(kind, item) for (kind, items) in [
                                ("upload", upload_items),
                                ("download", download_items),
                            ] for item in items),
                    )), ),
        )
 def patch_deps(self, monkeypatch):
     monkeypatch.setattr('treq_kerberos.treq', StubTreq(FakeTestResource()))
     monkeypatch.setattr('treq_kerberos.kerberos', FakeKerberos())
Example #40
0
 def __init__(self, marathon):
     self._marathon = marathon
     self.client = StubTreq(self.app.resource())
     self.event_requests = []
     self._called_get_apps = False
Example #41
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_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 🙁"}))))))
 def __init__(self, marathon):
     self._marathon = marathon
     self.client = StubTreq(self.app.resource())
     self.event_requests = []
     self._called_get_apps = False
Example #43
0
    def setUp(self):
        # prepare the authentication systems
        self.userdb = cred.InMemoryUnicodeUsernamePasswordDatabase()
        self.sitedb = cred.InMemoryUnicodeUsernamePasswordDatabase()
        self.userrealm = TestUserRealm()
        self.siterealm = TestSiteRealm()
        self.userportal = portal.Portal(self.userrealm)
        self.siteportal = portal.Portal(self.siterealm)
        self.userportal.registerChecker(self.userdb)
        self.siteportal.registerChecker(self.sitedb)

        # register tokens
        self.sitedb.addUser(
            self.SITE_A_TOKEN,
            self.SITE_A_SECRET,
        )
        self.siterealm.register_csauth_perm(
            self.SITE_A_TOKEN,
            TestWebAuthPermission(
                self.SITE_A_NAME,
                "http://{}/login".format(self.WEB_NAME),
                full_username=True,
            ),
        )
        self.sitedb.addUser(
            self.SITE_B_TOKEN,
            self.SITE_B_SECRET,
        )
        self.siterealm.register_csauth_perm(
            self.SITE_B_TOKEN,
            TestWebAuthPermission(
                self.SITE_B_NAME,
                "http://{}/bpage".format(self.WEB_NAME),
                full_username=False,
            ),
        )

        # register Users
        self.userdb.addUser(
            self.USER_A_NAME,
            self.USER_A_PSWD,
        )
        self.userdb.addUser(
            self.USER_B_NAME,
            self.USER_B_PSWD,
        )

        # prepare the auth site
        lr = BasicLoginResource()
        authpage = AuthResource(
            self.siteportal,
            self.userportal,
            lr,
            url=u"http://{}/auth".format(self.AUTH_NAME),
        )
        arr = DebugResource("auth")
        arr.putChild(b"auth", authpage)
        arr.putChild(b"expire", SessionExpiringResource())

        # prepare the web site
        lrr = DebugResource("web")

        # add the login page
        loginpage = TestLoginResource(
            u"http://{}/auth".format(self.AUTH_NAME),
            self.SITE_A_TOKEN,
            self.SITE_A_SECRET,
        )
        lrr.putChild(b"login", loginpage)

        # add a login page with a different permission
        bpage = TestLoginResource(
            u"http://{}/auth".format(self.AUTH_NAME),
            self.SITE_B_TOKEN,
            self.SITE_B_SECRET,
        )
        lrr.putChild(b"bpage", bpage)

        # add a resource with an invalid token/secret combination
        invalidepage = TestLoginResource(
            u"http://{}/auth".format(self.AUTH_NAME),
            self.SITE_A_TOKEN,
            self.SITE_B_SECRET,  # <- site B secret
        )
        lrr.putChild(b"invalid", invalidepage)

        # create and install treq stubs
        supersite = NameVirtualHost()
        supersite.addHost(self.AUTH_NAME.encode("ascii"), arr)
        supersite.addHost(self.WEB_NAME.encode("ascii"), lrr)
        supersite.default = DebugResource("default")

        self.treq = StubTreq(supersite)
        loginpage._set_request_dispatcher(self.treq)
        bpage._set_request_dispatcher(self.treq)
        invalidepage._set_request_dispatcher(self.treq)
Example #44
0
 def setup_method(self):
     self.responder_resource = Resource()
     self.server = MarathonAcmeServer(self.responder_resource)
     self.client = StubTreq(self.server.app.resource())
Example #45
0
 def setUp(self):
     service = StubFeed({URL.from_text(feed._source).host.encode('ascii'): make_xml(feed) for feed in FEEDS})
     treq = StubTreq(service.resource())
     self.retriever = FeedRetrieval(treq=treq)
Example #46
0
def app(core_server):
    """This fixture makes use of the IResource interface of the
    Klein application to mock Rasa Core server."""
    return StubTreq(core_server.app.resource())
    def test_api_error(self):
        """
        An error is reported if the API reports an error
        """
        stdout = StringIO()
        stderr = StringIO()

        basedir = FilePath(self.mktemp())
        global_config = create_global_configuration(
            basedir,
            "tcp:-1",
            FilePath(u"/no/tahoe/node-directory"),
            "tcp:127.0.0.1:-1",
        )

        # 2-tuples of "expected request" and the corresponding reply
        request_sequence = RequestSequence([
            # ((method, url, params, headers, data), (code, headers, body)),
            (
                (b"post",
                 self.url.child("magic-folder", "default", "snapshot").to_text(),
                 {b"path": [b"foo"]},
                 {
                     b'Host': [b'invalid.'],
                     b'Content-Length': [b'0'],
                     b'Connection': [b'close'],
                     b'Authorization': [b'Bearer ' + global_config.api_token],
                     b'Accept-Encoding': [b'gzip']
                 },
                 b""),
                (406, {}, b'{"reason": "an explanation"}')
            ),
        ])
        http_client = StubTreq(
            StringStubbingResource(
                request_sequence,
            )
        )
        client = create_magic_folder_client(
            Clock(),
            global_config,
            http_client,
        )

        with self.assertRaises(SystemExit):
            with request_sequence.consume(self.fail):
                yield dispatch_magic_folder_api_command(
                    ["--config", basedir.path, "add-snapshot",
                     "--file", "foo",
                     "--folder", "default"],
                    stdout=stdout,
                    stderr=stderr,
                    client=client,
                )
        self.assertThat(
            stdout.getvalue().strip(),
            Equals("")
        )
        self.assertThat(
            json.loads(stderr.getvalue()),
            Equals({"reason": "an explanation"})
        )
    def XXXtest_add_participant(self):
        """
        A new participant is added to a magic-folder
        """
        stdout = StringIO()
        stderr = StringIO()

        # 2-tuples of "expected request" and the corresponding reply
        request_sequence = RequestSequence([
            # ((method, url, params, headers, data), (code, headers, body)),
            (
                # expected request
                (b"post",
                 self.url.child("magic-folder", "default", "participants").to_text(),
                 {},
                 {
                     b'Host': [b'invalid.'],
                     b'Content-Length': [b'149'],
                     b'Connection': [b'close'],
                     b'Authorization': [b'Bearer ' + self.global_config.api_token],
                     b'Accept-Encoding': [b'gzip']
                 },
                 # XXX args, this fails because of different sorting of keys in body serialization
                 json.dumps({
                     "personal_dmd": "URI:DIR2-CHK:lq34kr5sp7mnvkhce4ahl2nw4m:dpujdl7sol6xih5gzil525tormolzaucq4re7snn5belv7wzsdha:1:5:328",
                     "author": {
                         "name": "amaya",
                     }
                 }).encode("utf8"),
                ),
                # expected response
                (200, {}, b"{}"),
            ),
        ])
        http_client = StubTreq(
            StringStubbingResource(
                request_sequence,
            )
        )
        client = create_magic_folder_client(
            Clock(),
            self.global_config,
            http_client,
        )
        with request_sequence.consume(self.fail):
            yield dispatch_magic_folder_api_command(
                ["--config", self.magic_config.path, "add-participant",
                 "--folder", "default",
                 "--author-name", "amaya",
                 "--personal-dmd", 'URI:DIR2-CHK:lq34kr5sp7mnvkhce4ahl2nw4m:dpujdl7sol6xih5gzil525tormolzaucq4re7snn5belv7wzsdha:1:5:328',
                ],
                stdout=stdout,
                stderr=stderr,
                client=client,
            )
        self.assertThat(
            stdout.getvalue().strip(),
            Equals("{}")
        )
        self.assertThat(
            stderr.getvalue().strip(),
            Equals("")
        )
Example #49
0
def treq_for_folders(reactor, basedir, auth_token, folders,
                     start_folder_services):
    """
    Construct a ``treq``-module-alike which is hooked up to a Magic Folder
    service with Magic Folders like the ones given.

    :param reactor: A reactor to give to the ``MagicFolderService`` which will
        back the HTTP interface.

    :param FilePath basedir: A non-existant directory to create and populate
        with a new Magic Folder service configuration.

    :param unicode auth_token: The authorization token accepted by the
        service.

    :param folders: A mapping from Magic Folder names to their configurations.
        These are the folders which will appear to exist.

    :param bool start_folder_services: If ``True``, start the Magic Folder
        service objects.  Otherwise, don't.

    :return: An object like the ``treq`` module.
    """
    global_config = create_global_configuration(
        basedir,
        # Make this endpoint string and the one below parse but make them
        # invalid, too, because we don't want anything to start listening on
        # these during this set of tests.
        #
        # https://github.com/LeastAuthority/magic-folder/issues/276
        u"tcp:-1",
        # It wants to know where the Tahoe-LAFS node directory is but we don't
        # have one and we don't want to invoke any functionality that requires
        # one.  Give it something bogus.
        FilePath(u"/non-tahoe-directory"),
        u"tcp:127.0.0.1:-1",
    )
    for name, config in folders.items():
        global_config.create_magic_folder(
            name,
            config[u"magic-path"],
            config[u"state-path"],
            config[u"author"],
            config[u"collective-dircap"],
            config[u"upload-dircap"],
            config[u"poll-interval"],
        )

    global_service = MagicFolderService(
        reactor,
        global_config,
        # Provide a TahoeClient so MagicFolderService doesn't try to look up a
        # Tahoe-LAFS node URL in the non-existent directory we supplied above
        # in its efforts to create one itself.
        TahoeClient(DecodedURL.from_text(u""), StubTreq(Resource())),
    )

    if start_folder_services:
        # Reach in and start the individual service for the folder we're going
        # to interact with.  This is required for certain functionality, eg
        # snapshot creation.  We avoid starting the whole global_service
        # because it wants to do error-prone things like bind ports.
        for name in folders:
            global_service.get_folder_service(name).startService()

    return create_testing_http_client(reactor, global_config, global_service,
                                      lambda: auth_token)
Example #50
0
class TestFeedAggregation(SynchronousTestCase):
    def setUp(self):
        service = StubFeed({URL.from_text(feed._source).host.encode('ascii'): make_xml(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_render_HTML(self):
        content = self.successResultOf(self.get(u'http://test.invalid/'))
        parsed = html.fromstring(content)
        self.assertEqual(
            parsed.xpath(u'/html/body/div/table/tr/th/a/text()'),
            [u'First feed', u'Second feed']
        )
        self.assertEqual(
            parsed.xpath(u'/html/body/div/table/tr/th/a/@href'),
            [u'http://feed-1/', u'http://feed-2/']
        )
        self.assertEqual(
            parsed.xpath(u'/html/body/div/table/tr/td/a/text()'),
            [u'First item', u'Second item']
        )
        self.assertEqual(
            parsed.xpath(u'/html/body/div/table/tr/td/a/@href'),
            [u'#first', u'#second']
        )

    def test_render_JSON(self):
        content = self.successResultOf(
            self.get(u'http://test.invalud/?json=true'))
        parsed = json.loads(content)
        self.assertEqual(
            parsed,
            {
                u'feeds': [
                    {
                        u'title': u'First feed',
                        u'link': u'http://feed-1/',
                        u'items': [
                            {
                                u'title': u'First item',
                                u'link': u'#first'
                            }
                        ]
                    },
                    {
                        u'title': u'Second feed',
                        u'link': u'http://feed-2/',
                        u'items': [
                            {
                                u'title': u'Second item',
                                u'link': u'#second'
                            }
                        ]
                    }
                ]
            }
        )
Example #51
0
 def setUp(self):
     service = StubFeed({URL.from_text(feed._source).host.encode('ascii'): make_xml(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())
Example #52
0
 def __init__(self):
     self.client = StubTreq(self.app.resource())
     self._signalled_hup = False
     self._signalled_usr1 = False
 def setup_method(self):
     self.responder_resource = Resource()
     self.server = MarathonAcmeServer(self.responder_resource)
     self.client = StubTreq(self.server.app.resource())
Example #54
0
class CsauthTests(unittest.TestCase):
    """
    Tests for L{txwebutils.csauth}.
    """
    WEB_NAME = u"web.localhost"
    AUTH_NAME = u"auth.localhost"

    SITE_A_NAME = u"Site Alpha"
    SITE_A_TOKEN = u"site a token"
    SITE_A_SECRET = u"secret for a"
    SITE_B_NAME = u"Site Beta"
    SITE_B_TOKEN = u"site B token"
    SITE_B_SECRET = u"secret for _b_"

    USER_A_NAME = u"UserA"
    USER_A_PSWD = u"PswdA"
    USER_B_NAME = u"IAmUserB"
    USER_B_PSWD = u"P-S-W-D_B"

    def setUp(self):
        # prepare the authentication systems
        self.userdb = cred.InMemoryUnicodeUsernamePasswordDatabase()
        self.sitedb = cred.InMemoryUnicodeUsernamePasswordDatabase()
        self.userrealm = TestUserRealm()
        self.siterealm = TestSiteRealm()
        self.userportal = portal.Portal(self.userrealm)
        self.siteportal = portal.Portal(self.siterealm)
        self.userportal.registerChecker(self.userdb)
        self.siteportal.registerChecker(self.sitedb)

        # register tokens
        self.sitedb.addUser(
            self.SITE_A_TOKEN,
            self.SITE_A_SECRET,
        )
        self.siterealm.register_csauth_perm(
            self.SITE_A_TOKEN,
            TestWebAuthPermission(
                self.SITE_A_NAME,
                "http://{}/login".format(self.WEB_NAME),
                full_username=True,
            ),
        )
        self.sitedb.addUser(
            self.SITE_B_TOKEN,
            self.SITE_B_SECRET,
        )
        self.siterealm.register_csauth_perm(
            self.SITE_B_TOKEN,
            TestWebAuthPermission(
                self.SITE_B_NAME,
                "http://{}/bpage".format(self.WEB_NAME),
                full_username=False,
            ),
        )

        # register Users
        self.userdb.addUser(
            self.USER_A_NAME,
            self.USER_A_PSWD,
        )
        self.userdb.addUser(
            self.USER_B_NAME,
            self.USER_B_PSWD,
        )

        # prepare the auth site
        lr = BasicLoginResource()
        authpage = AuthResource(
            self.siteportal,
            self.userportal,
            lr,
            url=u"http://{}/auth".format(self.AUTH_NAME),
        )
        arr = DebugResource("auth")
        arr.putChild(b"auth", authpage)
        arr.putChild(b"expire", SessionExpiringResource())

        # prepare the web site
        lrr = DebugResource("web")

        # add the login page
        loginpage = TestLoginResource(
            u"http://{}/auth".format(self.AUTH_NAME),
            self.SITE_A_TOKEN,
            self.SITE_A_SECRET,
        )
        lrr.putChild(b"login", loginpage)

        # add a login page with a different permission
        bpage = TestLoginResource(
            u"http://{}/auth".format(self.AUTH_NAME),
            self.SITE_B_TOKEN,
            self.SITE_B_SECRET,
        )
        lrr.putChild(b"bpage", bpage)

        # add a resource with an invalid token/secret combination
        invalidepage = TestLoginResource(
            u"http://{}/auth".format(self.AUTH_NAME),
            self.SITE_A_TOKEN,
            self.SITE_B_SECRET,  # <- site B secret
        )
        lrr.putChild(b"invalid", invalidepage)

        # create and install treq stubs
        supersite = NameVirtualHost()
        supersite.addHost(self.AUTH_NAME.encode("ascii"), arr)
        supersite.addHost(self.WEB_NAME.encode("ascii"), lrr)
        supersite.default = DebugResource("default")

        self.treq = StubTreq(supersite)
        loginpage._set_request_dispatcher(self.treq)
        bpage._set_request_dispatcher(self.treq)
        invalidepage._set_request_dispatcher(self.treq)

    @defer.inlineCallbacks
    def test_login_correct(self):
        """
        Test a correct login.
        """
        # first, GET loginpage
        r = yield self.treq.get(u"http://{}/login".format(self.WEB_NAME), )
        cookies = r.cookies()
        self.assertEqual(r.code, 200)
        pa_url = r.request.absoluteURI.decode("ascii")
        # ensure we were redirected to the auth server
        self.assertNotIn(six.text_type(self.WEB_NAME), pa_url)
        self.assertIn(six.text_type(self.AUTH_NAME), pa_url)
        # ensure paramters were set correctly
        self.assertIn(u"action=login", pa_url)
        self.assertIn(u"ctoken=", pa_url)

        # POST correct login userdata
        r = yield self.treq.post(
            pa_url,
            params={
                u"username": self.USER_A_NAME,
                u"password": self.USER_A_PSWD,
            },
            cookies=cookies,
            browser_like_redirects=True,
        )
        self.assertEqual(r.code, 200)
        url = r.request.absoluteURI.decode("ascii")
        # ensure we were redirected back to webserver
        self.assertNotIn(six.text_type(self.AUTH_NAME), url)
        self.assertIn(six.text_type(self.WEB_NAME), url)

        # ensure content was set correctly
        text = yield r.text()
        self.assertEqual(text, self.USER_A_NAME)

        # ensure cookies will keep us logged in
        cr = yield self.treq.get(
            u"http://{}/login".format(self.WEB_NAME),
            cookies=cookies,
        )
        url = cr.request.absoluteURI.decode("ascii")
        self.assertNotIn(six.text_type(self.AUTH_NAME), url)
        self.assertIn(six.text_type(self.WEB_NAME), url)

        # expire session
        er = yield self.treq.get(
            u"http://{}/expire".format(self.AUTH_NAME),
            cookies=cookies,
        )
        self.assertEqual(er.code, 200)

        # ensure we will no longer be logged in automatically
        cr = yield self.treq.get(
            u"http://{}/login".format(self.WEB_NAME),
            cookies=cookies,
        )
        url = cr.request.absoluteURI.decode("ascii")
        self.assertNotIn(six.text_type(self.WEB_NAME), url)
        self.assertIn(six.text_type(self.AUTH_NAME), url)

    @defer.inlineCallbacks
    def test_login_different_perm_correct(self):
        """
        Test a correct login with site b, which has different permissions.
        """
        # first, GET loginpage
        r = yield self.treq.get(u"http://{}/bpage".format(self.WEB_NAME), )
        cookies = r.cookies()
        self.assertEqual(r.code, 200)
        pa_url = r.request.absoluteURI.decode("ascii")
        # ensure we were redirected to the auth server
        self.assertNotIn(six.text_type(self.WEB_NAME), pa_url)
        self.assertIn(six.text_type(self.AUTH_NAME), pa_url)
        # ensure paramters were set correctly
        self.assertIn(u"action=login", pa_url)
        self.assertIn(u"ctoken=", pa_url)

        # POST correct login userdata
        # we use user b this time, so we also check whether different
        # users work
        r = yield self.treq.post(
            pa_url,
            params={
                u"username": self.USER_B_NAME,
                u"password": self.USER_B_PSWD,
            },
            cookies=cookies,
            browser_like_redirects=True,
        )
        self.assertEqual(r.code, 200)
        url = r.request.absoluteURI.decode("ascii")
        # ensure we were redirected back to webserver
        self.assertNotIn(six.text_type(self.AUTH_NAME), url)
        self.assertIn(six.text_type(self.WEB_NAME), url)
        # ensure we are at the bpade
        self.assertIn(u"bpage", url)

        # ensure content was set correctly
        text = yield r.text()
        self.assertEqual(text, six.text_type(len(self.USER_B_NAME)))

        # ensure cookies will keep us logged in
        cr = yield self.treq.get(
            u"http://{}/bpage".format(self.WEB_NAME),
            cookies=cookies,
        )
        url = cr.request.absoluteURI.decode("ascii")
        self.assertNotIn(six.text_type(self.AUTH_NAME), url)
        self.assertIn(six.text_type(self.WEB_NAME), url)
        self.assertIn(u"bpage", url)

    @defer.inlineCallbacks
    def test_crossite_cookies(self):
        """
        Test that cookies will kept you logged in across sites.
        """
        # first, GET loginpage
        r = yield self.treq.get(u"http://{}/login".format(self.WEB_NAME), )
        cookies = r.cookies()
        self.assertEqual(r.code, 200)
        pa_url = r.request.absoluteURI.decode("ascii")
        # ensure we were redirected to the auth server
        self.assertNotIn(six.text_type(self.WEB_NAME), pa_url)
        self.assertIn(six.text_type(self.AUTH_NAME), pa_url)
        self.assertIn(u"login", pa_url)
        # ensure paramters were set correctly
        self.assertIn(u"action=login", pa_url)
        self.assertIn(u"ctoken=", pa_url)

        # POST correct login userdata
        r = yield self.treq.post(
            pa_url,
            params={
                u"username": self.USER_A_NAME,
                u"password": self.USER_A_PSWD,
            },
            cookies=cookies,
            browser_like_redirects=True,
        )
        self.assertEqual(r.code, 200)
        url = r.request.absoluteURI.decode("ascii")
        # ensure we were redirected back to webserver
        self.assertNotIn(six.text_type(self.AUTH_NAME), url)
        self.assertIn(six.text_type(self.WEB_NAME), url)

        # ensure content was set correctly
        text = yield r.text()
        self.assertEqual(text, self.USER_A_NAME)

        # GET bpage
        r = yield self.treq.get(
            u"http://{}/bpage".format(self.WEB_NAME),
            cookies=cookies,
        )
        self.assertEqual(r.code, 200)
        url = r.request.absoluteURI.decode("ascii")
        # ensure we were not redirected to the auth server
        self.assertIn(six.text_type(self.WEB_NAME), url)
        self.assertNotIn(six.text_type(self.AUTH_NAME), url)
        self.assertIn(u"bpage", url)
        # ensure content is still correct
        # this is important, since the different sites have different
        # access to the userdata
        text = yield r.text()
        self.assertEqual(text, six.text_type(len(self.USER_A_NAME)))

    @defer.inlineCallbacks
    def test_login_invalid(self):
        """
        Test an invalid login.
        """
        # first, GET loginpage
        r = yield self.treq.get(u"http://{}/login".format(self.WEB_NAME), )
        cookies = r.cookies()
        self.assertEqual(r.code, 200)
        pa_url = r.request.absoluteURI.decode("ascii")
        # ensure we were redirected to the auth server
        self.assertNotIn(six.text_type(self.WEB_NAME), pa_url)
        self.assertIn(six.text_type(self.AUTH_NAME), pa_url)
        # ensure paramters were set correctly
        self.assertIn(u"action=login", pa_url)
        self.assertIn(u"ctoken=", pa_url)

        # POST incorrect login userdata
        r = yield self.treq.post(
            pa_url,
            params={
                u"username": self.USER_A_NAME,
                u"password": self.USER_B_PSWD,  # <- user b password
            },
            cookies=cookies,
            browser_like_redirects=True,
        )
        self.assertEqual(r.code, 401)
        url = r.request.absoluteURI.decode("ascii")
        # ensure we were not redirected back to webserver
        self.assertIn(six.text_type(self.AUTH_NAME), url)
        self.assertNotIn(six.text_type(self.WEB_NAME), url)

        # ensure a malicious request back to the webserver will fail
        qs = urlparse(url).query
        ctoken = parse_qs(qs)[u"ctoken"]
        r = yield self.treq.get(
            u"http://{}/login?action=callback&ctoken={}".format(
                self.WEB_NAME,
                ctoken,
            ),
            cookies=cookies,
        )
        cookies = r.cookies()
        self.assertEqual(r.code, 200)
        pa_url = r.request.absoluteURI.decode("ascii")
        # ensure we were redirected to the auth server
        self.assertNotIn(six.text_type(self.WEB_NAME), pa_url)
        self.assertIn(six.text_type(self.AUTH_NAME), pa_url)

    @defer.inlineCallbacks
    def test_token_secret_invalid(self):
        """
        Test an invalid token/secret.
        """
        # first, GET loginpage
        r = yield self.treq.get(u"http://{}/invalid".format(self.WEB_NAME), )
        cookies = r.cookies()
        self.assertEqual(r.code, 500)
        pa_url = r.request.absoluteURI.decode("ascii")
        # ensure we were not redirected to the auth server
        self.assertIn(six.text_type(self.WEB_NAME), pa_url)
        self.assertNotIn(six.text_type(self.AUTH_NAME), pa_url)
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 🙁"})))
        )))