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.
47 lines
1.7 KiB
TypeScript
47 lines
1.7 KiB
TypeScript
/**
|
|
* Runs once, in its own process, before any spec: confirm every shard's
|
|
* containerized half is answering, then clear its gateway mock.
|
|
*
|
|
* Seeding is NOT here any more. The order is load-bearing —
|
|
* `seed-users.sql` names its prerequisites as "created by the API's always-on
|
|
* boot seeders" (iam.organizations / units / positions) — and the freight app
|
|
* now boots inside the vitest *workers*, which this process cannot reach. So the
|
|
* template database is seeded once by `scripts/prepare-shards.mjs` (boot the app
|
|
* → boot seeders → SQL fixtures) and each shard is a clone of it.
|
|
*/
|
|
const SHARDS = Number(process.env.IT_SHARDS ?? 1);
|
|
const PAYMENT_BASE = Number(process.env.IT_PAYMENT_PORT_BASE ?? 3131);
|
|
const GATEWAY_BASE = Number(process.env.IT_GATEWAY_PORT_BASE ?? 4600);
|
|
|
|
async function waitFor(label: string, url: string, attempts = 60): Promise<void> {
|
|
for (let i = 0; i < attempts; i++) {
|
|
try {
|
|
const res = await fetch(url);
|
|
if (res.ok) return;
|
|
} catch {
|
|
/* not up yet */
|
|
}
|
|
await new Promise((r) => setTimeout(r, 2000));
|
|
}
|
|
throw new Error(`${label} never became healthy at ${url}`);
|
|
}
|
|
|
|
export async function setup(): Promise<void> {
|
|
const shards = Array.from({ length: SHARDS }, (_, i) => i);
|
|
|
|
await Promise.all(
|
|
shards.flatMap((i) => [
|
|
waitFor(`payment-api (shard ${i})`, `http://localhost:${PAYMENT_BASE + i}/health`),
|
|
waitFor(`gateway-mock (shard ${i})`, `http://localhost:${GATEWAY_BASE + i}/__control/health`),
|
|
]),
|
|
);
|
|
|
|
await Promise.all(
|
|
shards.map((i) =>
|
|
fetch(`http://localhost:${GATEWAY_BASE + i}/__control/reset`, { method: "POST" }),
|
|
),
|
|
);
|
|
|
|
console.log(`it: ${SHARDS} shard(s) ready`);
|
|
}
|