Skip to content

Commit 4953574

Browse files
committed
refactor: update integration tests for consolidated scripts
- Update imports from scripts.register_stac/augment_stac_item to scripts.register - Test upsert_item, add_visualization_links, s3_to_https, rewrite_asset_hrefs functions - Fix datetime to use datetime object instead of string - Fix S1 test to use 'vh' asset key (matches implementation) - All 6 integration tests passing
1 parent 8a30f82 commit 4953574

File tree

1 file changed

+110
-114
lines changed

1 file changed

+110
-114
lines changed

tests/integration/test_stac_api.py

Lines changed: 110 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
These tests verify end-to-end scenarios with mocked external dependencies.
44
"""
55

6-
from unittest.mock import MagicMock, patch
6+
from datetime import UTC, datetime
7+
from unittest.mock import MagicMock
78

89
import pytest
910
from pystac import Asset, Item
@@ -13,144 +14,105 @@
1314
def test_pystac_item():
1415
"""Sample pystac Item for testing."""
1516
item = Item(
16-
id="S2B_test",
17+
id="S2B_MSIL2A_20250101T000000_test",
1718
geometry=None,
1819
bbox=None,
19-
datetime="2025-01-01T00:00:00Z",
20+
datetime=datetime(2025, 1, 1, 0, 0, 0, tzinfo=UTC),
2021
properties={},
2122
collection="sentinel-2-l2a",
2223
)
24+
item.add_asset(
25+
"data",
26+
Asset(
27+
href="s3://bucket/test.zarr",
28+
media_type="application/vnd+zarr",
29+
roles=["data"],
30+
),
31+
)
2332
item.add_asset(
2433
"TCI_10m",
2534
Asset(
26-
href="https://example.com/data.zarr/TCI",
35+
href="s3://bucket/test.zarr/measurements/reflectance/r10m/TCI",
2736
media_type="application/vnd+zarr",
2837
roles=["visual"],
2938
),
3039
)
3140
return item
3241

3342

34-
@pytest.fixture
35-
def test_item_dict():
36-
"""Sample STAC item dictionary for registration tests."""
37-
return {
38-
"type": "Feature",
39-
"stac_version": "1.0.0",
40-
"id": "test-item",
41-
"collection": "test-collection",
42-
"geometry": None,
43-
"bbox": None,
44-
"properties": {"datetime": "2025-01-01T00:00:00Z"},
45-
"assets": {},
46-
"links": [],
47-
}
48-
43+
@pytest.mark.integration
44+
def test_upsert_item_creates_new(test_pystac_item):
45+
"""Test upsert_item creates new STAC item when it doesn't exist."""
46+
from scripts.register import upsert_item
4947

50-
@pytest.fixture
51-
def mock_stac_client():
52-
"""Mock pystac_client.Client for registration tests."""
48+
# Mock client and collection
5349
mock_client = MagicMock()
5450
mock_collection = MagicMock()
5551
mock_client.get_collection.return_value = mock_collection
5652

57-
# Mock the StacApiIO session (httpx client)
58-
mock_session = MagicMock()
59-
mock_stac_io = MagicMock()
60-
mock_stac_io.session = mock_session
61-
mock_stac_io.timeout = 30
62-
mock_client._stac_io = mock_stac_io
63-
64-
return mock_client
65-
66-
67-
@pytest.mark.integration
68-
def test_register_creates_new_item(test_item_dict, mock_stac_client):
69-
"""Test registration creates new STAC item when it doesn't exist."""
70-
from scripts.register_stac import register_item
71-
7253
# Mock item doesn't exist
73-
mock_stac_client.get_collection().get_item.side_effect = Exception("Not found")
54+
mock_collection.get_item.side_effect = Exception("Not found")
7455

7556
# Mock successful POST
7657
mock_response = MagicMock()
7758
mock_response.status_code = 201
7859
mock_response.raise_for_status = MagicMock()
79-
mock_stac_client._stac_io.session.post.return_value = mock_response
60+
mock_stac_io = MagicMock()
61+
mock_stac_io.session = MagicMock()
62+
mock_stac_io.session.post.return_value = mock_response
63+
mock_stac_io.timeout = 30
64+
mock_client._stac_io = mock_stac_io
8065

