- conftest.py: pytest_configure guard rejects non-postgresql+asyncpg:// URLs before any test collects (per constitution §2.5/§5.2 v1.3.0) - docker-compose.test.yml: isolated postgres-test (5433) + minio-test (9002) + api-test runner; one command runs the full suite against real PostgreSQL - Makefile: test-unit and test-integration targets - .env.test.example: documents variables needed to run tests outside Docker - Fix pre-existing test bug: integration tests using client fixture (NoOpAuthProvider) for write operations (upload/delete/patch) now use authed_client with Bearer token — these were never caught because tests never ran against a live stack Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
3.0 KiB
Research: PostgreSQL Integration Test Infrastructure
Decision 1: How to enforce the PostgreSQL dialect in conftest.py
Decision: Add a pytest_configure hook (or a module-level guard in conftest.py) that calls pytest.exit() if the resolved database URL does not start with postgresql+asyncpg://.
Rationale: pytest_configure runs before collection, giving the clearest possible failure signal. A module-level assertion would also work but produces a less readable traceback. pytest.exit() with a human-readable message is the idiomatic approach.
Alternatives considered:
- A custom pytest plugin in a separate file — unnecessary complexity for a one-liner guard.
- Raising an exception in the
enginefixture — runs too late (after collection); developers see confusing fixture errors instead of a clear message.
Decision 2: Separate docker-compose.test.yml vs profiles in docker-compose.yml
Decision: Use a standalone docker-compose.test.yml at the repo root.
Rationale: Docker Compose profiles require the developer to remember --profile test on every command. A separate file is explicit and self-contained. The test file can define its own service names and ports without touching the dev compose file at all.
Alternatives considered:
docker-compose.ymlwith atestprofile — profile discovery is non-obvious; modifying the dev file risks breaking the dev stack.- A
docker-compose.override.yml— override files apply automatically todocker compose up, which is the opposite of what we want for tests.
Decision 3: Port assignments for test services
Decision:
postgres-test: host port 5433 (standard offset from dev 5432)minio-testAPI: host port 9002 (offset from dev 9000)minio-testconsole: host port 9003 (offset from dev 9001)
Rationale: Predictable offsets make it easy to remember. Developers running both stacks simultaneously won't hit port conflicts.
Decision 4: S3 isolation strategy for tests
Decision: The api-test service sets S3_BUCKET_NAME=reactbin-test pointing to the dedicated minio-test instance. The minio-init-test sidecar creates that bucket before tests run.
Rationale: The existing conftest already manages database isolation via create_all / drop_all. MinIO requires bucket pre-creation (same as dev). A dedicated test bucket on a dedicated test MinIO instance gives full isolation. No changes to application storage code are needed.
Decision 5: Makefile vs shell scripts
Decision: A Makefile at the repo root with test-unit and test-integration targets.
Rationale: make is universally available on Linux/macOS developer machines. The targets are short wrappers that document the canonical test invocation. No build logic; just convenience aliases.
Alternatives considered:
- Shell scripts (
scripts/test.sh) — no discoverability;make helpis more ergonomic. package.jsonscripts — wrong tool for a Python/Docker project.justfile— not universally installed.