@@ -95,7 +95,7 @@ def pytest_collection_modifyitems(config, items):
9595
9696
9797# =============================================================================
98- # Container Fixtures - Auto-start MySQL and MinIO via testcontainers
98+ # Container Fixtures - Auto-start MySQL, PostgreSQL, and S3 via testcontainers
9999# =============================================================================
100100
101101# Check if we should use external containers (for CI or manual docker-compose)
@@ -164,36 +164,50 @@ def postgres_container():
164164
165165
166166@pytest .fixture (scope = "session" )
167- def minio_container ():
168- """Start MinIO container for the test session (or use external)."""
167+ def s3_container ():
168+ """Start an S3-compatible object store for the test session (or use external)."""
169169 if USE_EXTERNAL_CONTAINERS :
170170 # Use external container - return None, credentials come from env
171- logger .info ("Using external MinIO container" )
171+ logger .info ("Using external S3 container" )
172172 yield None
173173 return
174174
175- from testcontainers .minio import MinioContainer
176-
177- # MinIO withdrew its public images: `minio/minio` on Docker Hub is gone
178- # entirely (any tag, not just `latest`) and `quay.io/minio/minio` now
179- # requires authentication. Chainguard publishes a maintained build that
180- # needs no credentials and is drop-in for this fixture. Overridable so a
181- # deployment can point at its own mirror without editing tests.
182- container = MinioContainer (
183- image = os .environ .get ("DJ_TEST_MINIO_IMAGE" , "cgr.dev/chainguard/minio:latest" ),
184- access_key = "datajoint" ,
185- secret_key = "datajoint" ,
175+ import time
176+
177+ from testcontainers .core .container import DockerContainer
178+
179+ # SeaweedFS replaces MinIO, which withdrew its public images and archived
180+ # its source (#1560). It matched MinIO on s3fs directory emulation
181+ # (isdir/walk/find over prefixes and zero-byte markers), which GC depends
182+ # on. Overridable so a deployment can point at its own mirror.
183+ container = (
184+ DockerContainer (os .environ .get ("DJ_TEST_S3_IMAGE" , "chrislusf/seaweedfs:4.48" ))
185+ .with_command ("server -s3 -ip.bind=0.0.0.0" )
186+ .with_env ("AWS_ACCESS_KEY_ID" , "datajoint" )
187+ .with_env ("AWS_SECRET_ACCESS_KEY" , "datajoint" )
188+ .with_exposed_ports (8333 )
186189 )
187190 container .start ()
188191
189192 host = container .get_container_host_ip ()
190- port = container .get_exposed_port (9000 )
191- logger .info (f"MinIO container started at { host } :{ port } " )
193+ port = container .get_exposed_port (8333 )
194+ # The S3 gateway starts after the master, volume, and filer servers
195+ deadline = time .monotonic () + 60
196+ while True :
197+ try :
198+ urllib3 .PoolManager ().request ("GET" , f"http://{ host } :{ port } /" , retries = False , timeout = 1 )
199+ break
200+ except urllib3 .exceptions .HTTPError :
201+ if time .monotonic () > deadline :
202+ container .stop ()
203+ raise
204+ time .sleep (0.5 )
205+ logger .info (f"S3 container started at { host } :{ port } " )
192206
193207 yield container
194208
195209 container .stop ()
196- logger .info ("MinIO container stopped" )
210+ logger .info ("S3 container stopped" )
197211
198212
199213# =============================================================================
@@ -253,12 +267,12 @@ def db_creds_test(mysql_container) -> Dict:
253267
254268
255269@pytest .fixture (scope = "session" )
256- def s3_creds (minio_container ) -> Dict :
257- """S3/MinIO credentials from container or environment."""
258- if minio_container is not None :
270+ def s3_creds (s3_container ) -> Dict :
271+ """S3 credentials from container or environment."""
272+ if s3_container is not None :
259273 # From testcontainer
260- host = minio_container .get_container_host_ip ()
261- port = minio_container .get_exposed_port (9000 )
274+ host = s3_container .get_container_host_ip ()
275+ port = s3_container .get_exposed_port (8333 )
262276 return dict (
263277 endpoint = f"{ host } :{ port } " ,
264278 access_key = "datajoint" ,
0 commit comments