- 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>
56 lines
3.0 KiB
Markdown
56 lines
3.0 KiB
Markdown
# 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 `engine` fixture — 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.yml` with a `test` profile — profile discovery is non-obvious; modifying the dev file risks breaking the dev stack.
|
|
- A `docker-compose.override.yml` — override files apply automatically to `docker 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-test` API: host port 9002 (offset from dev 9000)
|
|
- `minio-test` console: 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 help` is more ergonomic.
|
|
- `package.json` scripts — wrong tool for a Python/Docker project.
|
|
- `justfile` — not universally installed.
|