Exemplo n.º 1
0
    def test_async_response(self):
        """
        FakeHttpServer supports asynchronous responses.
        """
        requests = []
        fake_http = FakeHttpServer(
            lambda req: requests.append(req) or NOT_DONE_YET)
        response1_d = fake_http.get_agent().request(
            "GET", b"http://example.com/hello/1")
        response2_d = fake_http.get_agent().request(
            "HEAD", b"http://example.com/hello/2")

        # Wait for the requests to arrive.
        yield wait0()
        [request1, request2] = requests
        self.assertNoResult(response1_d)
        self.assertNoResult(response2_d)
        self.assertEqual(request1.method, "GET")
        self.assertEqual(request1.path, b"http://example.com/hello/1")
        self.assertEqual(request2.method, "HEAD")
        self.assertEqual(request2.path, b"http://example.com/hello/2")

        # Send a response to the second request.
        request2.finish()
        response2 = yield response2_d
        self.assertNoResult(response1_d)
        yield self.assert_response(response2, 200, "")

        # Send a response to the first request.
        request1.write("Thank you for waiting.")
        request1.finish()
        response1 = yield response1_d
        yield self.assert_response(response1, 200, "Thank you for waiting.")
Exemplo n.º 2
0
    def test_async_response(self):
        """
        FakeHttpServer supports asynchronous responses.
        """
        requests = []
        fake_http = FakeHttpServer(
            lambda req: requests.append(req) or NOT_DONE_YET)
        response1_d = fake_http.get_agent().request(
            "GET", b"http://example.com/hello/1")
        response2_d = fake_http.get_agent().request(
            "HEAD", b"http://example.com/hello/2")

        # Wait for the requests to arrive.
        yield wait0()
        [request1, request2] = requests
        self.assertNoResult(response1_d)
        self.assertNoResult(response2_d)
        self.assertEqual(request1.method, "GET")
        self.assertEqual(request1.path, b"http://example.com/hello/1")
        self.assertEqual(request2.method, "HEAD")
        self.assertEqual(request2.path, b"http://example.com/hello/2")

        # Send a response to the second request.
        request2.finish()
        response2 = yield response2_d
        self.assertNoResult(response1_d)
        yield self.assert_response(response2, 200, "")

        # Send a response to the first request.
        request1.write("Thank you for waiting.")
        request1.finish()
        response1 = yield response1_d
        yield self.assert_response(response1, 200, "Thank you for waiting.")
Exemplo n.º 3
0
    def test_json_content_incorrect_content_type(self):
        """
        When a request is made with the json_content callback and the
        content-type header is set to a value other than 'application/json' in
        the response headers then an error should be raised.
        """
        d = self.cleanup_d(self.client.request('GET', path='/hello'))
        d.addCallback(json_content)

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/hello')))
        self.assertThat(request.requestHeaders,
                        HasHeader('accept', ['application/json']))

        request.setResponseCode(200)
        request.setHeader('Content-Type', 'application/octet-stream')
        request.write(json.dumps({}).encode('utf-8'))
        request.finish()

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            HTTPError,
            'Expected header "Content-Type" to be "application/json" but '
            'found "application/octet-stream" instead')))
Exemplo n.º 4
0
    def test_json_content_missing_content_type(self):
        """
        When a request is made with the json_content callback and the
        content-type header is not set in the response headers then an error
        should be raised.
        """
        d = self.cleanup_d(self.client.request('GET', path='/hello'))
        d.addCallback(json_content)

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/hello')))
        self.assertThat(request.requestHeaders,
                        HasHeader('accept', ['application/json']))

        request.setResponseCode(200)
        # Twisted will set the content type to "text/html" by default but this
        # can be disabled by setting the default content type to None:
        # https://twistedmatrix.com/documents/current/api/twisted.web.server.Request.html#defaultContentType
        request.defaultContentType = None
        request.write(json.dumps({}).encode('utf-8'))
        request.finish()

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            HTTPError, 'Expected header "Content-Type" to be '
                       '"application/json" but header not found in response')))
