mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
The suite drove a containerized freight API, so every code change needed an image rebuild before a test could see it, and there was no way to attach a debugger. Files also ran strictly in sequence against one shared database, which is the root of the warm-stack gotchas the README documents: stowaway paid bookings climbing back aboard, a short consist on the fifth file. The freight app now boots inside each vitest worker from dist/, and each worker owns a whole shard of the topology - its own database, payment API, gateway mock and broker vhost - so nothing mutable is shared and files run in parallel. Full suite drops from roughly 20 minutes to 196s at 4 shards. - main.ts exports createFreightApp() so the harness applies the same prefix, pipes, filters and interceptors as production instead of replaying them by hand; self-start is guarded by require.main so the Dockerfile CMD still boots - booking-window tick cadence is env-driven (BOOKING_WINDOW_TICK_CRON), */1 in the suite, */10 unchanged in production - prepare-shards.mjs seeds a template database (boot seeders, then the SQL fixtures that depend on them) and clones it per shard; it.mjs re-clones on every run, so each run is hermetic - gateway mock and payment API are generated per shard: the mock keeps modes and orders process-global and 20 of 25 specs reset it in beforeAll, and the inbound CBE bill query has to reach one specific shard's app - poll() samples every 250ms instead of 2000ms, keeping the caller's deadline - authz.it.ts seeds its own invoice; it previously read another spec's leftover and returned early, which silently passed on a pristine database Known: an unlocked MAX(sequence_no)+1 in train-scheduling.service.ts races under concurrent allocation and leaves a short consist, so 1-3 specs fail intermittently. Pre-existing and reproduces at the production tick cadence.
35 lines
1.4 KiB
TypeScript
35 lines
1.4 KiB
TypeScript
import { defineConfig } from "vitest/config";
|
|
|
|
/**
|
|
* One worker per shard. A shard owns its whole topology — in-process freight
|
|
* app, database, payment API, gateway mock, broker vhost — so files can run in
|
|
* parallel without sharing anything mutable. See src/app.ts.
|
|
*/
|
|
const SHARDS = Number(process.env.IT_SHARDS ?? 1);
|
|
|
|
export default defineConfig({
|
|
test: {
|
|
include: ["src/**/*.it.ts"],
|
|
globalSetup: ["src/global-setup.ts"],
|
|
pool: "forks",
|
|
// Pinned min = max: VITEST_POOL_ID is the shard key, and a worker that vitest
|
|
// declines to spawn is a shard whose containers are idling.
|
|
poolOptions: { forks: { minForks: SHARDS, maxForks: SHARDS } },
|
|
fileParallelism: SHARDS > 1,
|
|
// The freight app boots ONCE per worker and is memoised in module scope.
|
|
// With isolation on, every file would pay a fresh Nest boot (60 modules,
|
|
// TypeORM, 5 seeders) — 25 boots instead of `SHARDS`.
|
|
isolate: false,
|
|
testTimeout: 180_000,
|
|
hookTimeout: 180_000,
|
|
// Booking/scheduling steps are not idempotent — a retry would assert
|
|
// against a half-advanced booking. (A whole-RUN retry is now safe, because
|
|
// `it.mjs test` re-clones every shard database first.)
|
|
retry: 0,
|
|
// Workers hold a pg pool, three Socket.IO gateways, a RabbitMQ channel and
|
|
// live cron timers; don't wait on those handles to unwind.
|
|
teardownTimeout: 20_000,
|
|
reporters: ["verbose"],
|
|
},
|
|
});
|