Exemple #1
0
    def test_no_body_custom_status(self):
        request = MockRequest()
        actual = helpers.create_response(request, status=HTTPStatus.CREATED)

        assert isinstance(actual, HttpResponse)
        assert actual.status == HTTPStatus.CREATED
        assert actual.body is None
    def test_dispatch__with_middleware(self):
        calls = []

        class Middleware(object):
            def pre_request(self, request, path_args):
                calls.append('pre_request')

            def pre_dispatch(self, request, path_args):
                calls.append('pre_dispatch')
                path_args['foo'] = 'bar'

            def post_dispatch(self, request, response):
                calls.append('post_dispatch')
                return 'eek' + response

            def post_request(self, request, response):
                calls.append('post_request')
                response['test'] = 'header'
                return response

        def callback(request, **args):
            assert args['foo'] == 'bar'
            return 'boo'

        target = containers.ApiInterfaceBase(middleware=[Middleware()])
        operation = Operation(callback)
        actual = target.dispatch(operation, MockRequest())

        assert actual.body == '"eekboo"'
        assert actual.status == 200
        assert 'test' in actual.headers
        assert calls == [
            'pre_request', 'pre_dispatch', 'post_dispatch', 'post_request'
        ]
Exemple #3
0
    def test_no_body(self):
        request = MockRequest()
        actual = helpers.create_response(request)

        assert isinstance(actual, HttpResponse)
        assert actual.status == HTTPStatus.NO_CONTENT
        assert actual.body is None
Exemple #4
0
    def test_bind_to_instance(self):
        @decorators.Operation(tags=('eek', 'bar'))
        def target(binding, request):
            """
            Test target
            """
            assert binding == api
            return 'foo'

        class MockApi(object):
            def __init__(self):
                self.call_count = defaultdict(int)
                self.resource = User
                self.tags = {'bar'}

            def pre_dispatch(self, request, path_args):
                self.call_count['pre_dispatch'] += 1

            def post_dispatch(self, request, response):
                self.call_count['post_dispatch'] += 1
                return response

        api = MockApi()
        request = MockRequest()
        target.bind_to_instance(api)
        assert target.binding == api
        assert target.resource is User
        assert target.is_bound
        assert target.tags == {'eek', 'bar'}

        actual = target(request, {})
        assert actual == 'foo'
        assert api.call_count == {'pre_dispatch': 1, 'post_dispatch': 1}
Exemple #5
0
def test_get_resource__codec_exceptions(body, error_code):
    request = MockRequest(body=body)

    with pytest.raises(HttpError) as exc_info:
        helpers.get_resource(request, User)

    assert exc_info.value.status == HTTPStatus.BAD_REQUEST
    assert exc_info.value.resource.code == error_code
Exemple #6
0
    def test_execute(self):
        @decorators.ResourceOperation(resource=User)
        def my_func(request, user):
            assert isinstance(user, User)
            assert user.name == "Stephen"

        request = MockRequest(body='{"id": 1, "name": "Stephen"}')
        my_func(request, {})
Exemple #7
0
def test_get_resource():
    request = MockRequest(body='{"$": "tests.User", "id":10, "name": "Dave"}')
    request.request_codec = json_codec

    user = helpers.get_resource(request, User)

    assert isinstance(user, User)
    assert user.id == 10
    assert user.name == 'Dave'
Exemple #8
0
    def test_content(self):
        request = MockRequest()

        actual = helpers.create_response(request, {"foo": "bar"})

        assert isinstance(actual, HttpResponse)
        assert actual.status == HTTPStatus.OK
        assert actual.headers['Content-Type'] == json_codec.CONTENT_TYPE
        assert json_codec.json.loads(actual.body) == {"foo": "bar"}
    def test_dispatch__error_with_debug_enabled(self):
        def callback(request):
            raise ValueError()

        target = containers.ApiInterfaceBase(debug_enabled=True)
        operation = Operation(callback)

        with pytest.raises(ValueError):
            target.dispatch(operation, MockRequest())
    def test_dispatch__exceptions(self, error, status):
        def callback(request):
            raise error

        target = containers.ApiInterfaceBase()
        operation = Operation(callback)
        actual = target.dispatch(operation, MockRequest())

        assert actual.status == status
Exemple #11
0
    def test_execute__invalid(self):
        @decorators.ResourceOperation(resource=User)
        def my_func(request, user):
            assert isinstance(user, User)
            assert user.name == "Stephen"

        request = MockRequest(body='{"id": 1, "name": "Stephen"')
        with pytest.raises(HttpError):
            my_func(request, {})
    def test_dispatch__encode_error_with_debug_enabled(self):
        def callback(request):
            raise api.ImmediateHttpResponse(ValueError,
                                            HTTPStatus.NOT_MODIFIED, {})

        target = containers.ApiInterfaceBase(debug_enabled=True)
        operation = Operation(callback)

        with pytest.raises(TypeError):
            target.dispatch(operation, MockRequest())