Exemplo n.º 5
0
    def test_get_events(self):
        """
        When a request is made to Marathon's event stream, a callback should
        receive JSON-decoded data before the connection is closed.
        """
        data = []
        d = self.cleanup_d(self.client.get_events({'test': data.append}))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/v2/events')))
        self.assertThat(request.requestHeaders,
                        HasHeader('accept', ['text/event-stream']))

        request.setResponseCode(200)
        request.setHeader('Content-Type', 'text/event-stream')

        json_data = {'hello': 'world'}
        request.write(b'event: test\n')
        request.write(b'data: %s\n' % (json.dumps(json_data).encode('utf-8'),))
        request.write(b'\n')

        yield wait0()
        self.assertThat(data, Equals([json_data]))

        request.finish()
        yield d

        # Expect request.finish() to result in a logged failure
        flush_logged_errors(ResponseDone)
    def test_get_events_non_200(self):
        """
        When a request is made to Marathon's event stream, and a non-200
        response code is returned, an error should be raised.
        """
        data = []
        d = self.cleanup_d(self.client.get_events({'test': data.append}))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/v2/events'),
            query={'event_type': ['test']}))
        self.assertThat(request.requestHeaders,
                        HasHeader('accept', ['text/event-stream']))

        request.setResponseCode(202)
        request.setHeader('Content-Type', 'text/event-stream')

        json_data = {'hello': 'world'}
        write_json_event(request, 'test', json_data)

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            HTTPError, 'Non-200 response code (202) for url: '
                       'http://localhost:8080/v2/events?event_type=test')))

        self.assertThat(data, Equals([]))

        request.finish()
        yield d
    def test_get_events_incorrect_content_type(self):
        """
        When a request is made to Marathon's event stream, and the content-type
        header value returned is not "text/event-stream", an error should be
        raised.
        """
        data = []
        d = self.cleanup_d(self.client.get_events({'test': data.append}))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/v2/events'),
            query={'event_type': ['test']}))
        self.assertThat(request.requestHeaders,
                        HasHeader('accept', ['text/event-stream']))

        request.setResponseCode(200)
        request.setHeader('Content-Type', 'application/json')

        json_data = {'hello': 'world'}
        write_json_event(request, 'test', json_data)

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            HTTPError,
            'Expected header "Content-Type" to be "text/event-stream" but '
            'found "application/json" instead')))

        self.assertThat(data, Equals([]))

        request.finish()
        yield d
Exemplo n.º 8
0
    def test_get_events_incorrect_content_type(self):
        """
        When a request is made to Marathon's event stream, and the content-type
        header value returned is not "text/event-stream", an error should be
        raised.
        """
        data = []
        d = self.cleanup_d(self.client.get_events({'test': data.append}))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/v2/events')))
        self.assertThat(request.requestHeaders,
                        HasHeader('accept', ['text/event-stream']))

        request.setResponseCode(200)
        request.setHeader('Content-Type', 'application/json')

        json_data = {'hello': 'world'}
        request.write(b'event: test\n')
        request.write(b'data: %s\n' % (json.dumps(json_data).encode('utf-8'),))
        request.write(b'\n')

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            HTTPError,
            'Expected header "Content-Type" to be "text/event-stream" but '
            'found "application/json" instead')))

        self.assertThat(data, Equals([]))

        request.finish()
        yield d
