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.

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:

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:

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.