Exemple #13
0
    def test_request_headers(self, cors_config, expected):
        api_interface = ApiInterfaceBase(mock_endpoint)
        cors.CORS(api_interface, **cors_config)
        target = api_interface.middleware[0]
        http_request = MockRequest(headers={'Origin': 'http://my-domain.org'},
                                   current_operation=mock_endpoint)

        actual = target.request_headers(http_request)

        assert expected == actual
    def test_dispatch__http_response(self):
        def callback(request):
            return HttpResponse("eek")

        target = containers.ApiInterfaceBase()
        operation = Operation(callback)
        actual = target.dispatch(operation, MockRequest())

        assert actual.body == 'eek'
        assert actual.status == 200
Exemple #15
0
    def test_bad_values(self, options, query, expected_errors):
        mock_request = MockRequest(query=query)

        @decorators.ListOperation(**options)
        def my_func(request, **kwargs):
            return [1, 2, 3]

        with pytest.raises(ValidationError) as error:
            my_func(mock_request, {'foo': 'bar'})

        assert len(error.value.error_messages) == expected_errors
Exemple #16
0
    def test_pre_flight_headers(self, cors_config, expected):
        api_interface = ApiInterfaceBase(mock_endpoint)
        cors.CORS(api_interface, **cors_config)
        target = api_interface.middleware[0]
        http_request = MockRequest(headers={'Origin': 'http://my-domain.org'},
                                   current_operation=mock_endpoint)

        actual = target.pre_flight_headers(http_request, mock_endpoint.methods)

        assert 'GET, HEAD' == actual.pop('Allow')
        assert 'no-cache, no-store' == actual.pop('Cache-Control')
        assert expected == actual
Exemple #17
0
def test_get_resource__multiple():
    request = MockRequest(
        body='[{"$": "tests.User", "id":10, "name": "Dave"}]')
    request.request_codec = json_codec

    users = helpers.get_resource(request, User, allow_multiple=True)

    assert len(users) == 1
    user = users[0]
    assert isinstance(user, User)
    assert user.id == 10
    assert user.name == 'Dave'
    def test_dispatch__error_handled_by_middleware_raises_exception(self):
        class ErrorMiddleware(object):
            def handle_500(self, request, exception):
                assert isinstance(exception, ValueError)
                raise ValueError

        def callback(request):
            raise ValueError()

        target = containers.ApiInterfaceBase(middleware=[ErrorMiddleware()])
        operation = Operation(callback)

        actual = target.dispatch(operation, MockRequest())
        assert actual.status == 500
Exemple #19
0
    def test_post_request(self, origins, method, expected):
        api_interface = ApiInterfaceBase(mock_endpoint)
        cors.CORS(api_interface, origins=origins)
        target = api_interface.middleware[0]

        http_request = MockRequest(headers={'Origin': 'http://my-domain.org'},
                                   method=method,
                                   current_operation=mock_endpoint)
        http_response = HttpResponse('')

        actual = target.post_request(http_request, http_response)

        assert actual is http_response
        assert expected == actual.headers.get('Access-Control-Allow-Origin')
Exemple #20
0
    def test_unbound(self):
        @decorators.Operation(tags=('eek', 'bar'))
        def target(request):
            """
            Test target
            """
            return 'foo'

        request = MockRequest()
        assert target.resource is None
        assert not target.is_bound
        assert target.tags == {'eek', 'bar'}

        actual = target(request, {})
        assert actual == 'foo'
    def test_dispatch__error_handled_by_middleware(self):
        class ErrorMiddleware(object):
            def handle_500(self, request, exception):
                assert isinstance(exception, ValueError)
                return Error.from_status(HTTPStatus.SEE_OTHER, 0,
                                         "Quick over there...")

        def callback(request):
            raise ValueError()

        target = containers.ApiInterfaceBase(middleware=[ErrorMiddleware()])
        operation = Operation(callback)

        actual = target.dispatch(operation, MockRequest())
        assert actual.status == 303
