Skip to content

Testing

How to run every Metrica test suite locally and how the same suites run in CI. Tests are the gate on every deploy — the deploy job never runs unless test passes first (see Deployment).

The MVP is verified against a strict definition — one camera stream, detect people, track IDs, count IN/OUT, store events, show a live dashboard. Tests exist to protect that path, nothing more.

The backend uses pytest + pytest-asyncio (asyncio_mode = "auto", from backend/pyproject.toml). Tests run against a real Postgres, not SQLite — same engine as local dev and prod.

backend/tests/conftest.py connects to a hardcoded URL:

TEST_URL = postgresql+asyncpg://postgres:postgres@localhost:5433/retailpulse_test

Key fixtures:

  • an autouse _schema fixture drops and recreates all tables around every test (using Base.metadata), so each test starts from a clean schema;
  • the engine uses NullPool — pytest-asyncio uses a fresh event loop per test, and pooled asyncpg connections are bound to the loop that created them;
  • client overrides get_session and the auth signing keys with an in-process ES256 keypair, so tests mint their own valid JWTs (make_token) without hitting Supabase;
  • fixtures for seeded data (seed, tunis_seed), auth headers (auth_headers, superadmin_headers) and the worker key header (worker_headers, matching X-Worker-Key).

You need the test database on localhost:5433. The Docker Postgres container from local dev serves retailpulse; create the retailpulse_test database once:

Terminal window
docker compose -f backend/docker-compose.yml up -d
docker compose -f backend/docker-compose.yml exec db \
createdb -U postgres retailpulse_test

Then:

Terminal window
cd backend
uv sync
uv run pytest # or: uv run pytest -q

The CV worker has its own suite:

Terminal window
cd worker
uv sync
uv run pytest

Both JS apps run their suite through bun:

Terminal window
cd frontend && bun install && bun run test
cd admin && bun install && bun run test

The landing and docs sites have no test suite — their workflows are deploy-only.

Each app’s workflow runs its tests on every push and PR that touches the app directory, gating the deploy job.

App Workflow Test command Notes
Backend backend.yml uv run pytest -q Postgres 16 service on 5433
Dashboard frontend.yml bun run test
Admin admin.yml bun run test
Landing / Docs landing.yml / docs.yml deploy-only, no test gate

backend.yml’s test job spins up a postgres:16 service container that matches conftest.py’s TEST_URL:

services:
postgres:
image: postgres:16
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: retailpulse_test
ports:
- 5433:5432
options: >-
--health-cmd "pg_isready -U postgres"
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
DATABASE_URL: postgresql+asyncpg://postgres:postgres@localhost:5433/retailpulse_test

The job then runs uv sync and uv run pytest -q. The health-check options make Actions wait until Postgres is ready before the tests start. DATABASE_URL is set at the job level because app_settings.database_url is required at import.