Testing Strategy
Purpose and document map
This guide defines which test level owns each kind of evidence, how the suites are composed, and how deterministic data and coverage work. The opening implementation notes describe repository-specific security, authentication, checkout, and tooling evidence; the numbered sections then define shared responsibilities, test levels, execution profiles, coverage, and commands. Completion policy belongs in the Definition of Done, while CI scheduling and artifacts belong in ci.md.
Repository-specific evidence
Supply-chain infrastructure tests live under tests/ci/ and run inside pnpm test:ci. They
exercise the scanner wrappers with valid/invalid fixtures (high/critical versus moderate audit
payloads, narrow versus expired exceptions, clean/leak/broken scanner fixtures, Hadolint clean/dirty
fixtures, and migration drift/setup-failure fixtures) rather than asserting that a YAML step exists.
Secret fixtures use harmless synthetic patterns with redacted expectations; no real secret is
committed. The periodic diagnostics writer is covered with an injected runner so tests never require
network, Docker, or a live registry.
Email verification separates controlled Resend adapter/HTTP transport tests from Mocking Bird interceptor tests. Adapter tests assert serialization, auth, status and malformed response mapping, and cancellation without live delivery. Mock tests prove fail-closed matching, no network, redacted capped history, and synthetic-only previews. HTTP tests exercise the developer route's authentication, CSRF, rate limit, allowlist, and absent variant. Real PostgreSQL integration uses the existing user/session stack while external email remains mocked. No CI test sends live mail.
Console frontend tests cover the disabled registry, lazy developer declaration, response parsing, loading/empty/failure states, scenario and latency input, fixed send, redacted detail rendering, clearing, real-mode warning, and authenticated query cancellation. Backend route variants prove exact OpenAPI surfaces and absence when disabled; HTTP tests cover auth, Origin, CSRF, validation, rate limits, no-store responses, and safe injected captures. Playwright runs the enabled local mock journey in desktop and 360×800 mobile projects, listens for any provider-host request, and verifies scenario changes, rejected/success captures, synthetic preview, clear, navigation, and logout.
Authentication fast tests control the user service and session store to prove HTTP shapes, CSRF, origin, rotation, tampering and throttling without a database or hosted secret. Integration tests use real PostgreSQL and real HTTP/cookie handling for normalized uniqueness, Argon2 credentials, hashed session identifiers, revocation, expiry, disabled users and store failure. The integration database helper applies Drizzle's real journaled migrator. Dedicated tests cover fresh migration, message-only upgrade preserving id 1, and migration rerun.
Account tests also prove strict own-user profile input, persisted nullable preferences, password policy/current-credential failures, atomic all-session revocation, competing password changes, and credential-version rejection of a late session save. Frontend tests cover device/account/null/logout precedence, local drafts, failed-save consistency, translated field feedback, and cache cleanup.
Recovery service tests control randomness, clocks, padding, repository outcomes, template language, and mail acceptance without real sends. Fastify tests prove strict input, generic acknowledgement, Origin/CSRF, IP plus one-way account/token throttles, no-store/referrer headers, safe field policy, and the shared invalid-link outcome. Real PostgreSQL tests cover newest-token supersession, expiry, single use, two-consumer races, concurrent password-state issuance, rollback, password-change invalidation, and all-session revocation. Frontend RTL tests cover fragment removal, no browser storage, field focus, paste/password-manager fields, reveal, success, retry, and restart guidance. Playwright retrieves the link only from an allowlisted synthetic Mocking Bird preview and runs the real browser/backend/PostgreSQL flow for distinct desktop/mobile users; traces and reports do not retain the credential.
Checkout tests use the deterministic fake only; no live provider is ever contacted. Service tests
drive the CheckoutGateway port with stub gateways to prove the neutral request is validated before
any adapter call, that every provider-neutral outcome class passes through unchanged, and that a
thrown, malformed, or out-of-contract adapter result becomes an explicit unknown rather than a
success or a silent definitive failure. Fake-adapter tests assert each configured scenario is
deterministic across repeated calls, that globalThis.fetch is never called, and that the stable
provider reference is derived from the operation identity and issued only for outcomes where an
attempt may have been registered. Composition tests prove the configured scenario is selected, that
disabled yields an explicitly unavailable gateway, and that every hosted environment rejects
CHECKOUT_MODE=fake at startup. tests/ci/checkout-boundary.test.mjs is the static evidence for
the boundary: it scans backend domain/application and frontend sources for provider names, hosts,
and credential vocabulary, asserts the neutral contract imports nothing provider-specific, asserts
the fake stays network-free, and exercises the ESLint rule that forbids importing an adapter from
application or domain code.
Definitive-failure evidence is layered at every level that owns part of the behavior. Application tests prove that a definitive refusal and an unresolved outcome raise different errors and that the classes never cross. Configuration tests prove the optional refusal amounts default to absent, reject non-chargeable or equal values, and never fabricate a refusal when no adapter is composed. API tests prove the refusal's stable code and that the body leaks neither the outcome class nor any provider detail. Real PostgreSQL tests prove the durable half: a declined attempt writes no order, item, finalization, or attempt row, leaves the active queue byte-identical, and leaves today's gross and product counts unchanged across repeated reads before a successful continuation. Component tests prove the browser states the certainties for a definitive refusal while never claiming them for an unresolved one. Browser evidence drives both definitive classes through the real stack and then finalizes successfully in the same session.
Epic 007 refund persistence tests use a dedicated migrated PostgreSQL database. They prove that
legacy/missing payment references, non-commercial and unconfirmed orders are ineligible; concurrent
claims serialize; pending and unknown attempts survive repository recreation and block re-issuance;
definitive refusal permits a numbered retry; and only confirmed success changes the order to
Refunded while appending to, rather than replacing, fulfillment history. Application tests prove
that ineligible requests do not reach the gateway, duplicate requests reuse the durable operation,
and status resolution persists the provider reference before using the status-only gateway port.
HTTP tests prove anonymous and Origin/CSRF-invalid mutations are rejected before application
invocation; API and OpenAPI tests cover safe operation projections and separate issuance, read, and
resolution routes. Active-order and delivered-history read-model integration tests cover
server-computed eligibility, safe latest-operation state, and provider-reference redaction. KDS
component tests prove that the refund action exists only in the detailed menu, requires
confirmation, hides ineligible orders, blocks pending/unknown re-issuance, exposes status-only
resolution, and presents definitive refusal codes. Queue-panel tests hold the mutation response open
to prove the order remains until confirmed success and verify queue/history invalidation.
Order-history component tests cover expanded-detail-only action, confirmed refund and retained
history, ineligible suppression, accessible refunded icon/notice, struck gross total, and the refund
timestamp. History tests also cover delivered and refunded terminal orders, nullable delivery time,
and provider-fact redaction. Surfboard, local adapter, component, and browser behavior is covered in
their corresponding Epic 007 task evidence; no live money-moving provider request is made by
automated tests.
The process-only provisioning entrypoints and terminal input adapter are excluded from V8 source
coverage like the server composition root: target safety and application policy are unit tested,
while command execution is exercised against dedicated migrated databases. This exclusion does not
apply to the fixture-target guard or user/password application logic. Ambient declaration files and
type-only domain/contract modules (src/**/*.d.ts, domain/user.ts,
repository/user-repository.ts, repository/password-recovery-repository.ts,
application/email.ts, application/checkout.ts, diagnostics.ts) are also excluded because they
contain no executable statements; the runtime layers that use them are covered directly by unit and
PostgreSQL integration tests.
1. Responsibilities
Tests follow the architecture. Prefer the lowest practical level and add higher-level tests when they prove interactions that lower levels cannot. Test behavior and failure cases rather than implementation details. Do not duplicate every assertion at every level, replace real integration with mocks, weaken gates, or introduce abstractions solely to make trivial code testable.
The Definition of Done defines applicability and proportional local validation. CI §14 maps every mandatory requirement to commands, workflows, review, and enforcement gaps. Choosing adequate tests, proving acceptance coverage, and judging exclusions remain review responsibilities; a passing suite or 100% coverage alone is insufficient.
Requirements traceability
From Epic 003 onward, stable acceptance scenario IDs connect the human
contract to executable evidence. A proving test includes the exact ID in its title or native
metadata, for example [MVP-KDS-002-S02], and the epic traceability index points back to that test.
Incidental coverage without an ID is useful regression coverage but is not recorded as acceptance
evidence.
Use Playwright primarily for observable operator journeys. Use real-PostgreSQL/application integration tests for durable snapshots, history, concurrency, time boundaries, and invariants that would be artificial through the UI. Unit/component/API, architecture/static, or sourced discovery/manual evidence remains appropriate when the scenario explicitly calls for it. Headline journeys are long demonstrations and do not replace smaller failure/boundary scenarios.
Until a checker is implemented, review and repository search detect duplicate IDs, dangling test references, and scenarios without evidence. Never renumber a committed ID merely to close a gap; update the requirement and traceability explicitly.
2. Existing test levels
Unit and component/module tests
Backend unit tests cover configuration, application service behavior, repository mapping/calls with a fake database, HTTP injection, CORS, health, and error handling. Frontend Vitest/React Testing Library tests cover routes, providers, pages, layout, async states, endpoint operations, and transport/error normalization. Preference tests cover schema validation, storage denial, locale precedence, persistence, and store isolation. Localization tests exercise English fallback, development diagnostics, English/Swedish/Hungarian selection and document metadata, and resource parity. Shell tests cover page registration, semantic current links, temporary drawer close/focus, responsive transitions, build labels, and real appearance controls. The parity test validates shipped resources and deliberate fixtures for missing, extra, empty, and mismatched interpolation keys. They run without a database, Docker, or a real browser.
Error/feedback tests additionally cover safe code and field-issue translation, JSON/no-content transport, cancellation and malformed success responses, loading/empty/error semantics, snackbar replacement/dismissal, invalid-field focus, and unexpected React/route recovery through controlled test collaborators rather than fake production pages. Diagnostics tests parse real Pino JSON, verify request/header/body/error sentinels are absent, reject spoofed correlation values, and assert that frontend reporting contains only its allowlisted category/version/request-ID shape.
Frontend integration tests
frontend/src/App.integration.test.tsx exercises the real route tree, providers, page, query
client, and frontend API boundary with only external HTTP stubbed. It verifies a successful
response, API failure, and network failure through the real frontend layers. These tests run in
jsdom with the ordinary frontend suite. The common renderApp helper invokes the real
AppProviders hierarchy with isolated store, i18n, and query instances; tests may inject controlled
instances or preference state without sharing module singletons.
Database integration tests
backend/src/repository/postgres-message-repository.integration.test.ts exercises the real
repository, Drizzle/postgres.js, and PostgreSQL. It creates, persists, retrieves, and checks
not-found behavior. It uses foodtruck_order_test.
API-level tests
backend/src/app.test.ts tests externally visible HTTP behavior with Fastify injection and
controlled collaborators, including message 200/400/404/500, health, optional CORS, unknown API
routes, malformed/oversized JSON, and allowlisted strict-Zod field issues.
backend/src/app.integration.test.ts sends real HTTP requests over TCP through the real
service/repository to PostgreSQL, using foodtruck_order_api_test. The API tests assert that
success/error headers and error bodies carry the same server-generated request ID.
backend/src/openapi-contract.test.ts checks that public route methods/paths match OpenAPI and that
no explicit HEAD operation is documented. It does not validate every live response against every
OpenAPI schema; API assertions must cover the changed success/error/status/header behavior.
E2E tests
Playwright runs the shared specifications in desktop Chromium and a 360×800 touch-enabled Chromium
project. The whole suite signs in from one host IP, so it shares a single per-IP authentication/CSRF
throttle budget per window; both E2E stacks therefore raise that local-only maximum above the
production-shaped default, which the backend HTTP tests still prove (see
Docker). Every authenticated test acquires CSRF twice — before sign-in
and again after the post-login session rotation — so the maximum must stay above twice the number of
tests, which the CI tooling test enforces. tests/e2e/message.spec.ts verifies the database-backed
message; navigation.spec.ts checks deep links, history, responsive drawer transitions, focus, and
localized settings; auth.spec.ts proves protected direct navigation, browser login, session
refresh, current identity, persisted profile/appearance changes, password rotation with old-password
rejection, logout, and subsequent protection. Authentication/navigation tests provision a unique
account per test and project through the guarded production tooling, then delete that user (with
cascading sessions) in fixture teardown. Credentials are never command arguments or failure
attachments. Backend, database, transport, and frontend are real. Playwright assertions wait for
observable state; no fixed sleeps or test retries are configured. That includes asynchronously
observed signals such as a recorded request counter: a step must wait for the signal rather than
sample it in the same instant a related UI state becomes visible, because a protected query read is
issued in the render that shows the surrounding screen and the two are only milliseconds apart.
Application query retry (retry: 1) is separate from test retry. Playwright prohibits focused tests
in CI; there is no equivalent configured Vitest-wide focused/skipped-test gate. Review must check
skip/only usage.
password-recovery.spec.ts uses fixed allowlisted but per-project synthetic users, obtains links
through the local preview seam, verifies immediate fragment removal, one-time reset, both active
sessions revoked, old/new password behavior, and the same unknown-address acknowledgement.
products.spec.ts provisions its normal isolated authenticated identity and a UUID product directly
through the database fixture boundary. It proves anonymous direct navigation never requests or
reveals catalog data, login restores the protected destination, and the real browser/API/PostgreSQL
path displays every persisted field. The new MVP-PROD-002-S03 journey submits missing, overlong,
and out-of-range product data through the real form/API, checks safe localized field feedback and
first-invalid-field focus, and compares the PostgreSQL catalog before and after rejection. Fixture
teardown removes only the test's product.
The product HTTP and PostgreSQL integration tests cover the authenticated create, update, and
hard-delete paths. They verify the exact integer money/VAT units in responses, catalog reads, and
the database, while the frontend component tests cover conversion, label trimming, safe local/server
feedback, focus/ARIA behavior, draft-preserving retry, confirmed deletion, duplicate-submit
prevention, and catalog invalidation. products.spec.ts additionally creates both valid products
through the real browser/API/PostgreSQL path and removes only its generated rows. The
MVP-PROD-003-S01 edit evidence covers the authenticated PATCH contract, identity-preserving
PostgreSQL update, future catalog read, safe validation/not-found responses, frontend field mapping,
and a real desktop/mobile browser edit. The MVP-PROD-004-S01 deletion evidence covers the
authenticated DELETE contract, successful and absent-row results, hard deletion from PostgreSQL,
removal from subsequent catalog reads, frontend confirmation/safe failure, and a real desktop/mobile
browser deletion. The same scenario IDs appear in the E2E, API, database, service, and component
evidence; the product form does not duplicate the backend's invalid-field matrix.
order-history.integration.test.ts is the database evidence for durable finalized snapshots and
append-only significant history. It runs against real PostgreSQL and proves the storage guarantees
that no browser journey could establish: MVP-HIST-001-S01 reads the internal ID, classification,
fulfillment metadata, quantities, and product snapshots back through a new repository and
connection pool after the catalog was edited, and brackets finalized_at with database time on both
sides; MVP-HIST-001-S02/S03 run a mixed forward/backward sequence and require the full
chronology with bracketed, non-decreasing database timestamps plus a fresh-connection readback;
MVP-HIST-001-S06 requires the checkout attempt, outcome, charged gross, provider reference, and
recorded time to stay associated with the internal order identity through every transition and
delivery; MVP-HIST-001-S07 requires every fact to survive leaving the active queue; and
MVP-PROD-003-S02/MVP-PROD-004-S02 require that a rename/relabel/reprice/VAT edit and a product
deletion leave the finalized snapshot and history unchanged while later orders still use the new
catalog data. Untagged cases in the same file prove that direct UPDATE/DELETE against the
finalization, transition, item, and checkout-attempt tables is rejected by their triggers, and that
a rejected finalization writes no fact at all. History is deliberately not proven through the
browser: these rows have no operator-visible surface, and order-service.test.ts covers the
application history read and its absent-order result.
kds-order-entry.spec.ts provisions its normal isolated authenticated identity and a UUID product
directly through the database fixture boundary. It proves anonymous direct navigation to the
order-entry route never requests or reveals catalog data, login restores the protected destination,
the already-present empty current order exists together with the absence of any start-draft action,
and every product card is a labelled tap target meeting the 44 CSS-pixel baseline.
pages/kds/order-entry.test.tsx covers the same behavior in the component tests across the
protected loading, catalog, empty-catalog, and safe catalog-failure states. The tap evidence covers
one immediate unit, rapid same-card activations, mixed products, and the accessible quantity/status
feedback. The cart remains component-local and unpersisted. Component and browser journey evidence
use mixed quantities and fractional-krona minor-unit prices to prove the total item count,
quantity-derived line prices, and exact gross total for MVP-ORD-001-S05 and MVP-ORD-001-S06. The
MVP-ORD-001-S07/MVP-ORD-001-S08 component and browser cases exercise decrement, increment,
explicit line removal, cancel/confirm clearing, and recalculated counts and totals. The browser case
also observes all post-login requests during abandonment and proves that clearing issues no mutation
that could finalize, queue, or record a sale. The MVP-ORD-002-S01 through MVP-ORD-002-S06
component and browser cases cover absent optional values, bounded queue/note capture, leading-zero
preservation, both explicit fulfillment choices, metadata retention across catalogue/review
navigation, reset with abandonment, and reuse of one queue display number by a fresh local draft.
MVP-TASK-012 adds real-PostgreSQL evidence that non-commercial finalization commits the queued order
and immutable snapshots atomically, excludes them from the commercial-sales predicate, and never
calls the injected checkout facade. Component and browser evidence prove that success immediately
exposes the next empty cart while failure retains the reviewed draft.
MVP-TASK-013 adds real-PostgreSQL evidence that a confirmed checkout finalizes exactly one
commercial/Queued order, writes its immutable snapshots and the order_checkout_attempts row in
the same transaction, keeps the checkout facts associated with the internal order identity, counts
the order in the commercial-sales aggregate exactly once, and survives later catalog and metadata
edits unchanged. It also proves an unconfirmed outcome commits no order, no sale, and no attempt,
that an unavailable or free selection never reaches a provider, and that a charged order that cannot
be committed is not reported as success. Service unit tests cover the shared validation matrix, the
price-before-charge ordering, the chargeable-range guard, and the operation-identity/reference
association. HTTP tests cover the 201 contract, the 409 conflict with no provider detail in the
body, and the existing validation/internal-error mapping. order-api.test.ts and
pages/kds/order-entry.test.tsx cover the transport and the review action: the request body, the
immediate next empty cart, both actions disabled while a request is in flight, and the retained
draft when checkout does not confirm. kds-sales-reliability.spec.ts proves the same flow through
the real browser, backend, and PostgreSQL with the deterministic fake; its sales query is scoped to
the order under test because the desktop and mobile projects share one E2E database.
MVP-TASK-014 adds real-PostgreSQL evidence that the active queue is ordered by authoritative
queue-entry time rather than by the optional queue number: fixtures are given explicit, deliberately
inverted created_at values and a duplicated number, and the read must still return them oldest
first with every immutable snapshot attached. The same suite proves an absent queue number stays
null rather than gaining a placeholder, that a later catalog edit cannot change what the queue
shows, and that a genuinely empty database reads as an empty list. Service unit tests prove the use
case returns the repository's sequence unchanged and invokes neither pricing, persistence, nor
checkout. HTTP tests cover the 200 contract, the rejection of unexpected query input, the 401
for an unauthenticated read, and the 500 mapping, and auth-http.test.ts proves the read needs a
session but neither a CSRF token nor an exact Origin. order-api.test.ts proves the transport
issues one CSRF-free GET and preserves the server order. pages/kds/queue.test.tsx covers the
rendered slip: contents, quantity, note, and visible state; an omitted number rendering nothing; the
empty and failed queue states; and that a queue failure never removes order entry.
kds-queue-fulfillment.spec.ts proves the same ordering and omission through the real browser,
backend, and PostgreSQL, using fixed timestamps so the fixtures are deterministically the oldest
active orders in the shared E2E database.
MVP-TASK-016 adds unit coverage of orderItemFingerprint in order-fingerprint.test.ts: the
deterministic join of captured labels, snapshot order stability, the quantity-above-one rule, the
exact-fit case, truncation within the presentation width, the dropped dangling separator, the
code-point boundary that keeps an astral character intact, and the empty item list.
pages/kds/presentation.test.tsx covers the presentation: every order past the window keeps its own
compact entry, a numbered later order shows only its number while an unnumbered one shows its
fingerprint, an over-long fingerprint is truncated to the shared width, no entry leaks an internal
order identity, and no entry offers an expansion control. The two previous detailed-window cases now
also assert that the surplus orders appear as compact entries rather than being absent, which is the
behavior this task changes. kds-queue-fulfillment.spec.ts proves both scenarios at 768×1024
through the real stack: its own orders are made the newest so they fall beyond the three-order
tablet window regardless of what the shared database already holds, the expectations are read from
the same authoritative queue API the browser renders, and the per-run labels keep the two browser
projects' fixtures distinguishable from one another.
MVP-TASK-017 covers the busy-queue boundary in pages/kds/queue.test.tsx with 15 active orders
followed by a controlled query-cache update to 16. The case asserts that the visible count changes
with the authoritative array and that the three detailed slips plus every compact entry equal the
full queue, so 15 cannot become a hidden presentation cap. The empty-queue case also pins the
visible zero. kds-responsive-presentation.spec.ts creates 15 deterministic active fixtures at an
explicit 768×1024 viewport, checks each fixture has exactly one representation and scrolls the last
into view, then inserts a sixteenth order. It waits for the production 15-second poll and verifies
that both the count and total number of detailed-plus-compact representations increase by one in
desktop and touch-enabled Chromium. The shared E2E database may contain earlier active orders, so
the expected total is read from the authenticated queue API rather than assuming the test owns the
whole queue.
accessibility.spec.ts uses @axe-core/playwright in the existing desktop Chromium and 360×800
touch projects. It scans observed ready states for Home, login, forgot/reset, protected Settings,
the authenticated order-entry route, open/closed mobile navigation, login/recovery/settings field
validation and its summary, notification, light/dark appearance, and explicitly enabled local
Mocking Bird. The blocking tag set is WCAG 2 A/AA, WCAG 2.1 A/AA, and WCAG 2.2 AA; detected
violations are fixed rather than disabled. Recovery uses an allowlisted synthetic token lifecycle
and strips the fragment before scanning or reporting. The helper contains no application route mocks
or exception list.
navigation.spec.ts complements axe with behavior it cannot establish: 320px reflow, 768×1024
tablet and the 900px drawer transition, 1280×800 desktop, horizontal overflow, page/action and
drawer reachability, Swedish long copy, skip-link and drawer keyboard focus, Escape restoration,
route-heading focus, invalid-field focus, reduced-motion navigation, and representative 44×44
CSS-pixel targets. System appearance resolution remains covered at the theme/provider unit level.
These checks are a baseline, not a WCAG certification; the
manual review protocol owns physical-phone, virtual-keyboard, zoom,
visual-contrast, and screen-reader evidence.
CI artifact contract tests
tests/ci/artifacts.test.mjs uses Node's test runner to check workflow upload names, paths,
retention, conditions, blocking errors, producer setup, and application output preflight. It
executes that preflight against complete/missing/empty fixtures. It does not contact GitHub or prove
a hosted upload succeeded.
tests/ci/documentation-gates.test.mjs exercises locale failure cases, nested Markdown targets and
duplicate headings, generated-site missing/broken fixtures (including raw Markdown fragments), the
tracked-output policy with allowed Drizzle migrations, and positive/negative ESLint boundary
fixtures. It also verifies that quality, documentation generation/publishing, and weekly external
link diagnostics call the canonical scripts. These fixtures test checker behavior; they do not
pretend to prove prose meaning or architectural adequacy.
tests/ci/traceability.test.mjs drives scripts/traceability-check.mjs through valid, duplicate
scenario/journey, dangling test-ID, missing-evidence, planned/unknown/duplicate/unparsable
index-row, and report-determinism fixtures, asserts that only its own file is excluded from the
tracked test-file scan, then asserts the real repository corpus is clean and that
pnpm traceability:check is wired into quality. These fixtures test the mechanical ID and index
rules; they deliberately cannot judge whether a named test proves its scenario.
3. Profiles and deterministic data
The backend profiles share vitest.config.ts options: Node, explicit imports, source alias for
@foodtruck-order/db, local environment setup, and a 120-second bound for database/server lifecycle
hooks and individual database tests. Real journaled migrations and forced database cleanup can
synchronously flush many relation files on constrained PostgreSQL storage; keeping the bound above
30 seconds avoids timing out otherwise healthy integration runs while still preventing hangs.
vitest.config.tsselects non-integration tests only.vitest.integration.config.tsselects*.integration.test.tsonly.vitest.coverage.config.tscombines unit and integration tests for aggregate backend coverage.frontend/vitest.config.tsuses jsdom, RTL cleanup/jest-dom, and selects frontend tests includingApp.integration.test.tsx.
Backend integration files create distinct databases on the configured PostgreSQL instance, apply the real journaled migrations, and drop the databases afterwards. This verifies both migration history and repository/schema interaction; Docker/dev E2E independently verifies the packaged and host deployment paths.
Dev-mode E2E recreates foodtruck_order_e2e, runs pnpm db:migrate and pnpm db:seed, and leaves
that dedicated database for inspection until the next run. Docker-mode E2E uses the stack's
migrated/seeded local application database and starts no servers or state-changing database setup.
Its global setup only preflights the gateway. In both modes, the authentication fixture provisions
and cleans up per-test users in the application database; message-only tests create no users.
Docker-mode E2E reaches the application database only through the stack it is pointed at, so
pnpm validate:docker runs it against its own isolated Compose project rather than the development
stack. Running pnpm e2e:docker by hand against the pnpm dev:start stack therefore does create
and delete fixture users in the development database; use the isolated validation stack when the
local data must stay untouched.
Docker validation's backend lifecycle step is a data-bearing process-restart proof, not a bare
clean-exit check. tests/docker/backend-lifecycle.mjs finalizes two real orders through the
packaged gateway — one confirmed commercial and one non-commercial, then driven out of the active
queue while the other is left mid-fulfillment — stops the backend, asserts a clean SIGTERM exit,
starts it again, and asserts a different container PID and start time so the restart cannot be
confused with page state. It signs in again afterwards, because Compose generates the local signing
secret per start and restart logout is expected, and then re-reads the active queue, today's
commercial figures, and every durable fact row. History has no HTTP read, so those rows are read
with psql inside the Compose PostgreSQL container, and the row counts of all six fact tables are
compared as well: a rendered figure can look correct while rows were appended behind it. This
carries MVP-REL-001-S04, MVP-SALES-001-S09, MVP-HIST-001-S01, and MVP-HIST-001-S07. The
matching database-boundary evidence is order-restart-durability.integration.test.ts, which
rebuilds the same picture through a fresh repository, connection pool, and service graph after
closing every connection the application owns. Neither layer simulates a host crash, power loss,
high-availability failover, or backup/restore; those stay outside the MVP.
Test tooling requires a reachable PostgreSQL user permitted to create/drop databases for integration/dev E2E. Use local/test configuration, never a hosted production or staging connection. Concurrent runs sharing a database name or Compose project can collide; run them serially or isolate their instances/projects.
4. Coverage and execution
The command catalogue below is the implemented test workflow, not a future plan.
4.1 Strict code coverage policy
Vitest's native v8 provider enforces 100% statements, lines, functions, and branches, both globally and per-file, for configured production sources:
frontend/src/**/*.{ts,tsx}.backend/src/**/*.ts.
The frontend uses frontend/vitest.config.ts; the backend aggregate uses
backend/vitest.coverage.config.ts and shared coverageOptions. Any test or threshold failure
exits non-zero. Do not widen exclusions to conceal untested code.
Current justified exclusions are:
- Test files and test harnesses (
frontend/src/test/**,backend/src/test-support/**). - Pure declarations:
frontend/src/vite-env.d.tsand the backend domain/repository contract modules listed explicitly inbackend/vitest.config.tsemit no runtime statements. - Browser/process wiring:
frontend/src/main.tsxandbackend/src/server.ts, exercised by real E2E/Docker execution instead of unit coverage. - Static unbundled assets in
frontend/public/**; they are outsidefrontend/src. - Generated artifacts, migration output, and dependencies, outside the included source scope.
packages/dbdoes not currently have its own coverage threshold.
All included production files are covered, not whole excluded production directories. Unit, component, integration, and API tests contribute collectively; each test layer need not independently achieve 100%. E2E has no source coverage threshold and proves user journeys rather than implementation paths.
Execution environments and commands
Run root commands unless an affected workspace command is explicitly chosen.
| Command | Purpose |
|---|---|
pnpm test |
CI artifact tests followed by application fast tests. |
pnpm test:ci |
Node artifact-contract tests only. |
pnpm test:unit |
Frontend and backend fast tests; no database. |
pnpm test:integration |
Backend repository and real HTTP/PostgreSQL tests. |
pnpm test:all |
Application fast + integration tests; excludes CI tests/E2E. |
pnpm test:coverage |
Frontend then aggregate backend coverage; needs PostgreSQL. |
pnpm e2e |
Chromium with dedicated dev-mode database and servers. |
pnpm e2e:headed |
Dev-mode E2E with a visible browser. |
pnpm e2e:docker |
Chromium against an already running Compose gateway. |
pnpm validate:docker |
Isolated Compose build/start + integration + Docker E2E. |
Affected workspace commands include:
pnpm --filter @foodtruck-order/frontend test
pnpm --filter @foodtruck-order/frontend test:watch
pnpm --filter @foodtruck-order/frontend test:coverage
pnpm --filter @foodtruck-order/backend test:unit
pnpm --filter @foodtruck-order/backend test:watch
pnpm --filter @foodtruck-order/backend test:integration
pnpm --filter @foodtruck-order/backend test:coverage
Frontend/backend tests run on host tooling. Tests are not shipped in runtime application images and no test suite is executed inside those images.
Dev E2E uses backend port 3100 and frontend port 5174 (overrides: E2E_BACKEND_PORT,
E2E_FRONTEND_PORT). Playwright may reuse existing servers outside CI; CI forbids reuse. Vite's
API_PROXY_TARGET targets the E2E backend. Docker E2E uses E2E_GATEWAY_URL, defaulting to
http://localhost:8080.
Install Chromium once after dependency installation:
pnpm --filter @foodtruck-order/e2e exec playwright install chromium
Linux hosts may need browser system dependencies; CI uses install --with-deps chromium. Complete
prerequisites and setup belong in development.md. The exact clean-stack validation
belongs in Docker §6.8.
CI runs fast tests in test, integration and Docker E2E inside validate-docker, and instrumented
application tests again in coverage. Dev-mode E2E and test:all are not additional CI checks. A
passing validate:docker does not require another separate integration/Docker E2E run without a new
change or concern.
5. Future features
Later specifications should add regression/acceptance tests at appropriate levels, including business rules, persistence, HTTP contracts, and important browser journeys. Payment integrations should use a test/mock provider; tests must not initiate real production payments. Authentication, payment, order workflows, or real-time tests are not required before those features are in scope.