Exemple #22
0
    def test_cors_options(self, origins, expected):
        api_interface = ApiInterfaceBase(mock_endpoint)
        cors.CORS(api_interface, origins=origins)
        target = api_interface.middleware[0]

        http_request = MockRequest(headers={'Origin': 'http://my-domain.org'},
                                   method=Method.OPTIONS)
        cors._MethodsMiddleware(
            (Method.GET, Method.HEAD)).pre_dispatch(http_request, None)

        actual = target.cors_options(http_request)

        assert isinstance(actual, HttpResponse)
        assert 'GET, HEAD' == actual.headers.pop('Allow')
        assert 'no-cache, no-store' == actual.headers.pop('Cache-Control')
        assert expected == actual.headers.get('Access-Control-Allow-Origin')
Exemple #23
0
    def test_returning_total_count(self):
        mock_request = MockRequest()

        @decorators.ListOperation
        def my_func(request, foo, offset, limit):
            assert foo == 'bar'
            assert offset == 0
            assert limit == 50
            return [1, 2, 3], 5

        result = my_func(mock_request, {'foo': 'bar'})

        assert isinstance(result, HttpResponse)
        assert result.body == '[1, 2, 3]'
        assert result['X-Page-Offset'] == '0'
        assert result['X-Page-Limit'] == '50'
        assert result['X-Total-Count'] == '5'
Exemple #24
0
    def test_returning_total_count(self):
        mock_request = MockRequest()

        @decorators.WrappedListOperation
        def my_func(request, foo, offset, limit):
            assert foo == 'bar'
            assert offset == 0
            assert limit == 50
            return [1, 2, 3], 5

        result = my_func(mock_request, {'foo': 'bar'})

        assert isinstance(result, decorators.Listing)
        assert result.results == [1, 2, 3]
        assert result.offset == 0
        assert result.limit == 50
        assert result.total_count == 5
    def test_dispatch__with_middleware_pre_request_response(self):
        """
        Test scenario where pre-request hook returns a HTTP Response object
        """
        class Middleware(object):
            def pre_request(self, request, path_args):
                return HttpResponse('eek!', status=HTTPStatus.FORBIDDEN)

        def callback(request, **args):
            assert False, "Response should have already occurred!"

        target = containers.ApiInterfaceBase(middleware=[Middleware()])
        operation = Operation(callback)
        actual = target.dispatch(operation, MockRequest())

        assert actual.body == 'eek!'
        assert actual.status == 403
Exemple #26
0
    def test_options_handled(self, options, query, offset, limit):
        mock_request = MockRequest(query=query)

        @decorators.ListOperation(**options)
        def my_func(request, **kwargs):
            assert request is mock_request
            assert kwargs['offset'] == offset
            assert kwargs['limit'] == limit
            assert kwargs['foo'] == 'bar'
            return [1, 2, 3]

        result = my_func(mock_request, {'foo': 'bar'})

        assert isinstance(result, HttpResponse)
        assert result.body == '[1, 2, 3]'
        assert result['X-Page-Offset'] == str(offset)
        assert result['X-Page-Limit'] == str(limit)
        assert 'X-Total-Count' not in result.headers
Exemple #27
0
    def test_options_handled(self, options, query, offset, limit, bare):
        mock_request = MockRequest(query=query)

        @decorators.WrappedListOperation(**options)
        def my_func(request, **kwargs):
            assert request is mock_request
            assert kwargs['offset'] == offset
            assert kwargs['limit'] == limit
            assert kwargs['foo'] == 'bar'
            return [1, 2, 3]

        result = my_func(mock_request, {'foo': 'bar'})

        if bare:
            assert result == [1, 2, 3]
        else:
            assert isinstance(result, decorators.Listing)
            assert result.results == [1, 2, 3]
            assert result.offset == offset
            assert result.limit == limit
            assert result.total_count is None
Exemple #28
0

@pytest.mark.parametrize('value, expected', (
    (None, ''),
    ('', ''),
    ('text/plain', 'text/plain'),
    ('text/plain; encoding=UTF-8', 'text/plain'),
    ('text/plain; encoding=UTF-8; x=y', 'text/plain'),
))
def test_parse_content_type(value, expected):
    actual = helpers.parse_content_type(value)
    assert actual == expected


@pytest.mark.parametrize('http_request, expected', (
    (MockRequest(), 'application/json'),
    (MockRequest(headers={'accepts': 'text/html'}), 'text/html'),
    (MockRequest(headers={'Content-Type': 'text/plain'}), 'text/plain'),
    (MockRequest(headers={
        'accepts': 'text/html',
        'Content-Type': 'text/plain'
    }), 'text/html'),
    (MockRequest(headers={'x-auth': '123'}), 'application/json'),
))
def test_resolve_content_type(http_request, expected):
    actual = helpers.resolve_content_type([
        content_type_resolvers.accepts_header(),
        content_type_resolvers.content_type_header(),
        content_type_resolvers.specific_default('application/json'),
    ], http_request)
