Testing¶
Root task ci runs formatting, Clippy linting, database-free unit tests,
PostgreSQL 18 and RustFS integration tests, and Rust Playwright browser
journeys against the Loco application.
Loco test stages¶
| Command | Coverage |
|---|---|
task test:unit |
Rust library and doc tests, plus JavaScript unit tests; no database |
task test:integration |
API, access control, persistence, identity and queue integration tests |
task test:integration TARGET=care_api FILTER=fixture |
Focused Rust integration test binary and filter |
task test:browser |
Browser journeys (cargo nextest run --test browser) |
task test:browser FILTER=barcode_miss |
Run one browser journey by name substring |
Local integration runs provision disposable PostgreSQL 18 and RustFS containers
through compose.yaml; browser runs provision PostgreSQL only. Both ignore an
ambient DATABASE_URL. The
browser lane is a single job: Playwright is driven by the playwright-rs crate,
which assembles its driver in the playwright-rust cache directory and
Chromium in the usual ms-playwright cache. The driver supplies its own Node
runtime; frontend:prepare still uses Node and npm to build the application
CSS and scanner assets before the journeys run.
Journeys are marked #[ignore] so the shared integration lanes skip them;
task test:browser selects them with nextest --run-ignored only. Both
integration and browser tasks fail when a filter selects no tests.
The Rust browser lane is deliberately smaller than the retired JavaScript suite;
it does not claim test-for-test parity. Each journey chooses its viewport rather
than inheriting an automatic desktop/mobile matrix. Retained journeys now include
native passkeys and account forms, OAuth consent, care/scanner flows,
administration and location-deletion protection. CI uploads captures under
docs/screenshots/browser-journeys/ as the browser-journey-screenshots artifact.
At main 957e57390
on 23 October 2026, CI executed 34 browser journeys successfully, with one
unignored harness unit test excluded by --run-ignored only. The same run passed
153 Rust library tests, 19 JavaScript unit tests and 1,169 tests in the integration lane
(including the 153 library tests; 38 additional tests skipped), plus the separately invoked template-concurrency guard. Counts
describe that run, not universal page, device or live-provider coverage.
The remaining concrete gaps have existing owners: typed browser fixtures, passkey cancellation/reuse browser paths, live provider/passkey acceptance and accessibility. Do not recreate the deleted suite or count API/render assertions as browser proof.
Running cargo test --tests directly requires an owned DATABASE_URL; the
empty medtracker_reference base database is created lazily by
tests/support/template.rs when absent. task test:integration provisions
the owned instance. The ignored template_concurrency guardrail runs automatically
on an unfiltered task test:integration.
Integration fixtures clone a migrated and seeded template for each fixture
family into private databases. Short-lived OAuth fixture credentials are created
for each clone so they cannot expire while waiting in a shared template.
Each browser journey clones the seeded browser_template into a private
database with a restricted runtime role, spawns the compiled med-tracker
server on a dynamic port, and drops the database, role and server process on
completion — including failure paths. Storage runtimes get private buckets.
Browser journeys¶
Browser journeys live in tests/browser/journeys.rs as ordinary
#[tokio::test]s marked #[ignore] so the shared --tests lanes skip them.
Each journey supplies an explicit Viewport to harness::journey(...), which clones the seeded
browser_template into a private database, spawns the compiled
med-tracker server, and drops everything on exit.
- Choose
task test:integrationfor HTTP-level behaviour andtask test:browserfor a real browser driving the UI. - To add a retained journey, write a test in
journeys.rs, sign in withharness::sign_in(&journey), drive pages with semantic locators (get_by_role/get_by_label) andexpectassertions, and mark it#[ignore]like the others. - Fixture data comes from SeaORM entity seeds in
tests/support/fixtures.rs; add a seeding helper there rather than raw SQL. Template-level rows go throughharness'spreparecallback; per-journey rows (for exampleseed_barcode_metadata) run inside the journey itself. playwright-rsassembles its driver at build time andinstall_browsersfetches Chromium on first run; both land in the user cache and need no separate Node install for Playwright. Application assets still use Node and npm throughfrontend:prepare.- On failure the harness prints the tail of the server log; Playwright traces can be enabled per journey when more detail is needed.
Loco database setup¶
Start with an empty PostgreSQL 18 database. The setup connection must be able
to create the required non-login roles and standard citext, pg_trgm and
pgcrypto extensions, assign ownership and assume med_tracker_owner.
Keep these credentials separate from the restricted application login.
| Command | Result |
|---|---|
task db:migrate MIGRATION_DATABASE_URL=... |
Complete schema, including functions, triggers, RLS and grants |
task db:prepare SEED_DATABASE_URL=... |
Migrations followed by development fixtures |
task db:seed SEED_DATABASE_URL=... LOCO_ENV=test |
Migrations followed by test fixtures |
Loco owns schema installation through versioned SeaORM migrations. Ordinary tables, indexes and foreign keys use builders; PostgreSQL-specific definitions are bundled with their migration. The seed hook invokes the migrator once before loading data.
Repeated migration is a no-op. Synthetic seeding refuses production environments and populated application databases. Reset and down commands remain disabled; create another empty database for a fresh start.
TDD workflow¶
MedTracker follows Red-Green-Refactor:
- Write a failing test first.
- Make the smallest change that passes.
- Refactor while keeping tests green.
For a manual screen-reader and keyboard pass over browser journeys, use the manual accessibility smoke-test checklist.
Test fixture users use the password password.