Skip to content

Commit 7b20977

Browse files
jonhealy1gadomskivincentsarago
authored
test the bulk transactions extension (#907)
* test bulk transactions * add pr number --------- Co-authored-by: Pete Gadomski <pete.gadomski@gmail.com> Co-authored-by: Vincent Sarago <vincent.sarago@gmail.com>
1 parent a04d866 commit 7b20977

3 files changed

Lines changed: 200 additions & 2 deletions

File tree

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
### Added
66

7+
- Add tests for the `bulk transactions` extension ([#907](https://github.com/stac-utils/stac-fastapi/pull/907))
78
- Add tests for the `sort` extension ([#906](https://github.com/stac-utils/stac-fastapi/pull/906))
89
- Add tests for the `fields` extension ([#902](https://github.com/stac-utils/stac-fastapi/pull/902))
910
- Simply add a list of present stac-fastapi extensions to the Readme ([#898](https://github.com/stac-utils/stac-fastapi/pull/898))
Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
"""Fields extension module."""
22

3-
from .bulk_transactions import BulkTransactionExtension
3+
from .bulk_transactions import (
4+
AsyncBaseBulkTransactionsClient,
5+
BaseBulkTransactionsClient,
6+
BulkTransactionExtension,
7+
BulkTransactionMethod,
8+
Items,
9+
)
410

5-
__all__ = ["BulkTransactionExtension"]
11+
__all__ = [
12+
"AsyncBaseBulkTransactionsClient",
13+
"BaseBulkTransactionsClient",
14+
"BulkTransactionExtension",
15+
"BulkTransactionMethod",
16+
"Items",
17+
]
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
"""Tests for the Bulk Transactions extension."""
2+
3+
from typing import Iterator
4+
from unittest.mock import Mock
5+
6+
import pytest
7+
from starlette.testclient import TestClient
8+
9+
from stac_fastapi.api.app import StacApi
10+
from stac_fastapi.extensions.bulk_transactions import (
11+
AsyncBaseBulkTransactionsClient,
12+
BaseBulkTransactionsClient,
13+
BulkTransactionExtension,
14+
BulkTransactionMethod,
15+
Items,
16+
)
17+
from stac_fastapi.types.config import ApiSettings
18+
from stac_fastapi.types.core import BaseCoreClient
19+
20+
# --- UNIT TESTS ---
21+
22+
23+
def test_items_model_iteration():
24+
"""Test the Items pydantic model and its custom iterator."""
25+
item_data = {
26+
"id1": {"type": "Feature", "id": "id1"},
27+
"id2": {"type": "Feature", "id": "id2"},
28+
}
29+
bulk_items = Items(items=item_data, method=BulkTransactionMethod.INSERT)
30+
31+
# Test iteration returns values, not keys
32+
items_list = list(bulk_items)
33+
assert len(items_list) == 2
34+
assert {"type": "Feature", "id": "id1"} in items_list
35+
assert bulk_items.method == "insert"
36+
37+
38+
def test_base_client_chunks():
39+
"""Test the static _chunks method on BaseBulkTransactionsClient."""
40+
test_list = [1, 2, 3, 4, 5, 6, 7]
41+
42+
# Chunk by 3
43+
chunks = list(BaseBulkTransactionsClient._chunks(test_list, 3))
44+
assert chunks == [[1, 2, 3], [4, 5, 6], [7]]
45+
46+
# Chunk by larger than list
47+
chunks_large = list(BaseBulkTransactionsClient._chunks(test_list, 10))
48+
assert chunks_large == [[1, 2, 3, 4, 5, 6, 7]]
49+
50+
51+
def test_bulk_transaction_extension_defaults():
52+
"""Test the default instantiation of the BulkTransactionExtension."""
53+
mock_client = Mock(spec=AsyncBaseBulkTransactionsClient)
54+
ext = BulkTransactionExtension(client=mock_client)
55+
56+
assert ext.client == mock_client
57+
assert ext.schema_href is None
58+
assert ext.conformance_classes == []
59+
60+
61+
def test_bulk_transaction_extension_customization():
62+
"""Test instantiating BulkTransactionExtension with custom arguments."""
63+
mock_client = Mock(spec=AsyncBaseBulkTransactionsClient)
64+
custom_conformance = ["https://example.com/bulk-spec"]
65+
custom_schema = "https://example.com/bulk-schema.json"
66+
67+
ext = BulkTransactionExtension(
68+
client=mock_client,
69+
conformance_classes=custom_conformance,
70+
schema_href=custom_schema,
71+
)
72+
73+
assert ext.conformance_classes == custom_conformance
74+
assert ext.schema_href == custom_schema
75+
76+
77+
# --- INTEGRATION TESTS ---
78+
79+
80+
class DummyCoreClient(BaseCoreClient):
81+
"""Dummy core client for routing purposes."""
82+
83+
def all_collections(self, *args, **kwargs):
84+
raise NotImplementedError
85+
86+
def get_collection(self, *args, **kwargs):
87+
raise NotImplementedError
88+
89+
def get_item(self, *args, **kwargs):
90+
raise NotImplementedError
91+
92+
def get_search(self, *args, **kwargs):
93+
raise NotImplementedError
94+
95+
def post_search(self, *args, **kwargs):
96+
raise NotImplementedError
97+
98+
def item_collection(self, *args, **kwargs):
99+
raise NotImplementedError
100+
101+
102+
class DummyBulkTransactionsClient(AsyncBaseBulkTransactionsClient):
103+
"""Dummy client returning a success message to verify routing and parsing."""
104+
105+
async def bulk_item_insert(self, items: Items, **kwargs) -> str:
106+
"""Mock bulk insert that just returns a string confirming the count."""
107+
count = len(list(items))
108+
method = items.method.value
109+
return f"Successfully processed {count} items via {method}."
110+
111+
112+
@pytest.fixture
113+
def core_client() -> DummyCoreClient:
114+
return DummyCoreClient()
115+
116+
117+
@pytest.fixture
118+
def bulk_transactions_client() -> DummyBulkTransactionsClient:
119+
return DummyBulkTransactionsClient()
120+
121+
122+
@pytest.fixture
123+
def client(
124+
core_client: DummyCoreClient, bulk_transactions_client: DummyBulkTransactionsClient
125+
) -> Iterator[TestClient]:
126+
"""Fixture to set up the TestClient with the BulkTransactionExtension."""
127+
settings = ApiSettings()
128+
api = StacApi(
129+
settings=settings,
130+
client=core_client,
131+
extensions=[
132+
BulkTransactionExtension(client=bulk_transactions_client),
133+
],
134+
)
135+
with TestClient(api.app) as client:
136+
yield client
137+
138+
139+
@pytest.fixture
140+
def item() -> dict:
141+
"""A standard STAC Item dictionary."""
142+
return {
143+
"type": "Feature",
144+
"stac_version": "1.0.0",
145+
"stac_extensions": [],
146+
"id": "test_item",
147+
"geometry": {"type": "Point", "coordinates": [-105, 40]},
148+
"bbox": [-105, 40, -105, 40],
149+
"properties": {"datetime": "2020-06-13T13:00:00Z"},
150+
"links": [],
151+
"assets": {},
152+
"collection": "a-collection",
153+
}
154+
155+
156+
def test_bulk_item_insert(client: TestClient, item: dict) -> None:
157+
"""Test the bulk insert endpoint processes items correctly."""
158+
payload = {
159+
"items": {item["id"]: item, "test_item_2": {**item, "id": "test_item_2"}},
160+
"method": "insert",
161+
}
162+
163+
response = client.post("/collections/a-collection/bulk_items", json=payload)
164+
165+
assert response.is_success, response.text
166+
assert response.json() == "Successfully processed 2 items via insert."
167+
168+
169+
def test_bulk_item_upsert(client: TestClient, item: dict) -> None:
170+
"""Test the bulk insert endpoint properly passes the 'upsert' method."""
171+
payload = {"items": {item["id"]: item}, "method": "upsert"}
172+
173+
response = client.post("/collections/a-collection/bulk_items", json=payload)
174+
175+
assert response.is_success, response.text
176+
assert response.json() == "Successfully processed 1 items via upsert."
177+
178+
179+
def test_bulk_item_invalid_method(client: TestClient, item: dict) -> None:
180+
"""Test that pydantic catches invalid bulk transaction methods."""
181+
payload = {"items": {item["id"]: item}, "method": "invalid_method"}
182+
183+
response = client.post("/collections/a-collection/bulk_items", json=payload)
184+
185+
assert response.status_code == 400

0 commit comments

Comments
 (0)