mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-09 21:10:59 +00:00
Global setup migrates one template database and reads what a fresh install holds: its schemas, its tables and the rows the migrations seed. Each Vitest worker clones the template on its first file with CREATE DATABASE ... TEMPLATE and keeps the clone for the rest of the run, so files run in parallel without seeing each other's rows, advisory locks or queue schema. Schemas created at runtime (pg-boss, migration probes) are dropped when the next file starts. The truncate list is the catalogue now, every table in public except the migration bookkeeping, instead of a hand-kept list of 42 of 166 tables. The seeded catalogues are put back after each truncate in foreign-key order, so a test starts from what a fresh install has. The settings singleton is the one seeded table a test starts without, declared with its reason. next/headers and the boot compatibility probe are mocked once in the setup file. Two lock-wait probes now look at their own database only, since other workers' backends share pg_stat_activity.
91 lines
3.4 KiB
TypeScript
91 lines
3.4 KiB
TypeScript
import { afterEach, inject, vi } from "vitest";
|
|
|
|
import { prepareWorkerDatabase } from "./worker-database";
|
|
|
|
// Each worker runs against its own clone of the migrated template (see
|
|
// `worker-database.ts`). The URL is final before any application module is
|
|
// imported, so the Prisma singleton is built against the clone.
|
|
process.env.DATABASE_URL = await prepareWorkerDatabase(
|
|
inject("integrationDatabaseUrl"),
|
|
inject("integrationDatabasePlan"),
|
|
);
|
|
process.env.ENCRYPTION_KEY =
|
|
"0000000000000000000000000000000000000000000000000000000000000000";
|
|
delete process.env.ENCRYPTION_KEYS;
|
|
delete process.env.ENCRYPTION_ACTIVE_KEY_ID;
|
|
process.env.API_TOKEN_HMAC_KEY = "integration-test-hmac-key-32-bytes-minimum";
|
|
process.env.SESSION_SECRET = "integration-test-session-secret-32-bytes";
|
|
|
|
/**
|
|
* Leave no background work in flight when an integration test ends — the same
|
|
* contract the unit setup keeps, for the same reason: detached background work
|
|
* logs on its own handle, and a console write that reaches vitest after the
|
|
* worker has begun closing its RPC channel fails the run with
|
|
* `EnvironmentTeardownError: Closing rpc while "onUserConsoleLog" was pending`.
|
|
* This suite has its own config and its own setup file, so the unit hook does
|
|
* not cover it.
|
|
*
|
|
* The import is deferred: this file's whole job is to bridge DATABASE_URL into
|
|
* the worker BEFORE any application module reads it at import time, and a
|
|
* static import here would pull part of the application tree in first.
|
|
*/
|
|
afterEach(async () => {
|
|
const { settleBackgroundTasks } =
|
|
await import("@/lib/logging/background-tasks");
|
|
await settleBackgroundTasks();
|
|
});
|
|
|
|
/**
|
|
* Route handlers read cookies and headers through `next/headers`, which only
|
|
* works inside a Next.js request scope. Every file gets the same stand-in,
|
|
* backed by the shared jars in `mock-next-headers.ts`, and the same no-op for
|
|
* the boot-time compatibility probe. A file that needs different behaviour
|
|
* still declares its own `vi.mock`, which takes precedence over this one.
|
|
*/
|
|
vi.mock("next/headers", async () => {
|
|
const { cookieJar, headerJar } = await import("./mock-next-headers");
|
|
return {
|
|
headers: vi.fn(async () => ({
|
|
get: (name: string) => headerJar.get(name.toLowerCase()) ?? null,
|
|
has: (name: string) => headerJar.has(name.toLowerCase()),
|
|
})),
|
|
cookies: vi.fn(async () => ({
|
|
get: (name: string) => {
|
|
const value = cookieJar.get(name);
|
|
return value ? { name, value } : undefined;
|
|
},
|
|
set: (name: string, value: string) => {
|
|
cookieJar.set(name, value);
|
|
},
|
|
delete: (name: string) => {
|
|
cookieJar.delete(name);
|
|
},
|
|
})),
|
|
};
|
|
});
|
|
|
|
vi.mock("@/lib/db-compat", () => ({
|
|
ensureDbCompatibility: vi.fn().mockResolvedValue(undefined),
|
|
}));
|
|
|
|
/**
|
|
* The same pin the unit setup applies (see `vitest.setup.ts`): the Fitbit
|
|
* suites run before the Web API turndown, whatever the real date, and a test
|
|
* about the turndown flips `isFitbitRetired` itself. The factory imports the
|
|
* module lazily, so nothing of the application loads before DATABASE_URL is
|
|
* bridged above.
|
|
*/
|
|
vi.mock("@/lib/fitbit/retirement", async (importOriginal) => {
|
|
const actual =
|
|
await importOriginal<typeof import("@/lib/fitbit/retirement")>();
|
|
const isFitbitRetired = vi.fn((_now?: Date) => false);
|
|
return {
|
|
...actual,
|
|
isFitbitRetired,
|
|
fitbitRetirement: vi.fn((now?: Date) => ({
|
|
...actual.fitbitRetirement(now),
|
|
ended: isFitbitRetired(now),
|
|
})),
|
|
};
|
|
});
|