Exemplo n.º 9
0
    def test_get_events_non_200(self):
        """
        When a request is made to Marathon's event stream, and a non-200
        response code is returned, an error should be raised.
        """
        data = []
        d = self.cleanup_d(self.client.get_events({'test': data.append}))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/v2/events')))
        self.assertThat(request.requestHeaders,
                        HasHeader('accept', ['text/event-stream']))

        request.setResponseCode(202)
        request.setHeader('Content-Type', 'text/event-stream')

        json_data = {'hello': 'world'}
        request.write(b'event: test\n')
        request.write(b'data: %s\n' % (json.dumps(json_data).encode('utf-8'),))
        request.write(b'\n')

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            HTTPError, 'Non-200 response code (202) for url: '
                       'http://localhost:8080/v2/events')))

        self.assertThat(data, Equals([]))

        request.finish()
        yield d
    def test_get_events_no_callback(self):
        """
        When a request is made to Marathon's event stream, a callback should
        not receive event data if there is no callback for the event type.
        """
        data = []
        d = self.cleanup_d(self.client.get_events({'test': data.append}))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/v2/events'),
            query={'event_type': ['test']}))
        self.assertThat(request.requestHeaders,
                        HasHeader('accept', ['text/event-stream']))

        request.setResponseCode(200)
        request.setHeader('Content-Type', 'text/event-stream')

        json_data = {'hello': 'world'}
        request.write(b'event: not_test\n')
        request.write(
            'data: {}\n'.format(json.dumps(json_data)).encode('utf-8'))
        request.write(b'\n')

        yield wait0()
        self.assertThat(data, Equals([]))

        request.finish()
        yield d

        # Expect request.finish() to result in a logged failure
        flush_logged_errors(ResponseDone)
    def test_get_json_field_missing_content_type(self):
        """
        When get_json_field is used to make a request and the content-type
        header is not set in the response headers then an error should be
        raised.
        """
        d = self.cleanup_d(
            self.client.get_json_field('field-key', path='/my-path'))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/my-path')))

        request.setResponseCode(200)
        # Twisted will set the content type to "text/html" by default but this
        # can be disabled by setting the default content type to None:
        # https://twistedmatrix.com/documents/current/api/twisted.web.server.Request.html#defaultContentType
        request.defaultContentType = None
        request.write(json.dumps({}).encode('utf-8'))
        request.finish()

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            HTTPError, 'Expected header "Content-Type" to be '
                       '"application/json" but header not found in response')))
Exemplo n.º 12
0
    def test_get_json_field_error(self):
        """
        When get_json_field is used to make a request but the response code
        indicates an error, an HTTPError should be raised.
        """
        d = self.cleanup_d(
            self.client.get_json_field('field-key', path='/my-path'))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/my-path')))

        request.setResponseCode(404)
        request.write(b'Not found\n')
        request.finish()

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            HTTPError, '404 Client Error for url: %s' % self.uri('/my-path'))))
Exemplo n.º 13
0
    def test_server_abort(self):
        """
        If the server aborts, the client's connection is also lost.
        """
        fake_server = FakeServer(self.server_factory)
        conn, client = self.connect_client(fake_server)
        finished_d = conn.await_finished()
        self.assertNoResult(finished_d)

        # The disconnection gets scheduled, but doesn't actually happen until
        # the next reactor tick.
        conn.server_protocol.transport.abortConnection()
        self.assert_connected(conn, client)
        self.assertNoResult(finished_d)

        # Allow the reactor to run so the disconnection gets processed.
        yield wait0()
        self.assert_disconnected(conn, server_reason=ConnectionAborted)
        self.successResultOf(finished_d)
Exemplo n.º 14
0
    def test_server_abort(self):
        """
        If the server aborts, the client's connection is also lost.
        """
        fake_server = FakeServer(self.server_factory)
        conn, client = self.connect_client(fake_server)
        finished_d = conn.await_finished()
        self.assertNoResult(finished_d)

        # The disconnection gets scheduled, but doesn't actually happen until
        # the next reactor tick.
        conn.server_protocol.transport.abortConnection()
        self.assert_connected(conn, client)
        self.assertNoResult(finished_d)

        # Allow the reactor to run so the disconnection gets processed.
        yield wait0()
        self.assert_disconnected(conn, server_reason=ConnectionAborted)
        self.successResultOf(finished_d)
    def test_get_json_field_error(self):
        """
        When get_json_field is used to make a request but the response code
        indicates an error, an HTTPError should be raised.
        """
        d = self.cleanup_d(
            self.client.get_json_field('field-key', path='/my-path'))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/my-path')))

        request.setResponseCode(404)
        request.write(b'Not found\n')
        request.finish()

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            HTTPError, '404 Client Error for url: %s' % self.uri('/my-path'))))
    def test_request_fallback_all_failed(self):
        """
        When we make a request and there are multiple Marathon endpoints
        specified, and all the endpoints fail, the last failure should be
        returned.
        """
        agent = PerLocationAgent()
        agent.add_agent(b'localhost:8080', FailingAgent(RuntimeError('8080')))
        agent.add_agent(b'localhost:9090', FailingAgent(RuntimeError('9090')))
        client = MarathonClient(
            ['http://localhost:8080', 'http://localhost:9090'],
            client=treq_HTTPClient(agent))

        d = self.cleanup_d(client.request('GET', path='/my-path'))

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            RuntimeError, '9090')))

        flush_logged_errors(RuntimeError)