81-
with patch("pystac_client.Client.open", return_value=mock_stac_client):
82-
register_item(
83-
stac_url="https://stac.example.com",
84-
collection_id="test-collection",
85-
item_dict=test_item_dict,
86-
mode="create-or-skip",
87-
)
66+
upsert_item(mock_client, "test-collection", test_pystac_item)
8867

89-
# Verify POST was called to create item
90-
assert mock_stac_client._stac_io.session.post.called
68+
# Verify POST was called to create item
69+
assert mock_stac_io.session.post.called
9170

9271

9372
@pytest.mark.integration
94-
def test_register_skips_existing_item(test_item_dict, mock_stac_client):
95-
"""Test registration skips when item already exists."""
96-
from scripts.register_stac import register_item
97-
98-
# Mock item exists
99-
mock_stac_client.get_collection().get_item.return_value = MagicMock()
73+
def test_upsert_item_updates_existing(test_pystac_item):
74+
"""Test upsert_item updates existing item."""
75+
from scripts.register import upsert_item
10076

101-
with patch("pystac_client.Client.open", return_value=mock_stac_client):
102-
register_item(
103-
stac_url="https://stac.example.com",
104-
collection_id="test-collection",
105-
item_dict=test_item_dict,
106-
mode="create-or-skip",
107-
)
108-
109-
# Verify no POST/PUT/DELETE was called
110-
assert not mock_stac_client._stac_io.session.post.called
111-
assert not mock_stac_client._stac_io.session.put.called
112-
assert not mock_stac_client._stac_io.session.delete.called
113-
114-
115-
@pytest.mark.integration
116-
def test_register_updates_existing_item(test_item_dict, mock_stac_client):
117-
"""Test registration updates existing item in upsert mode."""
118-
from scripts.register_stac import register_item
77+
# Mock client and collection
78+
mock_client = MagicMock()
79+
mock_collection = MagicMock()
80+
mock_client.get_collection.return_value = mock_collection
11981

12082
# Mock item exists
121-
mock_stac_client.get_collection().get_item.return_value = MagicMock()
83+
mock_collection.get_item.return_value = MagicMock()
12284

12385
# Mock successful DELETE and POST
86+
mock_stac_io = MagicMock()
87+
mock_stac_io.session = MagicMock()
88+
mock_stac_io.timeout = 30
89+
12490
mock_delete_response = MagicMock()
12591
mock_delete_response.status_code = 204
12692
mock_delete_response.raise_for_status = MagicMock()
127-
mock_stac_client._stac_io.session.delete.return_value = mock_delete_response
93+
mock_stac_io.session.delete.return_value = mock_delete_response
12894

12995
mock_post_response = MagicMock()
13096
mock_post_response.status_code = 201
13197
mock_post_response.raise_for_status = MagicMock()
132-
mock_stac_client._stac_io.session.post.return_value = mock_post_response
98+
mock_stac_io.session.post.return_value = mock_post_response
13399

134-
with patch("pystac_client.Client.open", return_value=mock_stac_client):
135-
register_item(
136-
stac_url="https://stac.example.com",
137-
collection_id="test-collection",
138-
item_dict=test_item_dict,
139-
mode="upsert",
140-
)
100+
mock_client._stac_io = mock_stac_io
141101

142-
# Verify DELETE then POST was called
143-
assert mock_stac_client._stac_io.session.delete.called
144-
assert mock_stac_client._stac_io.session.post.called
102+
upsert_item(mock_client, "test-collection", test_pystac_item)
103+
104+
# Verify DELETE then POST was called
105+
assert mock_stac_io.session.delete.called
106+
assert mock_stac_io.session.post.called
145107

146108

147109
@pytest.mark.integration
148-
def test_augmentation_adds_visualization_links(test_pystac_item):
149-
"""Test augmentation workflow adds visualization links."""
150-
from scripts.augment_stac_item import add_visualization
110+
def test_add_visualization_links_s2(test_pystac_item):
111+
"""Test add_visualization_links for Sentinel-2."""
112+
from scripts.register import add_visualization_links
151113

