mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-07 04:12:04 +00:00
A statement-level hang in the integration suite surfaced only as a 60-second test timeout, because statement_timeout sat at the same 60 s. At 45 s the server cancels first, and its error names the statement and the lock or wait it was stuck on.
99 lines
4.4 KiB
TypeScript
99 lines
4.4 KiB
TypeScript
import { defineConfig } from "vitest/config";
|
||
import { dirname, resolve } from "node:path";
|
||
import { availableParallelism } from "node:os";
|
||
import { fileURLToPath } from "node:url";
|
||
|
||
import { createDurationSequencer } from "./tests/durations/sequencer";
|
||
|
||
const __filename = fileURLToPath(import.meta.url);
|
||
const __dirname = dirname(__filename);
|
||
|
||
function integrationWorkers(): number {
|
||
const pinned = Number.parseInt(
|
||
process.env.HEALTHLOG_INTEGRATION_WORKERS ?? "",
|
||
10,
|
||
);
|
||
if (Number.isInteger(pinned) && pinned > 0) return pinned;
|
||
// Half the cores: the other half runs Postgres and the container runtime.
|
||
return Math.max(1, Math.min(8, Math.floor(availableParallelism() / 2)));
|
||
}
|
||
|
||
// Integration suite — boots a real Postgres in a testcontainer and runs
|
||
// migrations against it. Slow (container boot ~5–15s on a cold pull),
|
||
// so excluded from the default `pnpm test` and triggered explicitly via
|
||
// `pnpm test:integration`.
|
||
export default defineConfig({
|
||
test: {
|
||
globals: true,
|
||
environment: "node",
|
||
// Same host-timezone pin as the unit config — CI runs UTC, so the
|
||
// integration contracts must be read against the same clock.
|
||
env: {
|
||
TZ: "UTC",
|
||
ENCRYPTION_KEY:
|
||
"0000000000000000000000000000000000000000000000000000000000000000",
|
||
ENCRYPTION_KEYS: "",
|
||
ENCRYPTION_ACTIVE_KEY_ID: "",
|
||
API_TOKEN_HMAC_KEY:
|
||
"dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd",
|
||
SESSION_SECRET: "integration-test-session-secret-32-bytes",
|
||
// Deletions erase at once here, the behaviour the suite was written
|
||
// against; the grace window (14 days in production) is pinned by the
|
||
// files that set it themselves (`account-deletion-grace.test.ts`,
|
||
// `offhost-purge-on-deletion.test.ts`).
|
||
ACCOUNT_DELETION_GRACE_DAYS: "0",
|
||
// Below `testTimeout`, so a statement that runs or waits too long is
|
||
// cancelled by the server, whose error names the statement and what it
|
||
// waited on. At the production default of 60 s the test timeout fired
|
||
// first and said only that the test took too long.
|
||
DATABASE_STATEMENT_TIMEOUT_MS: "45000",
|
||
},
|
||
include: ["tests/integration/**/*.test.ts"],
|
||
// Container boot + migration apply is slow; give each test a generous
|
||
// budget and beforeAll/afterAll twice that.
|
||
testTimeout: 60_000,
|
||
hookTimeout: 120_000,
|
||
// Files run in parallel, one database per worker: global setup migrates a
|
||
// template once and each worker clones it on its first file (see
|
||
// `tests/integration/worker-database.ts`). The worker count follows the
|
||
// host unless `HEALTHLOG_INTEGRATION_WORKERS` pins it; CI pins it to what
|
||
// a runner shared with the Postgres container can carry.
|
||
pool: "forks",
|
||
fileParallelism: true,
|
||
maxWorkers: integrationWorkers(),
|
||
// `--shard` deals files out by measured duration (CI runs four shards), and
|
||
// files start longest first so a parallel run does not end on one slow
|
||
// file. Order dependence is surfaced on purpose by the nightly shuffled
|
||
// run (`.github/workflows/test-nightly.yml`), which passes
|
||
// `--sequence.shuffle` and prints the seed it used.
|
||
sequence: {
|
||
sequencer: createDurationSequencer(
|
||
"tests/durations/integration.json",
|
||
0.6,
|
||
),
|
||
},
|
||
// v1.4.37 — switched from `isolate: false` to `isolate: true`.
|
||
// The previous setting reused the module graph across files inside
|
||
// one fork; mock declarations (`vi.mock("@/lib/notifications/...")`,
|
||
// `vi.mock("@parse/node-apn")`) registered in `apns-dispatch.test.ts`
|
||
// and `integration-status.test.ts` lost to whichever earlier file
|
||
// had already pulled the real dispatcher / sender modules into the
|
||
// shared graph, so the in-suite run showed flaky 0-call mocks while
|
||
// each file passed in isolation. Re-isolating module state per file
|
||
// costs ~10 s of import-rebuild on a 56-file run; the Postgres
|
||
// testcontainer + migrations still live in `globalSetup` so the
|
||
// expensive boot cost is unaffected.
|
||
isolate: true,
|
||
// One container for the whole run; tests truncate between them.
|
||
// Point each isolated worker at its own clone before application modules
|
||
// read DATABASE_URL at import time.
|
||
setupFiles: ["./tests/integration/environment-setup.ts"],
|
||
globalSetup: ["./tests/integration/global-setup.ts"],
|
||
},
|
||
resolve: {
|
||
alias: {
|
||
"@": resolve(__dirname, "src"),
|
||
},
|
||
},
|
||
});
|