Exemplo n.º 17
0
    def test_request_fallback_all_failed(self):
        """
        When we make a request and there are multiple Marathon endpoints
        specified, and all the endpoints fail, the last failure should be
        returned.
        """
        agent = PerLocationAgent()
        agent.add_agent(b'localhost:8080', FailingAgent(RuntimeError('8080')))
        agent.add_agent(b'localhost:9090', FailingAgent(RuntimeError('9090')))
        client = MarathonClient(
            ['http://localhost:8080', 'http://localhost:9090'],
            client=treq_HTTPClient(agent))

        d = self.cleanup_d(client.request('GET', path='/my-path'))

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            RuntimeError, '9090')))

        flush_logged_errors(RuntimeError)
    def test_server_error_response(self):
        """
        When a request is made and the raise_for_status callback is added and a
        5xx response code is returned, a HTTPError should be raised to indicate
        a server error.
        """
        d = self.cleanup_d(self.client.request('GET', path='/hello'))
        d.addCallback(raise_for_status)

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/hello')))

        request.setResponseCode(502)
        request.write(b'Bad gateway\n')
        request.finish()

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            HTTPError, '502 Server Error for url: %s' % self.uri('/hello'))))
Exemplo n.º 19
0
    def test_server_error_response(self):
        """
        When a request is made and the raise_for_status callback is added and a
        5xx response code is returned, a HTTPError should be raised to indicate
        a server error.
        """
        d = self.cleanup_d(self.client.request('GET', path='/hello'))
        d.addCallback(raise_for_status)

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/hello')))

        request.setResponseCode(502)
        request.write(b'Bad gateway\n')
        request.finish()

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            HTTPError, '502 Server Error for url: %s' % self.uri('/hello'))))
Exemplo n.º 20
0
    def test_get_events_multiple_callbacks(self):
        """
        When a request is made to Marathon's event stream, and there are
        events for multiple callbacks, those callbacks should receive
        JSON-decoded data for each event.
        """
        data1 = []
        data2 = []
        d = self.cleanup_d(self.client.get_events({
            'test1': data1.append,
            'test2': data2.append
        }))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/v2/events')))
        self.assertThat(request.requestHeaders,
                        HasHeader('accept', ['text/event-stream']))

        request.setResponseCode(200)
        request.setHeader('Content-Type', 'text/event-stream')

        json_data1 = {'hello': 'world'}
        request.write(b'event: test1\n')
        request.write(b'data: %s\n' % (json.dumps(json_data1).encode('utf-8')))
        request.write(b'\n')

        json_data2 = {'hello': 'computer'}
        request.write(b'event: test2\n')
        request.write(b'data: %s\n' % (json.dumps(json_data2).encode('utf-8')))
        request.write(b'\n')

        yield wait0()
        self.assertThat(data1, Equals([json_data1]))
        self.assertThat(data2, Equals([json_data2]))

        request.finish()
        yield d

        # Expect request.finish() to result in a logged failure
        flush_logged_errors(ResponseDone)
    def test_get_json_field_missing(self):
        """
        When get_json_field is used to make a request, the response is
        deserialized from JSON and if the specified field is missing, an error
        is raised.
        """
        d = self.cleanup_d(
            self.client.get_json_field('field-key', path='/my-path'))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/my-path')))

        json_response(request, {'other-field-key': 'do-not-care'})

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            KeyError,
            '\'Unable to get value for "field-key" from Marathon response: '
            '"{"other-field-key": "do-not-care"}"\''
        )))
