33These 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
89import pytest
910from pystac import Asset , Item
1314def 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