def test_integration_get_filtered_list(app, example_schema, example_model): class ExampleFiltersSchema(filters_schema.FilterSchema): basic = filters_schema.FilterField() listed = filters_schema.ListFilterField() dumb_name = filters_schema.FilterField(attribute='renamed') integer = filters_schema.FilterField(type_=fields.Int) skipped_filter = filters_schema.FilterField() class ExampleListView(resources.ResourceList): schema = example_schema filter_schema = ExampleFiltersSchema() applied_filters = {} def read_many(self, filters, sorting, pagination): self.applied_filters.update(filters) return [] application_api = api.Api(app) application_api.route(ExampleListView, 'example_list', '/examples/') response = app.test_client().get( '/examples/?filter[basic]=text&filter[listed]=first,second&filter[dumb-name]=another&filter[integer]=3', headers=JSONAPI_HEADERS) assert response.status_code == 200 assert ExampleListView.applied_filters == { 'basic': 'text', 'listed': ['first', 'second'], 'renamed': 'another', 'integer': 3, }
def test_integration_get_filtered_list(app, example_schema, example_model): class ExampleListView(resources.ResourceList): schema = example_schema filter_schema = filters_schema.FilterSchema({ 'basic': filters_schema.FilterField(), 'listed': filters_schema.ListFilterField(), 'renamed': filters_schema.FilterField(field_name='dumb-name'), 'integer': filters_schema.FilterField(parse_value=int), 'skipped_filter': filters_schema.FilterField(), }) applied_filters = [] def read_many(self, filters): self.applied_filters.append(filters) return [] application_api = api.Api(app) application_api.route(ExampleListView, 'example_list', '/examples/') response = app.test_client().get( '/examples/?filter[basic]=text&filter[listed]=first,second&filter[dumb-name]=another&filter[integer]=3', headers=JSONAPI_HEADERS) assert response.status_code == 200 assert ExampleListView.applied_filters == [{ 'basic': 'text', 'listed': ['first', 'second'], 'renamed': 'another', 'integer': 3, }]
def test_creating_view_with_dynamic_schema(app, example_schema, example_model): class ExampleDetailView(resources.ResourceDetail): def read(self, id): return example_model(id=id, body='Gwynbelidd') application_api = api.Api(app) application_api.route(ExampleDetailView, 'example_detail', '/examples/<id>/', view_kwargs={'schema': example_schema}) response = app.test_client().get( '/examples/f60717a3-7dc2-4f1a-bdf4-f2804c3127a4/', headers=JSONAPI_HEADERS) result = json.loads(response.data.decode()) assert result == { 'data': { 'id': 'f60717a3-7dc2-4f1a-bdf4-f2804c3127a4', 'type': 'example', 'attributes': { 'body': 'Gwynbelidd' } }, 'jsonapi': { 'version': '1.0' } }
def test_integration_patch_with_empty_response(app, example_schema, example_model): class ExampleDetailView(resources.ResourceDetail): schema = example_schema def update(self, id, data): pass json_data = json.dumps({ 'data': { 'type': 'example', 'id': 'f60717a3-7dc2-4f1a-bdf4-f2804c3127a4', 'attributes': { 'body': "Nice body.", } } }) application_api = api.Api(app) application_api.route(ExampleDetailView, 'example_list', '/examples/<id>/') response = app.test_client().patch( '/examples/f60717a3-7dc2-4f1a-bdf4-f2804c3127a4/', headers=JSONAPI_HEADERS, data=json_data, ) assert response.status_code == 204 assert response.data == b''
def test_integration_create_resource(app, example_schema, example_model): class ExampleListView(resources.ResourceList): schema = example_schema def create(self, *args, **kwargs): return example_model(id='f60717a3-7dc2-4f1a-bdf4-f2804c3127a4', body='Nice body.') json_data = json.dumps({ 'data': { 'type': 'example', 'id': 'f60717a3-7dc2-4f1a-bdf4-f2804c3127a4', 'attributes': { 'body': "Nice body.", } } }) application_api = api.Api(app) application_api.route(ExampleListView, 'example_list', '/examples/') response = app.test_client().post( '/examples/', headers=JSONAPI_HEADERS, data=json_data, ) assert json.loads(response.data.decode()) == { "data": { "type": "example", "id": "f60717a3-7dc2-4f1a-bdf4-f2804c3127a4", "attributes": { "body": "Nice body." } }, "jsonapi": { "version": "1.0" } }
def test_integration_get_list_use_schema_instead_of_atomic_schema(app): database_simulation.clear() application_api = api.Api(app) application_api.repository(ParentResourceRepositoryViewSet(), 'parent', '/parents/') parent = ParentModel(**{'id': 111}) database_simulation[parent.id] = parent response = app.test_client().get( '/parents/', headers=JSONAPI_HEADERS, ) result = json.loads(response.data.decode('utf-8')) expected = { 'meta': { 'count': 1 }, 'data': [{ 'id': '111', 'type': 'parent' }], 'jsonapi': { 'version': '1.0' } } assert expected == result
def test_integration_get_list(app): application_api = api.Api(app) application_api.repository(ExampleResourceRepositoryViewSet(), 'example', '/examples/') response = app.test_client().get('/examples/', headers=JSONAPI_HEADERS) result = json.loads(response.data.decode()) assert result == { 'data': [{ 'id': 'f60717a3-7dc2-4f1a-bdf4-f2804c3127a4', 'type': 'example', 'attributes': { 'body': 'heheh' } }, { 'id': 'f60717a3-7dc2-4f1a-bdf4-f2804c3127a5', 'type': 'example', 'attributes': { 'body': 'hihi' }, }], 'jsonapi': { 'version': '1.0' }, 'meta': { 'count': 2 } }
def test_integration_create_resource(app): application_api = api.Api(app) application_api.repository(ExampleResourceRepositoryViewSet(), 'example', '/examples/') json_data = json.dumps({ 'data': { 'type': 'example', 'id': 'f60717a3-7dc2-4f1a-bdf4-f2804c3127a4', 'attributes': { 'body': "Nice body.", } } }) response = app.test_client().post( '/examples/', headers=JSONAPI_HEADERS, data=json_data, ) assert json.loads(response.data.decode()) == { "data": { "type": "example", "id": "f60717a3-7dc2-4f1a-bdf4-f2804c3127a4", "attributes": { "body": "Nice body." } }, "jsonapi": { "version": "1.0" } }
def test_invalid_include_parameter_response(self, app): application_api = api.Api(app) application_api.repository(ParentResourceRepositoryViewSet(), 'parent', '/parents/') response = app.test_client().get( '/parents/f60717a3-7dc2-4f1a-bdf4-f2804c3127a4/?include=children.children', headers=JSONAPI_HEADERS) assert response.status_code == http.HTTPStatus.BAD_REQUEST
def test_bad_get_detail(app): database_simulation.clear() application_api = api.Api(app) application_api.repository(BadResourceRepositoryViewSet(), 'bad', '/bad/') response = app.test_client().get( '/bad/1/', headers=JSONAPI_HEADERS, ) result = json.loads(response.data.decode('utf-8')) assert result['errors'][0]['status'] == 500 assert result['errors'][0]['detail'] == 'Must have an `id` field'
def test_integration_delete(app): application_api = api.Api(app) view_set = ExampleResourceRepositoryViewSet() application_api.repository(view_set, 'example', '/examples/') response = app.test_client().delete( '/examples/f60717a3-7dc2-4f1a-bdf4-f2804c3127a4/', headers=JSONAPI_HEADERS) assert response.status_code == 204 assert response.data == b'' assert view_set.repository.deleted_ids == [ 'f60717a3-7dc2-4f1a-bdf4-f2804c3127a4' ]
def test_integration_create_nested_resource(app): application_api = api.Api(app) application_api.repository(ParentResourceRepositoryViewSet(), 'parent', '/parents/') json_data = json.dumps({ 'data': { 'type': 'parent', 'id': 'f60717a3-7dc2-4f1a-bdf4-f2804c3127a4', 'relationships': { 'descendant': { 'data': [ { 'type': 'descendant', 'attributes': { 'name': 'Olga', '__id__': '_tem_id_3344' }, 'id': 'f60717a3-7dc2-0000-0000-f2804c3127a4' } ] } } } }) response = app.test_client().post( '/parents/', headers=JSONAPI_HEADERS, data=json_data, ) result = json.loads(response.data.decode('utf-8')) expected = { "data": { "id": "f60717a3-7dc2-4f1a-bdf4-f2804c3127a4", "type": "parent", "relationships": { "descendant": { "data": [ { "id": "f60717a3-7dc2-0000-0000-f2804c3127a4", "type": "descendant", "attributes": { "__id__": "_tem_id_3344", "name": "Olga" } } ] } } }, "jsonapi": {"version": "1.0"} } assert expected == result
def test_integration_get_list_with_pagination(app, example_schema, example_model): class ExampleListView(resources.ResourceList): schema = example_schema def read_many(self, filters, pagination): return [ example_model(id='f60717a3-7dc2-4f1a-bdf4-f2804c3127a4', body='heheh'), example_model(id='f60717a3-7dc2-4f1a-bdf4-f2804c3127a5', body='hihi'), ] def get_count(self, filters): return 5 application_api = api.Api(app) application_api.route(ExampleListView, 'example_list', '/examples/') response = app.test_client().get( '/examples/?page[size]=2&page[number]=1', headers=JSONAPI_HEADERS ) result = json.loads(response.data.decode()) assert response.status_code == 200 assert result == { 'data': [ { 'type': 'example', 'id': 'f60717a3-7dc2-4f1a-bdf4-f2804c3127a4', 'attributes': { 'body': 'heheh' } }, { 'type': 'example', 'id': 'f60717a3-7dc2-4f1a-bdf4-f2804c3127a5', 'attributes': { 'body': 'hihi' } } ], 'links': { 'self': 'http://localhost/examples/?page[size]=2&page[number]=1', 'first': 'http://localhost/examples/?page[size]=2&page[number]=1', 'previous': None, 'next': 'http://localhost/examples/?page[size]=2&page[number]=2', 'last': 'http://localhost/examples/?page[size]=2&page[number]=3' }, 'meta': { 'count': 2 }, 'jsonapi': { 'version': '1.0' } }
def test_get_parent_detail_with_include_data(self, app): application_api = api.Api(app) application_api.repository(ParentResourceRepositoryViewSet(), 'parent', '/parents/') response = app.test_client().get( '/parents/f60717a3-7dc2-4f1a-bdf4-f2804c3127a4/?include=children', headers=JSONAPI_HEADERS ) result = json.loads(response.data.decode('utf-8')) result['included'].sort(key=lambda x: x['attributes']['name']) expected = { 'data': { 'attributes': { 'name': 'Adam' }, 'id': 'f60717a3-7dc2-4f1a-bdf4-f2804c3127a4', 'relationships': { 'children': { 'data': [ { 'id': mock.ANY, 'type': 'childs' }, { 'id': mock.ANY, 'type': 'childs' } ] } }, 'type': 'parents' }, 'included': [ { 'attributes': { 'name': 'Abel' }, 'id': mock.ANY, 'type': 'childs' }, { 'attributes': { 'name': 'Cain' }, 'id': mock.ANY, 'type': 'childs' }, ], 'jsonapi': { 'version': '1.0' } } assert expected == result
def test_bad_data_get_detail(app): database_simulation.clear() application_api = api.Api(app) application_api.repository(BadDataResourceRepositoryViewSet(), 'bad', '/bad/') model = BadDataModel(**{'id': 111, 'number': 1}) database_simulation[model.id] = model response = app.test_client().get( '/bad/111/', headers=JSONAPI_HEADERS, ) result = json.loads(response.data.decode('utf-8')) assert result['errors'][0]['status'] == 500 assert result['errors'][0][ 'detail'] == "'int' object has no attribute 'isoformat'"
def test_sorting(app, example_schema): class ExampleRepository(repositories.ResourceRepository): pass class ExampleListView(resource_repository_views.ResourceRepositoryViewSet): schema = example_schema repository = ExampleRepository api.Api(app).repository(ExampleListView(), 'example', '/examples/') get_list_mock = mock.MagicMock() with mock.patch( 'flask_jsonapi.resource_repositories.repositories.ResourceRepository.get_list', get_list_mock): app.test_client().get('/examples/?sort=body', headers=JSONAPI_HEADERS) get_list_mock.assert_called_once_with({}, ('body', ), {})
def test_integration_get_nested_resource(app): database_simulation.clear() application_api = api.Api(app) application_api.repository(ParentResourceRepositoryViewSet(), 'parent', '/parents/') response = app.test_client().get( '/parents/', headers=JSONAPI_HEADERS, ) result = json.loads(response.data.decode('utf-8')) expected = { "data": [], "meta": {"count": 0}, "jsonapi": {"version": "1.0"}, } assert expected == result
def test_bad_data_get_detail(app): database_simulation.clear() application_api = api.Api(app) application_api.repository(BadDataResourceRepositoryViewSet(), 'bad', '/bad/') model = BadDataModel(**{'id': 111, 'number': 1}) database_simulation[model.id] = model response = app.test_client().get( '/bad/111/', headers=JSONAPI_HEADERS, ) result = json.loads(response.data.decode('utf-8')) assert result['errors'][0]['status'] == 500 assert result['errors'][0]['detail'] == 'marshmallow.ValidationError' assert result['errors'][0]['source'] == { 'number': ['"1" cannot be formatted as a datetime.'] }
def test_integration_delete(app, example_schema, example_model): class ExampleDetailView(resources.ResourceDetail): schema = example_schema deleted_ids = [] def destroy(self, id): self.deleted_ids.append(id) application_api = api.Api(app) application_api.route(ExampleDetailView, 'example_detail', '/examples/<id>/') response = app.test_client().delete( '/examples/f60717a3-7dc2-4f1a-bdf4-f2804c3127a4/', headers=JSONAPI_HEADERS ) assert response.status_code == 204 assert response.data == b'' assert ExampleDetailView.deleted_ids == ['f60717a3-7dc2-4f1a-bdf4-f2804c3127a4']
def test_integration_create_resource_invalid_input(app, example_schema, example_model): class TestSchema(marshmallow_jsonapi.Schema): id = fields.UUID() f1 = fields.Str(required=True) f2 = fields.Str(required=True) class Meta: type_ = 'test' strict = True class ExampleListView(resources.ResourceList): schema = TestSchema def create(self, *args, **kwargs): return example_model(id='f60717a3-7dc2-4f1a-bdf4-f2804c3127a4', body='Nice body.') json_data = json.dumps({ 'data': { 'type': 'test', } }) application_api = api.Api(app) application_api.route(ExampleListView, 'example_list', '/examples/') response = app.test_client().post( '/examples/', headers=JSONAPI_HEADERS, data=json_data, ) result = json.loads(response.data.decode()) assert result == { 'errors': mock.ANY, "jsonapi": { "version": "1.0" } } assert list(sorted(result['errors'], key=lambda x: x['source']['pointer'])) == [ { 'detail': 'Missing data for required field.', 'source': {'pointer': '/data/attributes/f1'} }, { 'detail': 'Missing data for required field.', 'source': {'pointer': '/data/attributes/f2'} } ]
def test_integration_bad_content_type_header(app, example_schema, example_model): class ExampleListView(resources.ResourceList): schema = example_schema application_api = api.Api(app) application_api.route(ExampleListView, 'example_list', '/examples/') response = app.test_client().post('/examples/', headers={'accept': 'application/vnd.api+json'}) assert response.status_code == 415 assert json.loads(response.data.decode()) == { 'errors': [{ 'detail': 'Content-Type header must be application/vnd.api+json', 'source': '', 'status': 415, 'title': 'InvalidRequestHeader' }], 'jsonapi': { 'version': '1.0' }, }
def test_integration_selective_decorator_not_decorated_method(app): application_api = api.Api(app) application_api.repository(ExampleDecoratedResourceRepositoryViewSet(), 'example', '/examples/') response = app.test_client().get( '/examples/f60717a3-7dc2-4f1a-bdf4-f2804c3127a4/', headers=JSONAPI_HEADERS) result = json.loads(response.data.decode()) assert result == { 'data': { 'id': 'f60717a3-7dc2-4f1a-bdf4-f2804c3127a4', 'type': 'example', 'attributes': { 'body': 'Gwynbelidd' } }, 'jsonapi': { 'version': '1.0' } }
def test_create_with_missing_data(app): application_api = api.Api(app) application_api.repository(TomatoRepositoryViewSet(), 'tomatoes', '/tomatoes/') json_data = json.dumps({ 'data': { 'type': 'tomato', 'id': '61042c6f-0c83-4c26-aa74-2cfbd025879a', } }) response = app.test_client().post( '/tomatoes/', headers=JSONAPI_HEADERS, data=json_data, ) result = json.loads(response.data.decode('utf-8')) errors = result['errors'] assert errors[0]['detail'] in 'Missing data for required field.' assert errors[0]['source'] == {'pointer': '/data/attributes/name'}
def test_integration_get_list(app, example_schema, example_model): class ExampleListView(resources.ResourceList): schema = example_schema def read_many(self, filters, pagination): return [ example_model(id='f60717a3-7dc2-4f1a-bdf4-f2804c3127a4', body='heheh'), example_model(id='f60717a3-7dc2-4f1a-bdf4-f2804c3127a5', body='hihi'), ] application_api = api.Api(app) application_api.route(ExampleListView, 'example_list', '/examples/') response = app.test_client().get( '/examples/', headers=JSONAPI_HEADERS ) result = json.loads(response.data.decode()) assert response.status_code == 200 assert result == { 'data': [ { 'id': 'f60717a3-7dc2-4f1a-bdf4-f2804c3127a4', 'type': 'example', 'attributes': { 'body': 'heheh' } }, { 'id': 'f60717a3-7dc2-4f1a-bdf4-f2804c3127a5', 'type': 'example', 'attributes': { 'body': 'hihi' }, } ], 'jsonapi': { 'version': '1.0' }, 'meta': { 'count': 2 } }
def test_get_parent_detail_without_include_data(self, app): application_api = api.Api(app) application_api.repository(ParentResourceRepositoryViewSet(), 'parent', '/parents/') response = app.test_client().get( '/parents/f60717a3-7dc2-4f1a-bdf4-f2804c3127a4/', headers=JSONAPI_HEADERS) result = json.loads(response.data.decode()) expected = { 'data': { 'type': 'parents', 'id': 'f60717a3-7dc2-4f1a-bdf4-f2804c3127a4', 'attributes': { 'name': 'Adam' } }, 'jsonapi': { 'version': '1.0' } } assert expected == result
def test_integration_selective_decorator_decorated_method(app): application_api = api.Api(app) application_api.repository(ExampleDecoratedResourceRepositoryViewSet(), 'example', '/examples/') json_data = json.dumps({ 'data': { 'type': 'example', 'id': 'f60717a3-7dc2-4f1a-bdf4-f2804c3127a4', 'attributes': { 'body': "Nice body.", } } }) response = app.test_client().post( '/examples/', headers=JSONAPI_HEADERS, data=json_data, ) result = response.data.decode() assert result == 'View decorated!'
def test_integration_patch_with_empty_response(app, ): application_api = api.Api(app) view_set = ExampleResourceRepositoryViewSet() application_api.repository(view_set, 'example', '/examples/') json_data = json.dumps({ 'data': { 'type': 'example', 'id': 'f60717a3-7dc2-4f1a-bdf4-f2804c3127a4', 'attributes': { 'body': "Nice body.", } } }) response = app.test_client().patch( '/examples/f60717a3-7dc2-4f1a-bdf4-f2804c3127a4/', headers=JSONAPI_HEADERS, data=json_data, ) assert response.status_code == 204 assert response.data == b''
def test_integration_pagination(app, example_schema): class ExampleListView(resources.ResourceList): schema = example_schema applied_pagination = {} def read_many(self, filters, sorting, pagination): self.applied_pagination.update(pagination) return [] def get_count(self, filters): return 0 application_api = api.Api(app) application_api.route(ExampleListView, 'example_list', '/examples/') response = app.test_client().get( '/examples/?page[size]=100&page[number]=50', headers=JSONAPI_HEADERS) assert response.status_code == 200 assert ExampleListView.applied_pagination == { 'size': 100, 'number': 50, }
def test_create_with_invalid_data(app): application_api = api.Api(app) application_api.repository(TomatoRepositoryViewSet(), 'tomatoes', '/tomatoes/') json_data = json.dumps({ 'data': { 'type': 'tomato', 'id': 1234, 'attributes': { 'name': 'red', }, }, }) response = app.test_client().post( '/tomatoes/', headers=JSONAPI_HEADERS, data=json_data, ) result = json.loads(response.data.decode('utf-8')) errors = result['errors'] assert errors[0]['detail'] in 'Not a valid string.' assert errors[0]['source'] == {'pointer': '/data/id'}
def test_integration_patch(app, example_schema, example_model): class ExampleDetailView(resources.ResourceDetail): schema = example_schema def update(self, id, data): data.pop('id') return example_model(id=id, **data) json_data = json.dumps({ 'data': { 'type': 'example', 'id': 'f60717a3-7dc2-4f1a-bdf4-f2804c3127a4', 'attributes': { 'body': "Nice body.", } } }) application_api = api.Api(app) application_api.route(ExampleDetailView, 'example_list', '/examples/<id>/') response = app.test_client().patch( '/examples/f60717a3-7dc2-4f1a-bdf4-f2804c3127a4/', headers=JSONAPI_HEADERS, data=json_data, ) assert json.loads(response.data.decode()) == { "data": { "type": "example", "id": "f60717a3-7dc2-4f1a-bdf4-f2804c3127a4", "attributes": { "body": "Nice body." } }, "jsonapi": { "version": "1.0" } }