Exemplo n.º 22
0
    def test_get_json_field_missing(self):
        """
        When get_json_field is used to make a request, the response is
        deserialized from JSON and if the specified field is missing, an error
        is raised.
        """
        d = self.cleanup_d(
            self.client.get_json_field('field-key', path='/my-path'))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/my-path')))

        json_response(request, {'other-field-key': 'do-not-care'})

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            KeyError,
            '\'Unable to get value for "field-key" from Marathon response: '
            '"{"other-field-key": "do-not-care"}"\''
        )))
    def test_get_events_multiple_events(self):
        """
        When a request is made to Marathon's event stream, and there are
        multiple events for a single callback, that callback should receive
        JSON-decoded data for each event.
        """
        data = []
        d = self.cleanup_d(self.client.get_events({'test': data.append}))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/v2/events'),
            query={'event_type': ['test']}))
        self.assertThat(request.requestHeaders,
                        HasHeader('accept', ['text/event-stream']))

        request.setResponseCode(200)
        request.setHeader('Content-Type', 'text/event-stream')

        json_data1 = {'hello': 'world'}
        request.write(b'event: test\n')
        request.write(
            'data: {}\n'.format(json.dumps(json_data1)).encode('utf-8'))
        request.write(b'\n')

        json_data2 = {'hi': 'planet'}
        request.write(
            'data: {}\n'.format(json.dumps(json_data2)).encode('utf-8'))
        request.write(b'event: test\n')
        request.write(b'\n')

        yield wait0()
        self.assertThat(data, Equals([json_data1, json_data2]))

        request.finish()
        yield d

        # Expect request.finish() to result in a logged failure
        flush_logged_errors(ResponseDone)
    def test_get_events_multiple_callbacks(self):
        """
        When a request is made to Marathon's event stream, and there are
        events for multiple callbacks, those callbacks should receive
        JSON-decoded data for each event.
        """
        data1 = []
        data2 = []
        d = self.cleanup_d(self.client.get_events({
            'test1': data1.append,
            'test2': data2.append
        }))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/v2/events'),
            query={'event_type': ['test1', 'test2']}))
        self.assertThat(request.requestHeaders,
                        HasHeader('accept', ['text/event-stream']))

        request.setResponseCode(200)
        request.setHeader('Content-Type', 'text/event-stream')

        json_data1 = {'hello': 'world'}
        write_json_event(request, 'test1', json_data1)

        json_data2 = {'hello': 'computer'}
        write_json_event(request, 'test2', json_data2)

        yield wait0()
        self.assertThat(data1, Equals([json_data1]))
        self.assertThat(data2, Equals([json_data2]))

        request.finish()
        yield d

        # Expect request.finish() to result in a logged failure
        flush_logged_errors(ResponseDone)
    def test_get_json_field_incorrect_content_type(self):
        """
        When get_json_field is used to make a request and the content-type
        header is set to a value other than 'application/json' in the response
        headers then an error should be raised.
        """
        d = self.cleanup_d(
            self.client.get_json_field('field-key', path='/my-path'))

        request = yield self.requests.get()
        self.assertThat(request, HasRequestProperties(
            method='GET', url=self.uri('/my-path')))

        request.setResponseCode(200)
        request.setHeader('Content-Type', 'application/octet-stream')
        request.write(json.dumps({}).encode('utf-8'))
        request.finish()

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            HTTPError,
            'Expected header "Content-Type" to be "application/json" but '
            'found "application/octet-stream" instead')))
Exemplo n.º 26
0
    def test_request_failure(self):
        """
        When the requests to all the marathon-lb instances have a bad status
        code then an error should be raised.
        """
        d = self.cleanup_d(self.client.request('GET', path='/my-path'))

        for lb in ['lb1', 'lb2']:
            request = yield self.requests.get()
            self.assertThat(request, HasRequestProperties(
                method='GET', url='http://%s:9090/my-path' % (lb,)))

            request.setResponseCode(500)
            request.setHeader('content-type', 'text/plain')
            request.write(b'Internal Server Error')
            request.finish()

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            RuntimeError,
            'Failed to make a request to all marathon-lb instances'
        )))

        flush_logged_errors(HTTPError)
    def test_request_failure(self):
        """
        When the requests to all the marathon-lb instances have a bad status
        code then an error should be raised.
        """
        d = self.cleanup_d(self.client.request('GET', path='/my-path'))

        for lb in ['lb1', 'lb2']:
            request = yield self.requests.get()
            self.assertThat(request, HasRequestProperties(
                method='GET', url='http://%s:9090/my-path' % (lb,)))

            request.setResponseCode(500)
            request.setHeader('content-type', 'text/plain')
            request.write(b'Internal Server Error')
            request.finish()

        yield wait0()
        self.assertThat(d, failed(WithErrorTypeAndMessage(
            RuntimeError,
            'Failed to make a request to all marathon-lb instances'
        )))

        flush_logged_errors(HTTPError)