1818from vgi_rpc .external import ClientExternalConfig , Compression , ExternalRef
1919
2020from vgi ._test_fixtures .catalog_contents import CATALOG_PROBE , ContentsProbeCatalog , ContentsProbeWorker
21- from vgi .catalog import SchemaInfo
21+ from vgi .catalog import AttachOpaqueData , SchemaInfo
2222from vgi .catalog .contents_codec import decode_collection , decode_inventory , encode_collection
2323from vgi .catalog .snapshot import (
2424 CatalogSnapshotManager ,
@@ -103,6 +103,11 @@ def _policy(repository: Any, **kwargs: Any) -> CatalogSnapshotPolicy:
103103 )
104104
105105
106+ def _close_policy (policy : CatalogSnapshotPolicy ) -> None :
107+ assert policy .fetch_config is not None
108+ policy .fetch_config .fetch_config .close ()
109+
110+
106111@pytest .mark .parametrize ("compression" , [None , Compression (algorithm = "gzip" ), Compression ()])
107112def test_recovery_and_warm_hits_skip_inventory_and_upload (storage : _Storage , compression : Compression | None ) -> None :
108113 """Restart recovery does one validated GET; subsequent hits do no catalog work."""
@@ -116,15 +121,16 @@ def test_recovery_and_warm_hits_skip_inventory_and_upload(storage: _Storage, com
116121 assert isinstance (ref , ExternalRef )
117122 assert first .statistics == {"builds" : 1 , "uploads" : 1 , "fetches" : 1 }
118123 assert first .resolve (request , lambda : pytest .fail ("warm build" )) == ref
119- assert first .resolve (request , lambda : pytest .fail ("conditional build" ), "generation-1" ).not_modified
124+ unchanged = first .resolve (request , lambda : pytest .fail ("conditional build" ), "generation-1" )
125+ assert isinstance (unchanged , CatalogContentsResponse ) and unchanged .not_modified
120126 assert storage .uploads == 1 and storage .gets == 1
121127 recovered = CatalogSnapshotManager (policy )
122128 assert recovered .resolve (request , lambda : pytest .fail ("recovery build" )) == ref
123129 assert recovered .statistics == {"builds" : 0 , "uploads" : 0 , "fetches" : 1 }
124130 assert recovered .resolve (request , lambda : pytest .fail ("second recovery build" )) == ref
125131 assert storage .uploads == 1 and storage .gets == 2
126132 finally :
127- policy . fetch_config . fetch_config . close ( )
133+ _close_policy ( policy )
128134
129135
130136def test_scopes_singleflight_and_conditional_invalidation (storage : _Storage ) -> None :
@@ -150,7 +156,7 @@ def test_scopes_singleflight_and_conditional_invalidation(storage: _Storage) ->
150156 manager .resolve (request , lambda : response )
151157 assert storage .uploads == 3
152158 finally :
153- policy . fetch_config . fetch_config . close ( )
159+ _close_policy ( policy )
154160
155161
156162def test_singleflight_allows_independent_keys_to_build_concurrently (storage : _Storage ) -> None :
@@ -174,7 +180,7 @@ def build() -> CatalogContentsResponse:
174180 assert all (isinstance (future .result (), ExternalRef ) for future in futures )
175181 assert manager .statistics == {"builds" : 2 , "uploads" : 2 , "fetches" : 2 }
176182 finally :
177- policy . fetch_config . fetch_config . close ( )
183+ _close_policy ( policy )
178184
179185
180186def test_failed_readback_never_commits_and_caps_prevent_upload (storage : _Storage ) -> None :
@@ -195,7 +201,7 @@ def test_failed_readback_never_commits_and_caps_prevent_upload(storage: _Storage
195201 limited .resolve (request , lambda : response )
196202 assert storage .uploads == 1
197203 finally :
198- policy . fetch_config . fetch_config . close ( )
204+ _close_policy ( policy )
199205
200206
201207def test_import_validates_nested_catalog_and_contract (storage : _Storage ) -> None :
@@ -217,7 +223,7 @@ def test_import_validates_nested_catalog_and_contract(storage: _Storage) -> None
217223 manager .resolve (other , lambda : malformed )
218224 assert storage .uploads == 1
219225 finally :
220- policy . fetch_config . fetch_config . close ( )
226+ _close_policy ( policy )
221227
222228
223229def test_cheap_validator_avoids_all_publication_work (storage : _Storage ) -> None :
@@ -227,10 +233,10 @@ def test_cheap_validator_avoids_all_publication_work(storage: _Storage) -> None:
227233 manager = CatalogSnapshotManager (policy )
228234 try :
229235 result = manager .resolve (_request (), lambda : pytest .fail ("build" ), "fresh" )
230- assert result .not_modified
236+ assert isinstance ( result , CatalogContentsResponse ) and result .not_modified
231237 assert manager .statistics == {"builds" : 0 , "uploads" : 0 , "fetches" : 0 }
232238 finally :
233- policy . fetch_config . fetch_config . close ( )
239+ _close_policy ( policy )
234240
235241
236242def test_worker_auth_precedes_cache_and_fallback_is_explicit (
@@ -263,7 +269,7 @@ def test_worker_auth_precedes_cache_and_fallback_is_explicit(
263269 assert isinstance (response , CatalogContentsResponse )
264270 assert decode_inventory (response )
265271 finally :
266- policy . fetch_config . fetch_config . close ( )
272+ _close_policy ( policy )
267273
268274
269275def test_worker_fallback_rejects_generation_change (storage : _Storage , monkeypatch : pytest .MonkeyPatch ) -> None :
@@ -294,15 +300,17 @@ def changing_contents(*args: Any, **kwargs: Any) -> Any:
294300 worker .catalog_contents (attach .attach_opaque_data )
295301 assert storage .uploads == 0
296302 finally :
297- policy . fetch_config . fetch_config . close ( )
303+ _close_policy ( policy )
298304
299305
300306def test_collections_consume_multiple_batches_and_reject_wrong_schema () -> None :
301307 """Later rows and batches survive; legacy schema attachment bytes disappear."""
302308 records = [
303- SchemaInfo (path = [str (i )], comment = None , tags = {"x" : str (i )}, attach_opaque_data = b"secret" ) for i in range (7 )
309+ SchemaInfo (path = [str (i )], comment = None , tags = {"x" : str (i )}, attach_opaque_data = AttachOpaqueData (b"secret" ))
310+ for i in range (7 )
304311 ]
305312 encoded = encode_collection (SchemaInfo , records , batch_size = 2 )
313+ assert encoded is not None
306314 assert decode_collection (SchemaInfo , encoded ) == records
307315 assert len (list (pa .ipc .open_stream (encoded ))) == 4
308316 assert b"secret" not in encoded
@@ -337,7 +345,7 @@ def put_object(self, *, Bucket: str, Key: str, Body: bytes, **kwargs: Any) -> No
337345 self .store .objects ["/" + Key ] = (Body , kwargs .get ("ContentEncoding" ))
338346 self .etags [Key ] = str (int (self .etags .get (Key , "0" )) + 1 )
339347
340- def generate_presigned_url (self , operation : str , * , Params : Any , ExpiresIn : int ) -> str :
348+ def generate_presigned_url (self , operation : str , * , Params : dict [ str , str ] , ExpiresIn : int ) -> str :
341349 del operation , ExpiresIn
342350 return self .store .base + "/" + Params ["Key" ]
343351
@@ -364,7 +372,7 @@ def test_durable_s3_recovery_and_conditional_manifest_tombstone(storage: _Storag
364372 assert second .commit_if_absent (manifest ) == manifest
365373 assert second .lookup (key ) == manifest
366374 finally :
367- policy . fetch_config . fetch_config . close ( )
375+ _close_policy ( policy )
368376
369377
370378def test_s3_renewal_is_persisted_and_preserves_a_concurrent_replacement (
@@ -415,7 +423,7 @@ def concurrent_put(**kwargs: Any) -> None:
415423 assert repository .renew (renewed ) is None
416424 assert restarted .lookup (key ) == replacement
417425 finally :
418- policy . fetch_config . fetch_config . close ( )
426+ _close_policy ( policy )
419427
420428
421429def test_subprocess_client_resolves_published_catalog () -> None :
@@ -441,6 +449,7 @@ class Entry(ArrowSerializableDataclass):
441449 value : Annotated [str , ArrowType (pa .dictionary (pa .int16 (), pa .string ()))]
442450
443451 encoded = encode_collection (Entry , [Entry ("a" ), Entry ("b" ), Entry ("c" )], batch_size = 1 )
452+ assert encoded is not None
444453 assert decode_collection (Entry , encoded ) == [Entry ("a" ), Entry ("b" ), Entry ("c" )]
445454 with pytest .raises (ValueError , match = "trailing bytes" ):
446455 decode_collection (Entry , encoded + encoded )
@@ -469,7 +478,7 @@ def test_import_clamps_supplied_fetch_limits(storage: _Storage) -> None:
469478 with pytest .raises ((ValueError , RuntimeError ), match = "decompress|bytes|limit" ):
470479 limited .import_publication (request , replace (manifest , raw_size = 999 ))
471480 finally :
472- policy . fetch_config . fetch_config . close ( )
481+ _close_policy ( policy )
473482
474483
475484def test_import_checks_actual_outer_size_digest_and_stream_end (storage : _Storage ) -> None :
@@ -503,7 +512,7 @@ def test_import_checks_actual_outer_size_digest_and_stream_end(storage: _Storage
503512 with pytest .raises (ValueError , match = "trailing bytes" ):
504513 CatalogSnapshotManager (policy ).import_publication (request , invalid )
505514 finally :
506- policy . fetch_config . fetch_config . close ( )
515+ _close_policy ( policy )
507516
508517
509518def test_warm_renewal_rejects_changed_immutable_metadata (storage : _Storage ) -> None :
@@ -527,7 +536,7 @@ def renew(self, manifest: Any) -> Any:
527536 with pytest .raises (ValueError , match = "immutable" ):
528537 manager .resolve (request , lambda : pytest .fail ("renewal must not rebuild" ))
529538 finally :
530- policy . fetch_config . fetch_config . close ( )
539+ _close_policy ( policy )
531540
532541
533542def test_generation_change_does_not_commit (storage : _Storage ) -> None :
@@ -555,7 +564,7 @@ def build() -> CatalogContentsResponse:
555564 assert storage .uploads == 0
556565 assert repository .lookup (key ) is None
557566 finally :
558- policy . fetch_config . fetch_config . close ( )
567+ _close_policy ( policy )
559568
560569
561570@pytest .mark .parametrize ("phase" , ["lookup" , "readback" , "commit" ])
@@ -593,7 +602,7 @@ def change_generation(*args: Any, **kwargs: Any) -> Any:
593602 with pytest .raises (ValueError , match = "generation changed" ):
594603 manager .resolve (request , lambda : response , if_none_match = response .etag )
595604 finally :
596- policy . fetch_config . fetch_config . close ( )
605+ _close_policy ( policy )
597606
598607
599608@pytest .mark .parametrize ("phase" , ["readback" , "commit" ])
@@ -631,4 +640,4 @@ def change_generation(*args: Any, **kwargs: Any) -> Any:
631640 if phase == "readback" :
632641 assert repository .lookup (manifest .cache_key ) is None
633642 finally :
634- policy . fetch_config . fetch_config . close ( )
643+ _close_policy ( policy )
0 commit comments