152114
# Add visualization links
153-
add_visualization(
115+
add_visualization_links(
154116
item=test_pystac_item,
155117
raster_base="https://titiler.example.com",
156118
collection_id="sentinel-2-l2a",
@@ -159,44 +121,78 @@ def test_augmentation_adds_visualization_links(test_pystac_item):
159121
# Verify XYZ tile link added
160122
xyz_links = [link for link in test_pystac_item.links if link.rel == "xyz"]
161123
assert len(xyz_links) > 0
162-
assert "titiler.example.com" in xyz_links[0].target
124+
assert "titiler.example.com" in xyz_links[0].href
125+
assert "TCI" in xyz_links[0].href or "tci" in xyz_links[0].href.lower()
163126

164127
# Verify viewer link added
165128
viewer_links = [link for link in test_pystac_item.links if link.rel == "viewer"]
166129
assert len(viewer_links) > 0
167130

131+
# Verify EOPF Explorer link added
132+
explorer_links = [link for link in test_pystac_item.links if "explorer" in link.href.lower()]
133+
assert len(explorer_links) > 0
134+
135+
136+
@pytest.mark.integration
137+
def test_add_visualization_links_s1():
138+
"""Test add_visualization_links for Sentinel-1."""
139+
from scripts.register import add_visualization_links
140+
141+
# Create S1 item
142+
item = Item(
143+
id="S1A_IW_GRDH_test",
144+
geometry=None,
145+
bbox=None,
146+
datetime=datetime(2025, 1, 1, 0, 0, 0, tzinfo=UTC),
147+
properties={},
148+
collection="sentinel-1-grd",
149+
)
150+
item.add_asset(
151+
"vh",
152+
Asset(
153+
href="s3://bucket/test.zarr/measurements/VH",
154+
media_type="application/vnd+zarr",
155+
roles=["data"],
156+
),
157+
)
158+
159+
add_visualization_links(
160+
item=item,
161+
raster_base="https://titiler.example.com",
162+
collection_id="sentinel-1-grd",
163+
)
164+
165+
# Verify XYZ link contains VH
166+
xyz_links = [link for link in item.links if link.rel == "xyz"]
167+
assert len(xyz_links) > 0
168+
assert "VH" in xyz_links[0].href or "vh" in xyz_links[0].href.lower()
169+
168170

169171
@pytest.mark.integration
170-
def test_augmentation_adds_projection(test_pystac_item):
171-
"""Test augmentation extracts projection information."""
172-
from scripts.augment_stac_item import add_projection
172+
def test_s3_to_https_conversion():
173+
"""Test S3 URL to HTTPS conversion."""
174+
from scripts.register import s3_to_https
173175

174-
# Mock zarr group with spatial_ref
175-
# Note: This test validates the interface, actual zarr integration tested separately
176-
add_projection(item=test_pystac_item)
176+
result = s3_to_https("s3://bucket/path/file.zarr", "https://s3.example.com")
177177

178-
# Projection properties should be attempted (may not be set without real zarr)
179-
# This validates the function can be called and handles missing data gracefully
180-
assert test_pystac_item.properties is not None
178+
assert result.startswith("https://")
179+
assert "bucket" in result
180+
assert "file.zarr" in result
181181

182182

183183
@pytest.mark.integration
184-
def test_full_augmentation_pipeline(test_pystac_item):
185-
"""Test complete augmentation pipeline."""
186-
from scripts.augment_stac_item import augment
184+
def test_rewrite_asset_hrefs(test_pystac_item):
185+
"""Test asset href rewriting from old to new base."""
186+
from scripts.register import rewrite_asset_hrefs
187187

188-
# Run full augmentation
189-
result = augment(
188+
rewrite_asset_hrefs(
190189
item=test_pystac_item,
191-
raster_base="https://titiler.example.com",
192-
collection_id="sentinel-2-l2a",
193-
verbose=False,
190+
old_base="s3://bucket",
191+
new_base="s3://new-bucket",
192+
s3_endpoint="https://s3.example.com",
194193
)
195194

196-
# Verify item returned
197-
assert result.id == "S2B_test"
198-
199-
# Verify links added
200-
assert len(result.links) > 0
201-
xyz_count = sum(1 for link in result.links if link.rel == "xyz")
202-
assert xyz_count > 0
195+
# Verify assets were rewritten
196+
for asset in test_pystac_item.assets.values():
197+
assert "new-bucket" in asset.href
198+
assert "bucket/" not in asset.href or "new-bucket" in asset.href

0 commit comments

Comments
 (0)