test(e2e): isolate runs and identities without a new backend endpoint (WP-74)
The three specs shared one mutable backend and said so in their own
comments ("Restart the backend between CI runs"). WP-70 recorded the fix as
a dev-only seed endpoint; it isn't needed. The DB path already routes
through IConfiguration, so playwright.config's webServer hands the backend a
throwaway SQLite file per invocation — the same trick TestWebApplicationFactory
already uses, with zero backend change. And StubIdentityProvider already
honoured X-Subject; the only gap was that nothing sent it. That matters
because the backend has no IsDevelopment() gate anywhere, so a seed endpoint
would have had to invent the codebase's first environment gate.
subjectInterceptor mirrors the existing roleInterceptor and is wired into the
same isDevMode()-only list. Interceptors alone were not enough: the raw XHR
upload and the hand-written letter-preview fetch bypass Angular's chain (as
CLAUDE.md documents), so both now stamp X-Subject explicitly — without that,
every uploaded document still landed under DemoOwner.
reuseExistingServer stays on: flipping it would break local runs for anyone
already serving the docker stack. Each run gets a unique DB filename and
global-setup sweeps only prior runs' leftovers — deleting a fixed path
mid-run risks SQLite silently recreating an empty, unmigrated file under
fullyParallel.
Verified: e2e passes twice back-to-back with no backend restart, and
X-Subject was observed on a real request, not merely wired.
brief-v2.spec.ts keeps the shared identity for now — see the KNOWN GAP note;
a backend staleness bug makes /brief/preview return a sent letter with the
draft watermark for any non-DemoOwner BSN. actors.ts reserves the actor for
whoever fixes it.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,15 +1,48 @@
|
||||
import { defineConfig } from '@playwright/test';
|
||||
import * as os from 'node:os';
|
||||
import * as path from 'node:path';
|
||||
|
||||
// Smoke-level e2e (WP-19): one happy path, one degraded path, against the REAL
|
||||
// backend — proving the FE+BE seam, not replacing component/unit tests.
|
||||
const baseURL = process.env['E2E_BASE_URL'] ?? 'http://localhost:4200';
|
||||
|
||||
// WP-74: give the backend a THROWAWAY SQLite file per `npm run e2e` invocation
|
||||
// instead of its default `bigregister.db`, so two separate runs never see each
|
||||
// other's leftover Applications/Documents/Briefs (`Program.cs:44-46` already reads
|
||||
// `ConnectionStrings__AppDb` from `IConfiguration` — zero backend change; the same
|
||||
// trick `TestWebApplicationFactory.cs` uses per xUnit test class). `Program.cs:93`
|
||||
// self-migrates a fresh file on startup, so a brand-new path is immediately usable.
|
||||
//
|
||||
// UNIQUE filename per invocation (pid + timestamp), not a single fixed name: a fixed
|
||||
// name that gets deleted WHILE the backend still has it open only stays safe if
|
||||
// Microsoft.Data.Sqlite's connection pool never needs to open a fresh native handle
|
||||
// by path after the delete — under `fullyParallel: true` (several workers hitting
|
||||
// the backend concurrently) a pool miss is a real, if intermittent, risk, and it
|
||||
// would silently recreate an empty, unmigrated file mid-run (every query after that
|
||||
// would 500 on "no such table"). A unique path sidesteps the whole question: nothing
|
||||
// ever deletes the file THIS run is actively using. The tradeoff is litter in the OS
|
||||
// temp dir across many runs, since Playwright has no webServer teardown hook to
|
||||
// delete it once the run ends — `globalSetup` (below) sweeps prior runs' files at
|
||||
// the start of each new run instead. That's safe regardless of ordering: it only
|
||||
// ever touches OTHER runs' paths, whose processes have already exited.
|
||||
const dbPath = path.join(os.tmpdir(), `big-register-e2e-${process.pid}-${Date.now()}.db`);
|
||||
// Shared with `e2e/global-setup.ts`, which runs in this SAME node process (it's
|
||||
// loaded and invoked by the Playwright runner right after `webServer` comes up, not
|
||||
// spawned separately) — so this assignment is visible there without a second import.
|
||||
process.env['E2E_DB_PATH'] = dbPath;
|
||||
|
||||
export default defineConfig({
|
||||
testDir: './e2e',
|
||||
timeout: 30_000,
|
||||
fullyParallel: true,
|
||||
retries: process.env['CI'] ? 1 : 0,
|
||||
reporter: process.env['CI'] ? 'github' : 'list',
|
||||
// Sweeps `big-register-e2e-*.db*` files left behind by EARLIER invocations (never
|
||||
// this run's own `dbPath` — see its comment above). Runs after `webServer` is
|
||||
// already up (Playwright always starts webServer before globalSetup — there's no
|
||||
// config knob to reverse that), which is exactly why it must never touch the
|
||||
// current run's own file.
|
||||
globalSetup: './e2e/global-setup.ts',
|
||||
use: {
|
||||
baseURL,
|
||||
trace: 'on-first-retry',
|
||||
@@ -27,8 +60,22 @@ export default defineConfig({
|
||||
{
|
||||
command: 'dotnet run --project backend/src/BigRegister.Api --urls http://localhost:5000',
|
||||
url: 'http://localhost:5000/swagger',
|
||||
// WP-74 caveat, read before "fixing" this to `false`: this stays `!CI` (reuse
|
||||
// locally) on purpose, matching the FE server below and the docker-stack note
|
||||
// above it — turning reuse off would make `npm run e2e` hard-fail with
|
||||
// "port already in use" for anyone who already has the docker stack (or a
|
||||
// plain `dotnet run`) up on :5000, a real local-workflow regression for a POC
|
||||
// convenience feature. The tradeoff this buys: `env` below (the throwaway DB)
|
||||
// ONLY applies when Playwright itself spawns the process — reusing an
|
||||
// already-running backend silently falls back to THAT process's own DB
|
||||
// (typically the shared dev `bigregister.db`), so the isolation this WP adds
|
||||
// is real in CI (`reuseExistingServer` is always `false` there) and in the
|
||||
// common local case of "nothing was already running on :5000", but not if you
|
||||
// deliberately point e2e at an already-running shared backend — that was
|
||||
// already shared state before this WP and still is.
|
||||
reuseExistingServer: !process.env['CI'],
|
||||
timeout: 180_000,
|
||||
env: { ConnectionStrings__AppDb: `Data Source=${dbPath}` },
|
||||
},
|
||||
{
|
||||
command: 'npm start',
|
||||
|
||||
Reference in New Issue
Block a user