-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Reorganize view tests in separate files
The test_views_api was 2500+ lines long and became quite a jungle to get through..
- Loading branch information
Showing
8 changed files
with
2,560 additions
and
2,554 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,316 @@ | ||
import pytest | ||
from django.db import connection | ||
from django.urls import NoReverseMatch, reverse | ||
from rest_framework.status import HTTP_200_OK, HTTP_400_BAD_REQUEST | ||
from schematools.contrib.django.db import create_tables | ||
|
||
from rest_framework_dso.crs import CRS, RD_NEW | ||
from tests.utils import read_response_json | ||
|
||
|
||
@pytest.mark.django_db | ||
def test_list_dynamic_view_reload(api_client, api_rf, router, bommen_dataset): | ||
"""Prove that building the router also creates the available viewsets.""" | ||
with pytest.raises(NoReverseMatch): | ||
reverse("dynamic_api:bommen-bommen-list") | ||
|
||
router_urls = [p.name for p in router.urls] | ||
assert router_urls == ["api-root"] | ||
|
||
# Prove that the router URLs are extended on adding a model | ||
router.reload() | ||
assert len(router.urls) > 1 | ||
|
||
# Make sure the tables are created too | ||
if "bommen_bommen" not in connection.introspection.table_names(): | ||
create_tables(bommen_dataset, base_app_name="dso_api.dynamic_api") | ||
|
||
# Prove that URLs can now be resolved. | ||
url = reverse("dynamic_api:bommen-bommen-list") | ||
|
||
# Prove that the view is available and works | ||
response = api_client.get(url) | ||
data = read_response_json(response) | ||
|
||
assert response.status_code == 200, data | ||
assert data == { | ||
"_links": { | ||
"self": {"href": "http://testserver/v1/bommen/bommen/"}, | ||
}, | ||
"_embedded": {"bommen": []}, | ||
"page": {"number": 1, "size": 20}, | ||
} | ||
assert response["X-Pagination-Page"] == "1" | ||
assert response["X-Pagination-Limit"] == "20" | ||
|
||
|
||
@pytest.mark.django_db | ||
def test_filled_router(api_client, bommen_dataset, filled_router): | ||
"""Prove that building the router also creates the available viewsets.""" | ||
url = reverse("dynamic_api:bommen-bommen-list") | ||
response = api_client.get(url) | ||
assert response.status_code == 200, response.data | ||
|
||
|
||
@pytest.mark.django_db | ||
def test_list_dynamic_view_unregister(api_client, bommen_dataset, filled_router): | ||
"""Prove that unregistering works.""" | ||
url = reverse("dynamic_api:bommen-bommen-list") | ||
|
||
# Normal requests give a 200 | ||
response = api_client.get(url) | ||
assert response.status_code == 200 | ||
|
||
# Prove that unloading the model also removes the API from urls.py | ||
filled_router.clear_urls() | ||
response = api_client.get(url) | ||
assert response.status_code == 404 | ||
|
||
|
||
@pytest.mark.django_db | ||
class TestDSOViewMixin: | ||
"""Prove that the DSO view mixin logic is used within the dynamic API.""" | ||
|
||
def test_not_supported_crs(self, api_client, afval_dataset, filled_router): | ||
"""Prove that invalid CRS leads to a 406 status""" | ||
url = reverse("dynamic_api:afvalwegingen-containers-list") | ||
response = api_client.get(url, HTTP_ACCEPT_CRS="EPSG:2000") | ||
assert response.status_code == 406, response.data | ||
|
||
def test_bogus_crs(self, api_client, afval_dataset, filled_router): | ||
"""Prove that invalid CRS leads to a 406 status""" | ||
url = reverse("dynamic_api:afvalwegingen-containers-list") | ||
for crs in ("nonsense", "EPSG:", "EPSG:foo"): | ||
response = api_client.get(url, HTTP_ACCEPT_CRS=crs) | ||
assert response.status_code == 406, response.data | ||
|
||
def test_response_has_crs_from_accept_crs(self, api_client, afval_dataset, filled_router): | ||
"""Prove that response has a CRS header taken from the Accept-Crs header""" | ||
url = reverse("dynamic_api:afvalwegingen-containers-list") | ||
response = api_client.get(url, HTTP_ACCEPT_CRS="EPSG:4258") | ||
assert response.status_code == 200, response.data | ||
assert response.has_header("Content-Crs"), dict(response.items()) | ||
assert CRS.from_string("EPSG:4258") == CRS.from_string(response["Content-Crs"]) | ||
|
||
def test_response_has_crs_from_accept_crs_empty_data( | ||
self, api_client, afval_dataset, filled_router | ||
): | ||
"""Prove that response has a CRS header taken from the Accept-Crs header""" | ||
url = reverse("dynamic_api:afvalwegingen-containers-list") | ||
response = api_client.get(url, HTTP_ACCEPT_CRS="EPSG:4258") | ||
assert response.status_code == 200, response.data | ||
assert response.has_header("Content-Crs"), dict(response.items()) | ||
assert CRS.from_string("EPSG:4258") == CRS.from_string(response["Content-Crs"]) | ||
|
||
def test_response_has_crs_from_content(self, api_client, afval_container, filled_router): | ||
"""Prove that response has a CRS header taken from the Accept-Crs header""" | ||
url = reverse("dynamic_api:afvalwegingen-containers-list") | ||
response = api_client.get(url) | ||
assert response.status_code == 200, response.data | ||
assert response.has_header("Content-Crs"), dict(response.items()) | ||
assert CRS.from_string(response["Content-Crs"]) == RD_NEW | ||
|
||
|
||
@pytest.mark.django_db | ||
class TestListFilters: | ||
"""Prove that filtering works as expected.""" | ||
|
||
@staticmethod | ||
def test_filter_no_such_field(api_client, afval_dataset, filled_router): | ||
url = reverse("dynamic_api:afvalwegingen-containers-list") | ||
|
||
# Non-existing field. | ||
response = api_client.get(url, data={"nietbestaand": ""}) | ||
assert response.status_code == HTTP_400_BAD_REQUEST, response.data | ||
reason = response.data["invalid-params"][0]["reason"] | ||
assert reason == "Field 'nietbestaand' does not exist" | ||
|
||
@staticmethod | ||
@pytest.mark.parametrize("param", ["buurtcode[", "buurtcode[exact", "buurtcodeexact]"]) | ||
def test_filter_syntax_error(param, api_client, afval_dataset, filled_router): | ||
"""Existing field, but syntax error in the filter parameter.""" | ||
url = reverse("dynamic_api:afvalwegingen-containers-list") | ||
|
||
response = api_client.get(url, data={param: ""}) | ||
assert response.status_code == HTTP_400_BAD_REQUEST, response.data | ||
reason = response.data["invalid-params"][0]["reason"] | ||
assert param in reason | ||
|
||
@staticmethod | ||
@pytest.mark.parametrize( | ||
"lookup,expect", | ||
[ | ||
("", 0), | ||
("[like]", 1), | ||
], | ||
) | ||
def test_list_filter_wildcard(api_client, movies_data, filled_router, lookup, expect): | ||
"""Prove that ?name=foo doesn't work with wildcards (that now requires [like]). | ||
Second parameterized call tests whether using [like] does produce the desired effect. | ||
""" | ||
response = api_client.get("/v1/movies/movie/", data={f"name{lookup}": "foo1?3"}) | ||
assert response.status_code == 200, response | ||
assert response["Content-Type"] == "application/hal+json" | ||
data = read_response_json(response) | ||
assert len(data["_embedded"]["movie"]) == expect | ||
|
||
@staticmethod | ||
def test_list_filter_datetime(api_client, movies_data, filled_router): | ||
"""Prove that datetime fields can be queried using a single data value""" | ||
response = api_client.get("/v1/movies/movie/", data={"dateAdded": "2020-01-01"}) | ||
data = read_response_json(response) | ||
assert response.status_code == 200, response | ||
assert response["Content-Type"] == "application/hal+json" | ||
names = [movie["name"] for movie in data["_embedded"]["movie"]] | ||
assert names == ["foo123"] | ||
|
||
@staticmethod | ||
def test_list_filter_datetime_invalid(api_client, movies_data, filled_router): | ||
"""Prove that invalid input is captured, and returns a proper error response.""" | ||
response = api_client.get("/v1/movies/movie/", data={"dateAdded": "2020-01-fubar"}) | ||
assert response.status_code == 400, response | ||
assert response["Content-Type"] == "application/problem+json", response # check first | ||
data = read_response_json(response) | ||
assert response["Content-Type"] == "application/problem+json", response # and after | ||
assert data == { | ||
"type": "urn:apiexception:invalid", | ||
"title": "Invalid input.", | ||
"status": 400, | ||
"instance": "http://testserver/v1/movies/movie/?dateAdded=2020-01-fubar", | ||
"invalid-params": [ | ||
{ | ||
"type": "urn:apiexception:invalid:invalid", | ||
"name": "dateAdded", | ||
"reason": "Enter a valid ISO date-time, or single date.", | ||
} | ||
], | ||
"x-validation-errors": {"dateAdded": ["Enter a valid ISO date-time, or single date."]}, | ||
} | ||
|
||
@pytest.mark.django_db | ||
def test_relation_filter( | ||
self, api_client, vestiging_dataset, vestiging1, vestiging2, filled_router | ||
): | ||
url = reverse("dynamic_api:vestiging-vestiging-list") | ||
response = api_client.get(url) | ||
assert response.status_code == HTTP_200_OK | ||
data = read_response_json(response) | ||
assert len(data["_embedded"]["vestiging"]) == 2 | ||
|
||
response = api_client.get(url, data={"bezoekAdresId": "1"}) | ||
assert response.status_code == HTTP_200_OK, response.data | ||
data = read_response_json(response) | ||
assert len(data["_embedded"]["vestiging"]) == 1 | ||
assert data["_embedded"]["vestiging"][0]["bezoekAdresId"] == 1 | ||
|
||
|
||
@pytest.mark.django_db | ||
class TestLimitFields: | ||
"""Test how the ?_fields=... parameter works with all extra serializer weight.""" | ||
|
||
@staticmethod | ||
def test_fields(api_client, afval_dataset, afval_container, filled_router): | ||
url = reverse("dynamic_api:afvalwegingen-containers-list") | ||
response = api_client.get(url, data={"_fields": "id,serienummer"}) | ||
assert response.status_code == HTTP_200_OK, response.data | ||
data = read_response_json(response) | ||
assert data["_embedded"] == { | ||
"containers": [ | ||
{ | ||
"_links": { | ||
# _links block still exists with self link: | ||
"self": { | ||
"href": "http://testserver/v1/afvalwegingen/containers/1/", | ||
"id": 1, | ||
"title": "1", | ||
}, | ||
"schema": "https://schemas.data.amsterdam.nl/datasets/afvalwegingen/dataset#containers", # noqa: E501 | ||
}, | ||
"id": 1, | ||
"serienummer": "foobar-123", | ||
} | ||
] | ||
} | ||
|
||
|
||
@pytest.mark.django_db | ||
class TestSort: | ||
"""Prove that the ordering works as expected.""" | ||
|
||
@staticmethod | ||
def test_list_ordering_name(api_client, movies_data, filled_router): | ||
"""Prove that ?_sort=... works on the list view.""" | ||
|
||
response = api_client.get("/v1/movies/movie/", data={"_sort": "name"}) | ||
data = read_response_json(response) | ||
assert response.status_code == 200, data | ||
names = [movie["name"] for movie in data["_embedded"]["movie"]] | ||
assert names == ["foo123", "test"], data | ||
|
||
response = api_client.get("/v1/movies/movie/", data={"_sort": "-name"}) | ||
data = read_response_json(response) | ||
assert response.status_code == 200, data | ||
names = [movie["name"] for movie in data["_embedded"]["movie"]] | ||
assert names == ["test", "foo123"], data | ||
|
||
@staticmethod | ||
def test_list_ordering_name_old_param(api_client, movies_data, filled_router): | ||
"""Prove that ?_sort=... works on the list view.""" | ||
|
||
response = api_client.get("/v1/movies/movie/", data={"sorteer": "-name"}) | ||
data = read_response_json(response) | ||
|
||
assert response.status_code == 200, data | ||
names = [movie["name"] for movie in data["_embedded"]["movie"]] | ||
assert names == ["test", "foo123"] | ||
|
||
@staticmethod | ||
def test_list_ordering_date(api_client, movies_data, filled_router): | ||
"""Prove that ?_sort=... works on the list view.""" | ||
response = api_client.get("/v1/movies/movie/", data={"_sort": "-dateAdded"}) | ||
data = read_response_json(response) | ||
assert response.status_code == 200, data | ||
names = [movie["name"] for movie in data["_embedded"]["movie"]] | ||
assert names == ["test", "foo123"] | ||
|
||
@staticmethod | ||
def test_list_ordering_invalid(api_client, movies_category, django_assert_num_queries): | ||
"""Prove that ?_sort=... only works on a fixed set of fields.""" | ||
response = api_client.get("/v1/movies/movie/", data={"_sort": "foobarvalue"}) | ||
data = read_response_json(response) | ||
|
||
assert response.status_code == 400, data | ||
assert data == { | ||
"type": "urn:apiexception:invalid", | ||
"title": "Invalid input.", | ||
"status": 400, | ||
"instance": "http://testserver/v1/movies/movie/?_sort=foobarvalue", | ||
"invalid-params": [ | ||
{ | ||
"type": "urn:apiexception:invalid:invalid", | ||
"name": "invalid", | ||
"reason": "Field 'foobarvalue' does not exist", | ||
} | ||
], | ||
"x-validation-errors": ["Field 'foobarvalue' does not exist"], | ||
} | ||
|
||
|
||
@pytest.mark.django_db | ||
def test_nested_object_field_response( | ||
api_client, verblijfsobjecten_model, panden_data, verblijfsobjecten_data, filled_router | ||
): | ||
"""Prove that a nested object fields provides a correct response. | ||
The nested field will be "flattened", so the subfields will be expanded into the main table. | ||
""" | ||
url = reverse("dynamic_api:bag-panden-list") | ||
response = api_client.get( | ||
url, | ||
data={"_expand": "true", "id": "0363100012061164.3"}, | ||
) | ||
data = read_response_json(response) | ||
assert response.status_code == 200, data | ||
assert data["_embedded"]["panden"][0]["statusCode"] == 7 | ||
assert data["_embedded"]["panden"][0]["statusOmschrijving"] == "Sloopvergunning verleend" | ||
assert data["_embedded"]["panden"][0]["bagProces"] == {"code": 1} |
Oops, something went wrong.