class TestApiInterfaceBase(object):
    @pytest.mark.parametrize('options,name,debug_enabled,path_prefix', (
        ({}, 'api', False, UrlPath.parse('/api')),
        ({
            'name': '!api'
        }, '!api', False, UrlPath.parse('/!api')),
        ({
            'path_prefix': '/my-app/'
        }, 'api', False, UrlPath.parse('/my-app')),
        ({
            'debug_enabled': True
        }, 'api', True, UrlPath.parse('/api')),
    ))
    def test_options(self, options, name, debug_enabled, path_prefix):
        target = containers.ApiInterfaceBase(**options)

        assert target.name == name
        assert target.debug_enabled == debug_enabled
        assert target.path_prefix == path_prefix

    def test_init_non_absolute(self):
        with pytest.raises(ValueError):
            containers.ApiInterfaceBase(path_prefix='ab/c')

    def test_dispatch(self):
        pass

    @pytest.mark.parametrize('r, status, message', (
        (MockRequest(headers={
            'content-type': 'application/xml',
            'accepts': 'application/json'
        }), 422, 'Unprocessable Entity'),
        (MockRequest(headers={
            'content-type': 'application/json',
            'accepts': 'application/xml'
        }), 406, 'URI not available in preferred format'),
        (MockRequest(method=Method.POST), 405,
         'Specified method is invalid for this resource'),
    ))
    def test_dispatch__invalid_headers(self, r, status, message):
        target = containers.ApiInterfaceBase()
        operation = Operation(mock_callback)
        actual = target.dispatch(operation, r)

        assert actual.status == status
        assert actual.body == message

    @pytest.mark.parametrize('error,status', (
        (api.ImmediateHttpResponse(None, HTTPStatus.NOT_MODIFIED,
                                   {}), HTTPStatus.NOT_MODIFIED),
        (ValidationError("Error"), 400),
        (ValidationError({}), 400),
        (NotImplementedError, 501),
        (ValueError, 500),
        (api.ImmediateHttpResponse(ValueError, HTTPStatus.NOT_MODIFIED,
                                   {}), 500),
    ))
    def test_dispatch__exceptions(self, error, status):
        def callback(request):
            raise error

        target = containers.ApiInterfaceBase()
        operation = Operation(callback)
        actual = target.dispatch(operation, MockRequest())

        assert actual.status == status

    def test_dispatch__with_middleware(self):
        calls = []

        class Middleware(object):
            def pre_request(self, request, path_args):
                calls.append('pre_request')

            def pre_dispatch(self, request, path_args):
                calls.append('pre_dispatch')
                path_args['foo'] = 'bar'

            def post_dispatch(self, request, response):
                calls.append('post_dispatch')
                return 'eek' + response

            def post_request(self, request, response):
                calls.append('post_request')
                response['test'] = 'header'
                return response

        def callback(request, **args):
            assert args['foo'] == 'bar'
            return 'boo'

        target = containers.ApiInterfaceBase(middleware=[Middleware()])
        operation = Operation(callback)
        actual = target.dispatch(operation, MockRequest())

        assert actual.body == '"eekboo"'
        assert actual.status == 200
        assert 'test' in actual.headers
        assert calls == [
            'pre_request', 'pre_dispatch', 'post_dispatch', 'post_request'
        ]

    def test_dispatch__with_middleware_pre_request_response(self):
        """
        Test scenario where pre-request hook returns a HTTP Response object
        """
        class Middleware(object):
            def pre_request(self, request, path_args):
                return HttpResponse('eek!', status=HTTPStatus.FORBIDDEN)

        def callback(request, **args):
            assert False, "Response should have already occurred!"

        target = containers.ApiInterfaceBase(middleware=[Middleware()])
        operation = Operation(callback)
        actual = target.dispatch(operation, MockRequest())

        assert actual.body == 'eek!'
        assert actual.status == 403

    def test_dispatch__error_with_debug_enabled(self):
        def callback(request):
            raise ValueError()

        target = containers.ApiInterfaceBase(debug_enabled=True)
        operation = Operation(callback)

        with pytest.raises(ValueError):
            target.dispatch(operation, MockRequest())

    def test_dispatch__error_handled_by_middleware(self):
        class ErrorMiddleware(object):
            def handle_500(self, request, exception):
                assert isinstance(exception, ValueError)
                return Error.from_status(HTTPStatus.SEE_OTHER, 0,
                                         "Quick over there...")

        def callback(request):
            raise ValueError()

        target = containers.ApiInterfaceBase(middleware=[ErrorMiddleware()])
        operation = Operation(callback)

        actual = target.dispatch(operation, MockRequest())
        assert actual.status == 303

    def test_dispatch__error_handled_by_middleware_raises_exception(self):
        class ErrorMiddleware(object):
            def handle_500(self, request, exception):
                assert isinstance(exception, ValueError)
                raise ValueError

        def callback(request):
            raise ValueError()

        target = containers.ApiInterfaceBase(middleware=[ErrorMiddleware()])
        operation = Operation(callback)

        actual = target.dispatch(operation, MockRequest())
        assert actual.status == 500

    def test_dispatch__encode_error_with_debug_enabled(self):
        def callback(request):
            raise api.ImmediateHttpResponse(ValueError,
                                            HTTPStatus.NOT_MODIFIED, {})

        target = containers.ApiInterfaceBase(debug_enabled=True)
        operation = Operation(callback)

        with pytest.raises(TypeError):
            target.dispatch(operation, MockRequest())

    def test_dispatch__http_response(self):
        def callback(request):
            return HttpResponse("eek")

        target = containers.ApiInterfaceBase()
        operation = Operation(callback)
        actual = target.dispatch(operation, MockRequest())

        assert actual.body == 'eek'
        assert actual.status == 200

    def test_op_paths(self):
        target = containers.ApiInterfaceBase(MockResourceApi())

        actual = list(target.op_paths())

        assert actual == [
            (UrlPath.parse('/api/a/b'),
             Operation(mock_callback, 'a/b', Method.GET)),
            (UrlPath.parse('/api/a/b'),
             Operation(mock_callback, 'a/b', Method.POST)),
            (UrlPath.parse('/api/d/e'),
             Operation(mock_callback, 'd/e', (Method.POST, Method.PATCH))),
        ]

    def test_op_paths__collate_methods(self):
        target = containers.ApiInterfaceBase(MockResourceApi())

        actual = target.op_paths(collate_methods=True)

        assert actual == {
            UrlPath.parse('/api/a/b'): {
                Method.GET: Operation(mock_callback, 'a/b', Method.GET),
                Method.POST: Operation(mock_callback, 'a/b', Method.POST),
            },
            UrlPath.parse('/api/d/e'): {
                Method.POST:
                Operation(mock_callback, 'd/e', (Method.POST, Method.PATCH)),
                Method.PATCH:
                Operation(mock_callback, 'd/e', (Method.POST, Method.PATCH)),
            }
        }
