Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cleanup filter tests, move to single file #866

Merged
merged 2 commits into from
Aug 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
295 changes: 0 additions & 295 deletions src/tests/test_dynamic_api/test_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,16 @@

import pytest
from django.apps import apps
from django.contrib.gis.geos import GEOSGeometry
from django.http import QueryDict
from django.utils.timezone import now
from rest_framework.exceptions import ValidationError
from rest_framework.status import HTTP_400_BAD_REQUEST
from rest_framework.test import APIClient
from schematools.contrib.django.models import Dataset
from schematools.permissions import UserScopes

from dso_api.dynamic_api.filters import parser
from dso_api.dynamic_api.filters.lookups import _sql_wildcards
from dso_api.dynamic_api.filters.values import _parse_point, _validate_correct_x_y, str2geo
from rest_framework_dso.crs import RD_NEW
from tests.utils import read_response_json


class TestWildcard:
Expand Down Expand Up @@ -363,297 +359,6 @@ def test_subobject_filters(verblijfsobjecten_model, verblijfsobjecten_data, quer
assert result.count() == expect, result


@pytest.mark.django_db
class TestDynamicFilterSet:
"""Test how the filters work using the API client and views."""

@staticmethod
@pytest.mark.parametrize(
"query,expect",
[
("regimes.dagen=ma,di,wo,do,vr", 1),
("regimes.dagen=ma,di,wo,do", 0),
("regimes.dagen=ma,di,wo,do,vr,za,zo", 1),
("regimes.dagen[contains]=ma,wo", 1),
("regimes.dagen[contains]=ma,wo,foo", 0),
],
)
def test_array_field(parkeervak, query, expect):
response = APIClient().get(f"/v1/parkeervakken/parkeervakken/?{query}")
data = read_response_json(response)
assert response.status_code == 200, data
assert len(data["_embedded"]["parkeervakken"]) == expect

@staticmethod
@pytest.mark.parametrize(
["query", "expect_code"],
[
("regimes.dagen=ma,di,wo,do,vr", 200),
("regimes.dagen.foo=test", 400),
("regimes.foo=test", 400), # subfield has different codepath if not found.
("foobar=test", 400),
],
)
def test_parsing_invalid(
parkeervakken_parkeervak_model, parkeervakken_regime_model, query, expect_code
):
"""Prove that looking for a relation on a nested field won't crash."""
response = APIClient().get(f"/v1/parkeervakken/parkeervakken/?{query}")
data = read_response_json(response)
assert response.status_code == expect_code, data

@staticmethod
def test_numerical_filters(parkeervakken_parkeervak_model, filled_router):
"""Test comparisons on numerical fields."""

p1 = parkeervakken_parkeervak_model.objects.create(id="p1", aantal=1)
p2 = parkeervakken_parkeervak_model.objects.create(id="p2", aantal=2)
p5 = parkeervakken_parkeervak_model.objects.create(id="p5", aantal=5)

for op, filter_aantal, expect in [
("lte", 1, [p1]),
("gt", 3, [p5]),
("lt", 3, [p1, p2]),
("gte", 6, []),
]:
response = APIClient().get(
"/v1/parkeervakken/parkeervakken/",
data={f"aantal[{op}]": str(filter_aantal)},
)
data = read_response_json(response)["_embedded"]["parkeervakken"]
assert len(data) == len(expect)
assert {x["id"] for x in data} == {x.id for x in expect}

@staticmethod
def test_geofilter_contains(parkeervakken_parkeervak_model, filled_router):
"""
Prove that geofilter contains filters work as expected.
"""
parkeervakken_parkeervak_model.objects.create(
id="121138489006",
type="File",
soort="MULDER",
aantal=1.0,
e_type="E6b",
buurtcode="A05d",
straatnaam="Zoutkeetsgracht",
geometry=GEOSGeometry(
"POLYGON((121140.66 489048.21, 121140.72 489047.1, 121140.8 489046.9, 121140.94 "
"489046.74,121141.11 489046.62, 121141.31 489046.55, 121141.52 489046.53, "
"121134.67 489045.85, 121134.47 489047.87, 121140.66 489048.21))",
28992,
),
)

# Inside using RD
response = APIClient().get(
"/v1/parkeervakken/parkeervakken/",
data={"geometry[contains]": "121137.7,489046.9"},
HTTP_ACCEPT_CRS=28992,
)
data = read_response_json(response)
assert len(data["_embedded"]["parkeervakken"]) == 1, "inside with R/D"

# Inside using WGS84
response = APIClient().get(
"/v1/parkeervakken/parkeervakken/",
data={"geometry[contains]": "52.388231,4.8897865"},
HTTP_ACCEPT_CRS=4326,
)
data = read_response_json(response)
assert response.status_code == 200, data
assert len(data["_embedded"]["parkeervakken"]) == 1, "inside with WGS84"

# Outside using WGS84
response = APIClient().get(
"/v1/parkeervakken/parkeervakken/",
data={"geometry[contains]": "52.3883019,4.8900356"},
HTTP_ACCEPT_CRS=4326,
)
data = read_response_json(response)
assert len(data["_embedded"]["parkeervakken"]) == 0, "Outside using WGS84"

# Invalid WGS84 coords
response = APIClient().get(
"/v1/parkeervakken/parkeervakken/",
data={"geometry[contains]": "52.388231,48897865"},
HTTP_ACCEPT_CRS=4326,
)
assert response.status_code == 400, "Outside WGS84 range"

@staticmethod
def test_filter_isempty(parkeervakken_parkeervak_model, filled_router):
parkeervakken_parkeervak_model.objects.create(
id="121138489006",
type="File",
soort="MULDER",
aantal=1.0,
e_type="E6b",
buurtcode="A05d",
straatnaam="Zoutkeetsgracht",
)
parkeervakken_parkeervak_model.objects.create(
id="121138489007",
type="File",
soort="",
aantal=1.0,
e_type="E6b",
buurtcode="A05d",
straatnaam="Zoutkeetsgracht",
)
parkeervakken_parkeervak_model.objects.create(
id="121138489008",
type="File",
soort=None,
aantal=1.0,
e_type="E6b",
buurtcode="A05d",
straatnaam="Zoutkeetsgracht",
)
response = APIClient().get(
"/v1/parkeervakken/parkeervakken/",
data={"soort[isempty]": "true"},
)
data = read_response_json(response)
assert len(data["_embedded"]["parkeervakken"]) == 2
assert (
data["_embedded"]["parkeervakken"][0]["id"] == "121138489007"
or data["_embedded"]["parkeervakken"][0]["id"] == "121138489008"
)

response = APIClient().get(
"/v1/parkeervakken/parkeervakken/",
data={"soort[isempty]": "false"},
)
data = read_response_json(response)
assert len(data["_embedded"]["parkeervakken"]) == 1
assert data["_embedded"]["parkeervakken"][0]["id"] == "121138489006"

@staticmethod
@pytest.mark.parametrize(
"query",
[
{"actors.name": "John Doe"},
{"actors.name[like]": "J*"},
],
)
def test_filter_m2m_results(movies_data_with_actors, filled_router, query):
"""Prove that filtering M2M models still return the objects just once."""
response = APIClient().get("/v1/movies/movie/", data=query)
data = read_response_json(response)
assert response.status_code == 200, data
assert data["_embedded"] == {
"movie": [
{
"_links": {
"schema": (
"https://schemas.data.amsterdam.nl/datasets/movies/dataset#movie"
),
"self": {
"href": "http://testserver/v1/movies/movie/3/",
"id": 3,
"title": "foo123",
},
"actors": [
# Both shown, even if filtered on existence of one.
{
"href": "http://testserver/v1/movies/actor/1/",
"id": "1",
"title": "John Doe",
},
{
"href": "http://testserver/v1/movies/actor/2/",
"id": "2",
"title": "Jane Doe",
},
],
"category": {
"href": "http://testserver/v1/movies/category/1/",
"id": 1,
"title": "bar",
},
},
"categoryId": 1,
"dateAdded": "2020-01-01T00:45:00",
"enjoyable": None,
"id": 3,
"name": "foo123",
"url": None,
}
]
}

@staticmethod
@pytest.mark.parametrize(
"query",
[
"movies.name=foo123",
"movies.id[in]=3,4",
"movies.name[not]=many",
],
)
def test_filter_reverse_fk(movies_data_with_actors, filled_router, query):
"""Prove that filtering reverse FK models works and returns the objects just once."""
response = APIClient().get(f"/v1/movies/category/?{query}")
data = read_response_json(response)
assert response.status_code == 200, data
assert set(data["_embedded"].keys()) == {"category"}, data
assert len(data["_embedded"]["category"]) == 1, data

@staticmethod
def test_filter_reverse_m2m(movies_data_with_actors, filled_router):
"""Prove that filtering M2M models works and returns the objects just once."""
response = APIClient().get("/v1/movies/actor/?movies.name=foo123")
data = read_response_json(response)
assert response.status_code == 200, data
assert set(data["_embedded"].keys()) == {"actor"}, data
assert len(data["_embedded"]["actor"]) == 2, data
names = [a["name"] for a in data["_embedded"]["actor"]]
assert names == ["John Doe", "Jane Doe"]

@staticmethod
@pytest.mark.parametrize(
"url",
[
"/v1/movies/movie/?nonexistent=foo123", # invalid normal field
"/v1/movies/movie/?name.nonexistent=foo123", # not a relation
"/v1/movies/category/?movies.nonexistent=foo123", # using reverse FK
"/v1/movies/category/?movies.name.nonexistent=foo123", # using reverse FK
"/v1/movies/actor/?movies.nonexistent=foo123", # using reverse M2M
"/v1/movies/actor/?movies.name.nonexistent=foo123", # using reverse M2M
],
)
def test_filter_invalid_field(movies_model, filled_router, url):
"""Prove that walking over M2M models works and doesn't crash the parser.
Note this uses the "movies" dataset, not the hardcoded movie models/serializers.
"""
response = APIClient().get(url)
data = read_response_json(response)
assert response.status_code == 400, data

@staticmethod
@pytest.mark.parametrize("params", [{"e_type": "whatever"}, {"_sort": "e_type"}])
def test_snake_case_400(params, parkeervakken_parkeervak_model):
"""Filter names match property names in the schema.
We no longer accept snake-cased property names."""
response = APIClient().get("/v1/parkeervakken/parkeervakken/", data=params)
assert response.status_code == HTTP_400_BAD_REQUEST


@pytest.mark.django_db
def test_temporal_relation_isnull(huishoudelijkafval_data, filled_router):
"""isnull on a relation should check whether a relation exists at all,
not check that a relation exists with a null identifier.
"""
response = APIClient().get(
"/v1/huishoudelijkafval/cluster/?bagNummeraanduiding.identificatie[isnull]=true"
)
assert response.status_code == 200
data = read_response_json(response)

assert len(data["_embedded"]["cluster"]) > 0


@pytest.mark.parametrize(
"value",
[
Expand Down
Empty file.
Loading