Skip to content

Commit 7355a80

Browse files
committed
move item collection tests
1 parent 62e8d30 commit 7355a80

File tree

2 files changed

+192
-184
lines changed

2 files changed

+192
-184
lines changed

stac_fastapi/tests/api/test_api.py

Lines changed: 0 additions & 184 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import json
21
import os
32
import random
43
import uuid
@@ -1517,189 +1516,6 @@ async def test_search_collection_limit_env_variable(
15171516
assert int(limit) == len(resp_json["features"])
15181517

15191518

1520-
@pytest.mark.asyncio
1521-
async def test_collection_items_limit_env_variable(
1522-
app_client, txn_client, load_test_data
1523-
):
1524-
limit = "5"
1525-
os.environ["STAC_ITEM_LIMIT"] = limit
1526-
1527-
test_collection = load_test_data("test_collection.json")
1528-
test_collection_id = "test-collection-items-limit"
1529-
test_collection["id"] = test_collection_id
1530-
await create_collection(txn_client, test_collection)
1531-
1532-
item = load_test_data("test_item.json")
1533-
item["collection"] = test_collection_id
1534-
1535-
for i in range(10):
1536-
test_item = item.copy()
1537-
test_item["id"] = f"test-item-collection-{i}"
1538-
await create_item(txn_client, test_item)
1539-
1540-
resp = await app_client.get(f"/collections/{test_collection_id}/items")
1541-
assert resp.status_code == 200
1542-
resp_json = resp.json()
1543-
assert int(limit) == len(resp_json["features"])
1544-
1545-
1546-
@pytest.mark.asyncio
1547-
async def test_collection_items_sort_desc(app_client, txn_client, ctx):
1548-
"""Verify GET /collections/{collectionId}/items honors descending sort on properties.datetime."""
1549-
first_item = ctx.item
1550-
1551-
# Create a second item in the same collection with an earlier datetime
1552-
second_item = dict(first_item)
1553-
second_item["id"] = "another-item-for-collection-sort-desc"
1554-
another_item_date = datetime.strptime(
1555-
first_item["properties"]["datetime"], "%Y-%m-%dT%H:%M:%SZ"
1556-
) - timedelta(days=1)
1557-
second_item["properties"]["datetime"] = another_item_date.strftime(
1558-
"%Y-%m-%dT%H:%M:%SZ"
1559-
)
1560-
1561-
await create_item(txn_client, second_item)
1562-
1563-
# Descending sort: the original (newer) item should come first
1564-
resp = await app_client.get(
1565-
f"/collections/{first_item['collection']}/items",
1566-
params=[("sortby", "-properties.datetime")],
1567-
)
1568-
assert resp.status_code == 200
1569-
resp_json = resp.json()
1570-
assert resp_json["features"][0]["id"] == first_item["id"]
1571-
assert resp_json["features"][1]["id"] == second_item["id"]
1572-
1573-
1574-
@pytest.mark.asyncio
1575-
async def test_collection_items_sort_asc(app_client, txn_client, ctx):
1576-
"""Verify GET /collections/{collectionId}/items honors ascending sort on properties.datetime."""
1577-
first_item = ctx.item
1578-
1579-
# Create a second item in the same collection with an earlier datetime
1580-
second_item = dict(first_item)
1581-
second_item["id"] = "another-item-for-collection-sort-asc"
1582-
another_item_date = datetime.strptime(
1583-
first_item["properties"]["datetime"], "%Y-%m-%dT%H:%M:%SZ"
1584-
) - timedelta(days=1)
1585-
second_item["properties"]["datetime"] = another_item_date.strftime(
1586-
"%Y-%m-%dT%H:%M:%SZ"
1587-
)
1588-
1589-
await create_item(txn_client, second_item)
1590-
1591-
# Ascending sort: the older item should come first
1592-
resp = await app_client.get(
1593-
f"/collections/{first_item['collection']}/items",
1594-
params=[("sortby", "+properties.datetime")],
1595-
)
1596-
assert resp.status_code == 200
1597-
resp_json = resp.json()
1598-
assert resp_json["features"][0]["id"] == second_item["id"]
1599-
assert resp_json["features"][1]["id"] == first_item["id"]
1600-
1601-
# Also verify bare field (no +) sorts ascending by default
1602-
resp = await app_client.get(
1603-
f"/collections/{first_item['collection']}/items",
1604-
params=[("sortby", "properties.datetime")],
1605-
)
1606-
assert resp.status_code == 200
1607-
resp_json = resp.json()
1608-
assert resp_json["features"][0]["id"] == second_item["id"]
1609-
assert resp_json["features"][1]["id"] == first_item["id"]
1610-
1611-
1612-
@pytest.mark.asyncio
1613-
async def test_item_collection_query(app_client, txn_client, ctx):
1614-
"""Simple query parameter test on the Item Collection route.
1615-
1616-
Creates an item with a unique property and ensures it can be retrieved
1617-
using the 'query' parameter on GET /collections/{collection_id}/items.
1618-
"""
1619-
unique_val = str(uuid.uuid4())
1620-
test_item = deepcopy(ctx.item)
1621-
test_item["id"] = f"query-basic-{unique_val}"
1622-
# Add a property to filter on
1623-
test_item.setdefault("properties", {})["test_query_key"] = unique_val
1624-
1625-
await create_item(txn_client, test_item)
1626-
1627-
# Provide the query parameter as a JSON string without adding new imports
1628-
query_param = f'{{"test_query_key": {{"eq": "{unique_val}"}}}}'
1629-
1630-
resp = await app_client.get(
1631-
f"/collections/{test_item['collection']}/items",
1632-
params=[("query", query_param)],
1633-
)
1634-
assert resp.status_code == 200
1635-
resp_json = resp.json()
1636-
ids = [f["id"] for f in resp_json["features"]]
1637-
assert test_item["id"] in ids
1638-
1639-
1640-
@pytest.mark.asyncio
1641-
async def test_filter_by_id(app_client, ctx):
1642-
"""Test filtering items by ID using the filter parameter."""
1643-
# Get the test item and collection from the context
1644-
item = ctx.item
1645-
collection_id = item["collection"]
1646-
item_id = item["id"]
1647-
1648-
# Create a filter to match the item by ID
1649-
filter_body = {"op": "=", "args": [{"property": "id"}, item_id]}
1650-
1651-
# Make the request with the filter
1652-
params = [("filter", json.dumps(filter_body)), ("filter-lang", "cql2-json")]
1653-
1654-
resp = await app_client.get(
1655-
f"/collections/{collection_id}/items",
1656-
params=params,
1657-
)
1658-
1659-
# Verify the response
1660-
assert resp.status_code == 200
1661-
resp_json = resp.json()
1662-
1663-
# Should find exactly one matching item
1664-
assert len(resp_json["features"]) == 1
1665-
assert resp_json["features"][0]["id"] == item_id
1666-
assert resp_json["features"][0]["collection"] == collection_id
1667-
1668-
1669-
@pytest.mark.asyncio
1670-
async def test_filter_by_nonexistent_id(app_client, ctx, txn_client):
1671-
"""Test filtering with a non-existent ID returns no results."""
1672-
# Get the test collection and item from context
1673-
collection_id = ctx.collection["id"]
1674-
item_id = ctx.item["id"]
1675-
1676-
# First, verify the item exists
1677-
resp = await app_client.get(f"/collections/{collection_id}/items/{item_id}")
1678-
assert resp.status_code == 200
1679-
1680-
# Create a non-existent ID
1681-
non_existent_id = f"non-existent-{str(uuid.uuid4())}"
1682-
1683-
# Create a filter with the non-existent ID using CQL2-JSON syntax
1684-
filter_body = {"op": "=", "args": [{"property": "id"}, non_existent_id]}
1685-
1686-
# URL-encode the filter JSON
1687-
import urllib.parse
1688-
1689-
encoded_filter = urllib.parse.quote(json.dumps(filter_body))
1690-
1691-
# Make the request with URL-encoded filter in the query string
1692-
url = f"/collections/{collection_id}/items?filter-lang=cql2-json&filter={encoded_filter}"
1693-
resp = await app_client.get(url)
1694-
1695-
# Verify the response
1696-
assert resp.status_code == 200
1697-
resp_json = resp.json()
1698-
assert (
1699-
len(resp_json["features"]) == 0
1700-
), f"Expected no items with ID {non_existent_id}, but found {len(resp_json['features'])} matches"
1701-
1702-
17031519
async def test_search_max_item_limit(
17041520
app_client, load_test_data, txn_client, monkeypatch
17051521
):
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
import json
2+
import os
3+
import uuid
4+
from copy import deepcopy
5+
from datetime import datetime, timedelta
6+
7+
import pytest
8+
9+
from ..conftest import create_collection, create_item
10+
11+
12+
@pytest.mark.asyncio
13+
async def test_item_collection_limit_env_variable(
14+
app_client, txn_client, load_test_data
15+
):
16+
limit = "5"
17+
os.environ["STAC_ITEM_LIMIT"] = limit
18+
19+
test_collection = load_test_data("test_collection.json")
20+
test_collection_id = "test-collection-items-limit"
21+
test_collection["id"] = test_collection_id
22+
await create_collection(txn_client, test_collection)
23+
24+
item = load_test_data("test_item.json")
25+
item["collection"] = test_collection_id
26+
27+
for i in range(10):
28+
test_item = item.copy()
29+
test_item["id"] = f"test-item-collection-{i}"
30+
await create_item(txn_client, test_item)
31+
32+
resp = await app_client.get(f"/collections/{test_collection_id}/items")
33+
assert resp.status_code == 200
34+
resp_json = resp.json()
35+
assert int(limit) == len(resp_json["features"])
36+
37+
38+
@pytest.mark.asyncio
39+
async def test_item_collection_sort_desc(app_client, txn_client, ctx):
40+
"""Verify GET /collections/{collectionId}/items honors descending sort on properties.datetime."""
41+
first_item = ctx.item
42+
43+
# Create a second item in the same collection with an earlier datetime
44+
second_item = dict(first_item)
45+
second_item["id"] = "another-item-for-collection-sort-desc"
46+
another_item_date = datetime.strptime(
47+
first_item["properties"]["datetime"], "%Y-%m-%dT%H:%M:%SZ"
48+
) - timedelta(days=1)
49+
second_item["properties"]["datetime"] = another_item_date.strftime(
50+
"%Y-%m-%dT%H:%M:%SZ"
51+
)
52+
53+
await create_item(txn_client, second_item)
54+
55+
# Descending sort: the original (newer) item should come first
56+
resp = await app_client.get(
57+
f"/collections/{first_item['collection']}/items",
58+
params=[("sortby", "-properties.datetime")],
59+
)
60+
assert resp.status_code == 200
61+
resp_json = resp.json()
62+
assert resp_json["features"][0]["id"] == first_item["id"]
63+
assert resp_json["features"][1]["id"] == second_item["id"]
64+
65+
66+
@pytest.mark.asyncio
67+
async def test_item_collection_sort_asc(app_client, txn_client, ctx):
68+
"""Verify GET /collections/{collectionId}/items honors ascending sort on properties.datetime."""
69+
first_item = ctx.item
70+
71+
# Create a second item in the same collection with an earlier datetime
72+
second_item = dict(first_item)
73+
second_item["id"] = "another-item-for-collection-sort-asc"
74+
another_item_date = datetime.strptime(
75+
first_item["properties"]["datetime"], "%Y-%m-%dT%H:%M:%SZ"
76+
) - timedelta(days=1)
77+
second_item["properties"]["datetime"] = another_item_date.strftime(
78+
"%Y-%m-%dT%H:%M:%SZ"
79+
)
80+
81+
await create_item(txn_client, second_item)
82+
83+
# Ascending sort: the older item should come first
84+
resp = await app_client.get(
85+
f"/collections/{first_item['collection']}/items",
86+
params=[("sortby", "+properties.datetime")],
87+
)
88+
assert resp.status_code == 200
89+
resp_json = resp.json()
90+
assert resp_json["features"][0]["id"] == second_item["id"]
91+
assert resp_json["features"][1]["id"] == first_item["id"]
92+
93+
# Also verify bare field (no +) sorts ascending by default
94+
resp = await app_client.get(
95+
f"/collections/{first_item['collection']}/items",
96+
params=[("sortby", "properties.datetime")],
97+
)
98+
assert resp.status_code == 200
99+
resp_json = resp.json()
100+
assert resp_json["features"][0]["id"] == second_item["id"]
101+
assert resp_json["features"][1]["id"] == first_item["id"]
102+
103+
104+
@pytest.mark.asyncio
105+
async def test_item_collection_query(app_client, txn_client, ctx):
106+
"""Simple query parameter test on the Item Collection route.
107+
108+
Creates an item with a unique property and ensures it can be retrieved
109+
using the 'query' parameter on GET /collections/{collection_id}/items.
110+
"""
111+
unique_val = str(uuid.uuid4())
112+
test_item = deepcopy(ctx.item)
113+
test_item["id"] = f"query-basic-{unique_val}"
114+
# Add a property to filter on
115+
test_item.setdefault("properties", {})["test_query_key"] = unique_val
116+
117+
await create_item(txn_client, test_item)
118+
119+
# Provide the query parameter as a JSON string without adding new imports
120+
query_param = f'{{"test_query_key": {{"eq": "{unique_val}"}}}}'
121+
122+
resp = await app_client.get(
123+
f"/collections/{test_item['collection']}/items",
124+
params=[("query", query_param)],
125+
)
126+
assert resp.status_code == 200
127+
resp_json = resp.json()
128+
ids = [f["id"] for f in resp_json["features"]]
129+
assert test_item["id"] in ids
130+
131+
132+
@pytest.mark.asyncio
133+
async def test_item_collection_filter_by_id(app_client, ctx):
134+
"""Test filtering items by ID using the filter parameter."""
135+
# Get the test item and collection from the context
136+
item = ctx.item
137+
collection_id = item["collection"]
138+
item_id = item["id"]
139+
140+
# Create a filter to match the item by ID
141+
filter_body = {"op": "=", "args": [{"property": "id"}, item_id]}
142+
143+
# Make the request with the filter
144+
params = [("filter", json.dumps(filter_body)), ("filter-lang", "cql2-json")]
145+
146+
resp = await app_client.get(
147+
f"/collections/{collection_id}/items",
148+
params=params,
149+
)
150+
151+
# Verify the response
152+
assert resp.status_code == 200
153+
resp_json = resp.json()
154+
155+
# Should find exactly one matching item
156+
assert len(resp_json["features"]) == 1
157+
assert resp_json["features"][0]["id"] == item_id
158+
assert resp_json["features"][0]["collection"] == collection_id
159+
160+
161+
@pytest.mark.asyncio
162+
async def test_item_collection_filter_by_nonexistent_id(app_client, ctx, txn_client):
163+
"""Test filtering with a non-existent ID returns no results."""
164+
# Get the test collection and item from context
165+
collection_id = ctx.collection["id"]
166+
item_id = ctx.item["id"]
167+
168+
# First, verify the item exists
169+
resp = await app_client.get(f"/collections/{collection_id}/items/{item_id}")
170+
assert resp.status_code == 200
171+
172+
# Create a non-existent ID
173+
non_existent_id = f"non-existent-{str(uuid.uuid4())}"
174+
175+
# Create a filter with the non-existent ID using CQL2-JSON syntax
176+
filter_body = {"op": "=", "args": [{"property": "id"}, non_existent_id]}
177+
178+
# URL-encode the filter JSON
179+
import urllib.parse
180+
181+
encoded_filter = urllib.parse.quote(json.dumps(filter_body))
182+
183+
# Make the request with URL-encoded filter in the query string
184+
url = f"/collections/{collection_id}/items?filter-lang=cql2-json&filter={encoded_filter}"
185+
resp = await app_client.get(url)
186+
187+
# Verify the response
188+
assert resp.status_code == 200
189+
resp_json = resp.json()
190+
assert (
191+
len(resp_json["features"]) == 0
192+
), f"Expected no items with ID {non_existent_id}, but found {len(resp_json['features'])} matches"

0 commit comments

Comments
 (0)