Exemple #30
0
    def test_get_swagger(self, monkeypatch):
        monkeypatch.setattr(
            swagger,
            'CODECS',
            {
                'application/json': None  # Only the Keys are used.
            })

        request = MockRequest()
        target = swagger.SwaggerSpec("Example", schemes='http')

        base = ApiInterfaceBase(ApiVersion(target, ), )

        base.registered_codecs.clear()
        base.registered_codecs[
            'application/yaml'] = None  # Only the keys are used.

        actual = target.get_swagger(request)
        expected = {
            'swagger': '2.0',
            'info': {
                'title': 'Example',
                'version': '1'
            },
            'host': '127.0.0.1',
            'schemes': ['http'],
            'basePath': '/api/v1',
            'consumes': ['application/yaml'],
            'produces': ['application/yaml'],
            'paths': {},
            'definitions': {
                'Error': {
                    'type': 'object',
                    'properties': {
                        'code': {
                            'description':
                            'Custom application specific error code that references into the application.',
                            'type': 'integer',
                            'format': 'int64',
                        },
                        'developer_message': {
                            'description':
                            'An error message suitable for the application developer',
                            'type': 'string'
                        },
                        'message': {
                            'description':
                            'A message that can be displayed to an end user',
                            'type': 'string'
                        },
                        'meta': {
                            'description':
                            'Additional meta information that can help solve errors.',
                        },
                        'status': {
                            'description': 'HTTP status code of the response.',
                            'type': 'integer',
                            'format': 'int64',
                        }
                    },
                },
                'Listing': {
                    'type': 'object',
                    'properties': {
                        'limit': {
                            'description':
                            'Limit or page size of the result set',
                            'type': 'integer',
                            'format': 'int64',
                        },
                        'offset': {
                            'description': 'Offset within the result set.',
                            'type': 'integer',
                            'format': 'int64',
                        },
                        'results': {
                            'description': 'List of resources.',
                        },
                        'total_count': {
                            'description':
                            'The total number of items in the result set.',
                            'type': 'integer',
                            'format': 'int64',
                        }
                    },
                }
            }
        }
        assert actual == expected