Skip to content

Commit d13ef26

Browse files
test: finish moving the dev and test setup from MinIO to SeaweedFS
- docker-compose: replace the minio service with s3 (SeaweedFS 4.48, the image the fixture uses); the old minio/minio image no longer pulls - rename the requires_minio marker to requires_s3 and the minio_client fixture to s3_client - default S3_ENDPOINT for external containers is localhost:8333 - drop the unused minio extra from testcontainers; relock pixi - update CONTRIBUTING.md Refs #1560
1 parent e3d2a55 commit d13ef26

8 files changed

Lines changed: 53 additions & 234 deletions

File tree

‎CONTRIBUTING.md‎

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ pytest tests/
3636

3737
## Running Tests
3838

39-
Tests use [testcontainers](https://testcontainers.com/) to automatically manage MySQL, PostgreSQL, and MinIO containers. No manual `docker-compose up` required.
39+
Tests use [testcontainers](https://testcontainers.com/) to automatically manage MySQL, PostgreSQL, and S3 ([SeaweedFS](https://github.com/seaweedfs/seaweedfs)) containers. No manual `docker-compose up` required.
4040

4141
Integration tests are **backend-parameterized** — tests using the `backend` fixture run automatically against both MySQL and PostgreSQL.
4242

@@ -67,13 +67,13 @@ Tests automatically spin up both MySQL and PostgreSQL containers via testcontain
6767
### External Containers (for debugging)
6868

6969
```bash
70-
# MySQL + MinIO
71-
docker compose up -d db minio
70+
# MySQL + S3
71+
docker compose up -d db s3
7272
DJ_USE_EXTERNAL_CONTAINERS=1 pixi run -e test test
7373
docker compose down
7474

75-
# MySQL + PostgreSQL + MinIO
76-
docker compose up -d db postgres minio
75+
# MySQL + PostgreSQL + S3
76+
docker compose up -d db postgres s3
7777
DJ_USE_EXTERNAL_CONTAINERS=1 pixi run -e test test
7878
docker compose down
7979
```
@@ -133,7 +133,7 @@ For `DJ_USE_EXTERNAL_CONTAINERS=1`:
133133

134134
| Variable | Default | Description |
135135
|----------|---------|-------------|
136-
| `S3_ENDPOINT` | `localhost:9000` | MinIO endpoint |
136+
| `S3_ENDPOINT` | `localhost:8333` | S3 endpoint |
137137

138138
---
139139

‎docker-compose.yaml‎

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
# Development environment with MySQL and MinIO services
1+
# Development environment with MySQL, PostgreSQL, and S3 (SeaweedFS) services
22
#
33
# NOTE: docker-compose is OPTIONAL for running tests.
44
# Tests use testcontainers to automatically manage containers.
55
# Just run: pytest tests/
66
#
77
# Use docker-compose for development/debugging when you want
88
# persistent containers that survive test runs:
9-
# docker compose up -d db minio # Start services manually
9+
# docker compose up -d db s3 # Start services manually
1010
# pytest tests/ # Tests will use these containers
1111
#
1212
# Full Docker testing (CI):
@@ -37,20 +37,17 @@ services:
3737
timeout: 30s
3838
retries: 5
3939
interval: 15s
40-
minio:
41-
image: minio/minio:${MINIO_VER:-RELEASE.2025-02-28T09-55-16Z}
40+
s3:
41+
# Same image as the test fixture in tests/conftest.py
42+
image: chrislusf/seaweedfs:${SEAWEEDFS_VER:-4.48}
4243
environment:
43-
- MINIO_ACCESS_KEY=datajoint
44-
- MINIO_SECRET_KEY=datajoint
44+
- AWS_ACCESS_KEY_ID=datajoint
45+
- AWS_SECRET_ACCESS_KEY=datajoint
4546
ports:
46-
- "9000:9000"
47-
command: server --address ":9000" /data
47+
- "8333:8333"
48+
command: server -s3 -ip.bind=0.0.0.0
4849
healthcheck:
49-
test:
50-
- "CMD"
51-
- "curl"
52-
- "--fail"
53-
- "http://localhost:9000/minio/health/live"
50+
test: [ "CMD", "nc", "-z", "localhost", "8333" ]
5451
timeout: 30s
5552
retries: 5
5653
interval: 15s
@@ -67,7 +64,7 @@ services:
6764
condition: service_healthy
6865
postgres:
6966
condition: service_healthy
70-
minio:
67+
s3:
7168
condition: service_healthy
7269
environment:
7370
- DJ_HOST=db
@@ -80,7 +77,7 @@ services:
8077
- DJ_PG_USER=postgres
8178
- DJ_PG_PASS=password
8279
- DJ_PG_PORT=5432
83-
- S3_ENDPOINT=minio:9000
80+
- S3_ENDPOINT=s3:8333
8481
- S3_ACCESS_KEY=datajoint
8582
- S3_SECRET_KEY=datajoint
8683
- S3_BUCKET=datajoint.test

‎pixi.lock‎

Lines changed: 3 additions & 181 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎pyproject.toml‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ test = [
9696
"matplotlib",
9797
"ipython",
9898
"graphviz",
99-
"testcontainers[mysql,minio,postgres]>=4.0",
99+
"testcontainers[mysql,postgres]>=4.0",
100100
"polars>=0.20.0",
101101
"pyarrow>=14.0.0",
102102
]
@@ -117,7 +117,7 @@ test = [
117117
"matplotlib",
118118
"ipython",
119119
"s3fs>=2023.1.0",
120-
"testcontainers[mysql,minio,postgres]>=4.0",
120+
"testcontainers[mysql,postgres]>=4.0",
121121
"psycopg2-binary>=2.9.0",
122122
"polars>=0.20.0",
123123
"pyarrow>=14.0.0",
@@ -245,7 +245,7 @@ ignore-words-list = "rever,numer,astroid"
245245
[tool.pytest.ini_options]
246246
markers = [
247247
"requires_mysql: marks tests as requiring MySQL database (deselect with '-m \"not requires_mysql\"')",
248-
"requires_minio: marks tests as requiring MinIO object storage (deselect with '-m \"not requires_minio\"')",
248+
"requires_s3: marks tests as requiring S3-compatible object storage (deselect with '-m \"not requires_s3\"')",
249249
"mysql: marks tests that run on MySQL backend (select with '-m mysql')",
250250
"postgresql: marks tests that run on PostgreSQL backend (select with '-m postgresql')",
251251
"backend_agnostic: marks tests that should pass on all backends (auto-marked for parameterized tests)",
@@ -293,7 +293,7 @@ test-py315 = { features = ["test", "py315"] }
293293
test = "pytest tests/"
294294
test-cov = "pytest --cov-report term-missing --cov=datajoint tests/"
295295
# Optional: use external containers (docker-compose) instead of testcontainers
296-
services-up = "docker compose up -d db minio"
296+
services-up = "docker compose up -d db s3"
297297
services-down = "docker compose down"
298298
test-external = { cmd = "DJ_USE_EXTERNAL_CONTAINERS=1 pytest tests/", depends-on = ["services-up"] }
299299

‎tests/conftest.py‎

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
44
Tests are organized by their dependencies:
55
- Unit tests: No external dependencies, run with `pytest -m "not requires_mysql"`
6-
- Integration tests: Require MySQL/MinIO, marked with @pytest.mark.requires_mysql
6+
- Integration tests: Require MySQL/S3, marked with @pytest.mark.requires_mysql
77
88
Containers are automatically started via testcontainers when needed.
99
Just run: pytest tests/
@@ -58,9 +58,9 @@ def pytest_collection_modifyitems(config, items):
5858
"db_creds_root",
5959
"db_creds_test",
6060
}
61-
# Tests that use these fixtures require MinIO
62-
minio_fixtures = {
63-
"minio_client",
61+
# Tests that use these fixtures require S3 object storage
62+
s3_fixtures = {
63+
"s3_client",
6464
"s3fs_client",
6565
"s3_creds",
6666
"stores_config",
@@ -83,8 +83,8 @@ def pytest_collection_modifyitems(config, items):
8383
# Auto-add marks based on fixture usage
8484
if fixturenames & mysql_fixtures:
8585
item.add_marker(pytest.mark.requires_mysql)
86-
if fixturenames & minio_fixtures:
87-
item.add_marker(pytest.mark.requires_minio)
86+
if fixturenames & s3_fixtures:
87+
item.add_marker(pytest.mark.requires_s3)
8888

8989
# Auto-mark backend-parameterized tests
9090
if fixturenames & backend_fixtures:
@@ -282,7 +282,7 @@ def s3_creds(s3_container) -> Dict:
282282
else:
283283
# From environment (external container)
284284
return dict(
285-
endpoint=os.environ.get("S3_ENDPOINT", "localhost:9000"),
285+
endpoint=os.environ.get("S3_ENDPOINT", "localhost:8333"),
286286
access_key=os.environ.get("S3_ACCESS_KEY", "datajoint"),
287287
secret_key=os.environ.get("S3_SECRET_KEY", "datajoint"),
288288
bucket=os.environ.get("S3_BUCKET", "datajoint.test"),
@@ -501,7 +501,7 @@ def connection_test(connection_root, prefix, db_creds_test):
501501

502502

503503
# =============================================================================
504-
# S3/MinIO Fixtures
504+
# S3 Fixtures
505505
# =============================================================================
506506

507507

@@ -523,7 +523,7 @@ def stores_config(s3_creds, tmpdir_factory):
523523
bucket=s3_creds.get("bucket", "datajoint-test"),
524524
location="dj/repo",
525525
stage=str(tmpdir_factory.mktemp("repo-s3")),
526-
secure=False, # MinIO runs without SSL in tests
526+
secure=False, # The test S3 server runs without SSL
527527
),
528528
"local": dict(protocol="file", location=str(tmpdir_factory.mktemp("local"))),
529529
"share": dict(
@@ -533,7 +533,7 @@ def stores_config(s3_creds, tmpdir_factory):
533533
secret_key=s3_creds["secret_key"],
534534
bucket=s3_creds.get("bucket", "datajoint-test"),
535535
location="dj/store/repo",
536-
secure=False, # MinIO runs without SSL in tests
536+
secure=False, # The test S3 server runs without SSL
537537
),
538538
}
539539

@@ -580,7 +580,7 @@ def http_client():
580580

581581
@pytest.fixture(scope="session")
582582
def s3fs_client(s3_creds):
583-
"""Initialize s3fs filesystem for MinIO."""
583+
"""Initialize s3fs filesystem for the test S3 server."""
584584
import s3fs
585585

586586
return s3fs.S3FileSystem(
@@ -591,8 +591,8 @@ def s3fs_client(s3_creds):
591591

592592

593593
@pytest.fixture(scope="session")
594-
def minio_client(s3_creds, s3fs_client, teardown=False):
595-
"""S3 filesystem with test bucket created (legacy name for compatibility)."""
594+
def s3_client(s3_creds, s3fs_client, teardown=False):
595+
"""S3 filesystem with the test bucket created."""
596596
bucket = s3_creds["bucket"]
597597

598598
# Create bucket if it doesn't exist

‎tests/integration/test_attach.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from tests.schema_external import Attach
66

77

8-
def test_attach_attributes(schema_ext, minio_client, tmpdir_factory):
8+
def test_attach_attributes(schema_ext, s3_client, tmpdir_factory):
99
"""Test saving files in attachments"""
1010
import datajoint as dj
1111

@@ -46,7 +46,7 @@ def test_attach_attributes(schema_ext, minio_client, tmpdir_factory):
4646
assert p2 == path2[0]
4747

4848

49-
def test_return_string(schema_ext, minio_client, tmpdir_factory):
49+
def test_return_string(schema_ext, s3_client, tmpdir_factory):
5050
"""Test returning string on fetch"""
5151
import datajoint as dj
5252

‎tests/integration/test_codecs.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def test_codec_graph(schema_codec):
7272
c.delete()
7373

7474

75-
def test_codec_chained(schema_codec, minio_client):
75+
def test_codec_chained(schema_codec, s3_client):
7676
"""Test codec chaining (layout -> blob)."""
7777
c = Connectivity()
7878
c.delete()

‎tests/integration/test_npy_codec.py‎

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -257,14 +257,14 @@ def test_codec_validate_rejects_object_dtype(self):
257257

258258

259259
# =============================================================================
260-
# Integration Tests (require database + MinIO)
260+
# Integration Tests (require database + S3)
261261
# =============================================================================
262262

263263

264264
class TestNpyCodecIntegration:
265265
"""Integration tests for NpyCodec with real storage."""
266266

267-
def test_insert_fetch_roundtrip(self, schema_npy, minio_client):
267+
def test_insert_fetch_roundtrip(self, schema_npy, s3_client):
268268
"""Basic insert and fetch should preserve array data."""
269269
rec = Recording()
270270
rec.delete()
@@ -284,7 +284,7 @@ def test_insert_fetch_roundtrip(self, schema_npy, minio_client):
284284

285285
rec.delete()
286286

287-
def test_npy_ref_caching(self, schema_npy, minio_client):
287+
def test_npy_ref_caching(self, schema_npy, s3_client):
288288
"""NpyRef should cache loaded data."""
289289
rec = Recording()
290290
rec.delete()
@@ -304,7 +304,7 @@ def test_npy_ref_caching(self, schema_npy, minio_client):
304304

305305
rec.delete()
306306

307-
def test_npy_ref_array_protocol(self, schema_npy, minio_client):
307+
def test_npy_ref_array_protocol(self, schema_npy, s3_client):
308308
"""NpyRef should work transparently in numpy operations."""
309309
rec = Recording()
310310
rec.delete()
@@ -331,7 +331,7 @@ def test_npy_ref_array_protocol(self, schema_npy, minio_client):
331331

332332
rec.delete()
333333

334-
def test_npy_ref_indexing(self, schema_npy, minio_client):
334+
def test_npy_ref_indexing(self, schema_npy, s3_client):
335335
"""NpyRef should support indexing/slicing."""
336336
rec = Recording()
337337
rec.delete()
@@ -351,7 +351,7 @@ def test_npy_ref_indexing(self, schema_npy, minio_client):
351351

352352
rec.delete()
353353

354-
def test_bulk_fetch_lazy(self, schema_npy, minio_client):
354+
def test_bulk_fetch_lazy(self, schema_npy, s3_client):
355355
"""Fetching via to_dicts should return NpyRefs that are lazy."""
356356
rec = Recording()
357357
rec.delete()
@@ -381,7 +381,7 @@ def test_bulk_fetch_lazy(self, schema_npy, minio_client):
381381

382382
rec.delete()
383383

384-
def test_different_dtypes(self, schema_npy, minio_client):
384+
def test_different_dtypes(self, schema_npy, s3_client):
385385
"""NpyCodec should handle various numpy dtypes."""
386386
rec = Recording()
387387
rec.delete()
@@ -405,7 +405,7 @@ def test_different_dtypes(self, schema_npy, minio_client):
405405

406406
rec.delete()
407407

408-
def test_multidimensional_arrays(self, schema_npy, minio_client):
408+
def test_multidimensional_arrays(self, schema_npy, s3_client):
409409
"""NpyCodec should handle various array shapes."""
410410
rec = Recording()
411411
rec.delete()
@@ -430,7 +430,7 @@ def test_multidimensional_arrays(self, schema_npy, minio_client):
430430

431431
rec.delete()
432432

433-
def test_schema_addressed_path(self, schema_npy, minio_client):
433+
def test_schema_addressed_path(self, schema_npy, s3_client):
434434
"""NpyCodec should store files with .npy extension."""
435435
rec = Recording()
436436
rec.delete()
@@ -453,7 +453,7 @@ def test_schema_addressed_path(self, schema_npy, minio_client):
453453
class TestNpyCodecEdgeCases:
454454
"""Edge case tests for NpyCodec."""
455455

456-
def test_empty_array(self, schema_npy, minio_client):
456+
def test_empty_array(self, schema_npy, s3_client):
457457
"""NpyCodec should handle empty arrays."""
458458
rec = Recording()
459459
rec.delete()
@@ -470,7 +470,7 @@ def test_empty_array(self, schema_npy, minio_client):
470470

471471
rec.delete()
472472

473-
def test_large_array(self, schema_npy, minio_client):
473+
def test_large_array(self, schema_npy, s3_client):
474474
"""NpyCodec should handle large arrays."""
475475
rec = Recording()
476476
rec.delete()
@@ -488,7 +488,7 @@ def test_large_array(self, schema_npy, minio_client):
488488

489489
rec.delete()
490490

491-
def test_structured_array(self, schema_npy, minio_client):
491+
def test_structured_array(self, schema_npy, s3_client):
492492
"""NpyCodec should handle structured arrays."""
493493
rec = Recording()
494494
rec.delete()

0 commit comments

Comments
 (0)