Skip to content

Commit 4e92bd5

Browse files
authored
Add search params tests (#11)
1 parent 27c97e7 commit 4e92bd5

File tree

5 files changed

+453
-14
lines changed

5 files changed

+453
-14
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,5 +28,6 @@ dev = [
2828
"pytest-cov>=7.0.0",
2929
"requests>=2.32.5",
3030
"ruff>=0.12.12",
31+
"shapely>=2.1.2",
3132
"tqdm>=4.67.1",
3233
]

src/stac_fastapi_pgstac_pair_search/models.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import json
12
import logging
23
import os
34
import re
@@ -187,16 +188,25 @@ def end_date(self) -> Optional[dt]:
187188
@model_validator(mode="before")
188189
def validate_spatial(cls, values: Dict[str, Any]) -> Dict[str, Any]:
189190
for prefix in ["first", "second"]:
190-
if (
191-
values.get(f"{prefix}-intersects")
192-
and values.get(f"{prefix}-bbox") is not None
193-
):
191+
bbox = values.get(f"{prefix}-bbox")
192+
intersects = values.get(f"{prefix}-intersects")
193+
collections = values.get(f"{prefix}-collections")
194+
ids = values.get(f"{prefix}-ids")
195+
if bbox and intersects:
194196
raise ValueError(
195197
f"{prefix}-intersects and {prefix}-bbox parameters are mutually exclusive"
196198
)
197-
collections = values.get(f"{prefix}-collections")
199+
if bbox:
200+
if isinstance(bbox, str):
201+
values[f"{prefix}-bbox"] = list(map(float, bbox.split(",")))
202+
if intersects:
203+
if isinstance(intersects, str):
204+
values[f"{prefix}-intersects"] = json.loads(intersects)
198205
if isinstance(collections, str):
199206
values[f"{prefix}-collections"] = collections.split(",")
207+
if ids:
208+
if isinstance(ids, str):
209+
values[f"{prefix}-ids"] = [ids]
200210
filter_expr = values.get("filter_expr")
201211
if filter_expr:
202212
try:

tests/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ pytest>=8.4.2
44
pytest-cov>=7.0.0
55
requests>=2.32.5
66
ruff>=0.12.12
7+
shapely>=2.1.2
78
tqdm>=4.67.1

tests/test_app.py

Lines changed: 211 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import json
22

33
import pytest
4+
from shapely.geometry import box, mapping
45

56

67
def test_app(client):
@@ -181,10 +182,213 @@ def test_pair_search_response(client, method: str, response_type: str):
181182
assert first != second
182183

183184

184-
# https://pair-search-demo.eox.at/catalogue/pair-search?
185-
# response-type=pair&
186-
# first-collection=ASA_IMS_1P&
187-
# second-collection=ASA_IMS_1P&
188-
# first-bbox=12.6,41.8,12.7,41.9&
189-
# first-datetime=2003-01-01T00:00Z/2004-01-01T00:00Z&
190-
# filter=((N_DIFF(second.oads:baseline_perpendicular_offset, first.oads:baseline_perpendicular_offset) BETWEEN-500 AND 500) AND (T_DIFF(T_START(first.datetime),T_START(seconds.datetime)) BETWEEN TimeDelta('0D') ANDTimeDelta('356D') AND (first.sat:orbit_state = second.sat:orbit_state)AND (first.sar:beam_id = second.sar:beam_id) AND(first.sar:polarization = second.sar:polarization) AND(first.oads:wrs_longitude_grid = second.oads:wrs_longitude_grid) AND(first.oads:wrs_latitude_grid = second.oads:wrs_latitude_grid) AND(first.oads:mission_phase = second.oads:mission_phase))
185+
# first-bbox, second-bbox
186+
@pytest.mark.parametrize("method", ["get", "post"])
187+
@pytest.mark.parametrize("first_bbox", [None, [6.21203, 43.36210, 6.35085, 43.48009]])
188+
@pytest.mark.parametrize("second_bbox", [None, [6.21203, 43.36210, 6.35085, 43.48009]])
189+
def test_bbox(client, method: str, first_bbox, second_bbox):
190+
# bbox only intersects with product ASA_IMS_1PNESA20100602_094953_000000152090_00022_43162_0000
191+
intersecting_id = "ASA_IMS_1PNESA20100602_094953_000000152090_00022_43162_0000"
192+
params = {
193+
"first-collections": ["ENVISAT.ASA.IMS_1P"],
194+
"second-collections": ["ENVISAT.ASA.IMS_1P"],
195+
}
196+
if first_bbox:
197+
params["first-bbox"] = ",".join(map(str, first_bbox))
198+
if second_bbox:
199+
params["second-bbox"] = ",".join(map(str, second_bbox))
200+
201+
response = client.request(
202+
method=method,
203+
url="/pair-search",
204+
params=params if method == "get" else None,
205+
content=json.dumps(params) if method == "post" else None,
206+
)
207+
if response.status_code != 200:
208+
raise ValueError(f"Response: {response.status_code}, {response.text}")
209+
210+
response_json = response.json()
211+
212+
assert "numberPairsReturned" in response_json
213+
assert "numberPairsMatched" in response_json
214+
215+
# special case: if both bboxes are set, no pairs are returned, because there is
216+
# only one product
217+
if first_bbox and second_bbox:
218+
assert response_json["numberPairsReturned"] == 0
219+
assert response_json["numberPairsMatched"] == 0
220+
assert not response_json["featurePairs"]
221+
else:
222+
assert response_json["numberPairsReturned"] > 0
223+
assert response_json["numberPairsMatched"] > 0
224+
assert response_json["featurePairs"]
225+
for first, second in response_json["featurePairs"]:
226+
if first_bbox:
227+
assert first == intersecting_id
228+
if second_bbox:
229+
assert second == intersecting_id
230+
231+
232+
# first-intersects, second-intersects
233+
@pytest.mark.parametrize("method", ["get", "post"])
234+
@pytest.mark.parametrize(
235+
"first_intersects", [None, mapping(box(6.21203, 43.36210, 6.35085, 43.48009))]
236+
)
237+
@pytest.mark.parametrize(
238+
"second_intersects", [None, mapping(box(6.21203, 43.36210, 6.35085, 43.48009))]
239+
)
240+
def test_intersects(client, method: str, first_intersects, second_intersects):
241+
# bbox only intersects with product ASA_IMS_1PNESA20100602_094953_000000152090_00022_43162_0000
242+
intersecting_id = "ASA_IMS_1PNESA20100602_094953_000000152090_00022_43162_0000"
243+
params = {
244+
"first-collections": ["ENVISAT.ASA.IMS_1P"],
245+
"second-collections": ["ENVISAT.ASA.IMS_1P"],
246+
}
247+
if first_intersects:
248+
params["first-intersects"] = json.dumps(first_intersects, separators=(",", ":"))
249+
if second_intersects:
250+
params["second-intersects"] = json.dumps(
251+
second_intersects, separators=(",", ":")
252+
)
253+
254+
response = client.request(
255+
method=method,
256+
url="/pair-search",
257+
params=params if method == "get" else None,
258+
content=json.dumps(params) if method == "post" else None,
259+
)
260+
if response.status_code != 200:
261+
raise ValueError(f"Response: {response.status_code}, {response.text}")
262+
263+
response_json = response.json()
264+
265+
assert "numberPairsReturned" in response_json
266+
assert "numberPairsMatched" in response_json
267+
268+
# special case: if both bboxes are set, no pairs are returned, because there is
269+
# only one product
270+
if first_intersects and second_intersects:
271+
assert response_json["numberPairsReturned"] == 0
272+
assert response_json["numberPairsMatched"] == 0
273+
assert not response_json["featurePairs"]
274+
else:
275+
assert response_json["numberPairsReturned"] > 0
276+
assert response_json["numberPairsMatched"] > 0
277+
assert response_json["featurePairs"]
278+
for first, second in response_json["featurePairs"]:
279+
if first_intersects:
280+
assert first == intersecting_id
281+
if second_intersects:
282+
assert second == intersecting_id
283+
284+
285+
# first-ids, second-ids
286+
@pytest.mark.parametrize("method", ["get", "post"])
287+
@pytest.mark.parametrize(
288+
"first_ids",
289+
[
290+
None,
291+
"ASA_IMS_1PNESA20100602_094953_000000152090_00022_43162_0000",
292+
["ASA_IMS_1PNESA20100602_094953_000000152090_00022_43162_0000"],
293+
],
294+
)
295+
@pytest.mark.parametrize(
296+
"second_ids",
297+
[
298+
None,
299+
"ASA_IMS_1PNESA20100602_094953_000000152090_00022_43162_0000",
300+
["ASA_IMS_1PNESA20100602_094953_000000152090_00022_43162_0000"],
301+
],
302+
)
303+
def test_ids(client, method: str, first_ids, second_ids):
304+
intersecting_id = "ASA_IMS_1PNESA20100602_094953_000000152090_00022_43162_0000"
305+
params = {
306+
"first-collections": ["ENVISAT.ASA.IMS_1P"],
307+
"second-collections": ["ENVISAT.ASA.IMS_1P"],
308+
}
309+
if first_ids:
310+
params["first-ids"] = first_ids
311+
if second_ids:
312+
params["second-ids"] = second_ids
313+
314+
response = client.request(
315+
method=method,
316+
url="/pair-search",
317+
params=params if method == "get" else None,
318+
content=json.dumps(params) if method == "post" else None,
319+
)
320+
if response.status_code != 200:
321+
raise ValueError(f"Response: {response.status_code}, {response.text}")
322+
323+
response_json = response.json()
324+
325+
assert "numberPairsReturned" in response_json
326+
assert "numberPairsMatched" in response_json
327+
328+
# special case: if both bboxes are set, no pairs are returned, because there is
329+
# only one product
330+
if first_ids and second_ids:
331+
assert response_json["numberPairsReturned"] == 0
332+
assert response_json["numberPairsMatched"] == 0
333+
assert not response_json["featurePairs"]
334+
else:
335+
assert response_json["numberPairsReturned"] > 0
336+
assert response_json["numberPairsMatched"] > 0
337+
assert response_json["featurePairs"]
338+
for first, second in response_json["featurePairs"]:
339+
if first_ids:
340+
assert first == intersecting_id
341+
if second_ids:
342+
assert second == intersecting_id
343+
344+
345+
# first-datetime, second-datetime
346+
@pytest.mark.parametrize("method", ["get", "post"])
347+
@pytest.mark.parametrize(
348+
"first_datetime",
349+
[None, "2010-06-02T09:49:53.149000Z", "2010-06-02T00:00:00Z/2010-06-02T23:59:59Z"],
350+
)
351+
@pytest.mark.parametrize(
352+
"second_datetime",
353+
[None, "2010-06-02T09:49:53.149000Z", "2010-06-02T00:00:00Z/2010-06-02T23:59:59Z"],
354+
)
355+
def test_datetime(client, method: str, first_datetime, second_datetime):
356+
# bbox only intersects with product ASA_IMS_1PNESA20100602_094953_000000152090_00022_43162_0000
357+
intersecting_id = "ASA_IMS_1PNESA20100602_094953_000000152090_00022_43162_0000"
358+
params = {
359+
"first-collections": ["ENVISAT.ASA.IMS_1P"],
360+
"second-collections": ["ENVISAT.ASA.IMS_1P"],
361+
}
362+
if first_datetime:
363+
params["first-datetime"] = first_datetime
364+
if second_datetime:
365+
params["second-datetime"] = second_datetime
366+
367+
response = client.request(
368+
method=method,
369+
url="/pair-search",
370+
params=params if method == "get" else None,
371+
content=json.dumps(params) if method == "post" else None,
372+
)
373+
if response.status_code != 200:
374+
raise ValueError(f"Response: {response.status_code}, {response.text}")
375+
376+
response_json = response.json()
377+
assert "numberPairsReturned" in response_json
378+
assert "numberPairsMatched" in response_json
379+
380+
# special case: if both bboxes are set, no pairs are returned, because there is
381+
# only one product
382+
if first_datetime and second_datetime:
383+
assert response_json["numberPairsReturned"] == 0
384+
assert response_json["numberPairsMatched"] == 0
385+
assert not response_json["featurePairs"]
386+
else:
387+
assert response_json["numberPairsReturned"] > 0
388+
assert response_json["numberPairsMatched"] > 0
389+
assert response_json["featurePairs"]
390+
for first, second in response_json["featurePairs"]:
391+
if first_datetime:
392+
assert first == intersecting_id
393+
if second_datetime:
394+
assert second == intersecting_id

0 commit comments

Comments
 (0)