mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-10 21:32:18 +00:00
fix(ci): harden test determinism and dependencies
This commit is contained in:
1 parent
9adeb8fc19
commit
3ce6726646
13 files changed
+354
-75
No files matched your search
@@ -0,0 +1,30 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { getPrismaClient } from "./setup";
|
||||
|
||||
const TEST_ENCRYPTION_KEY =
|
||||
"0000000000000000000000000000000000000000000000000000000000000000";
|
||||
const TEST_HMAC_KEY = "integration-test-hmac-key-32-bytes-minimum";
|
||||
const TEST_SESSION_SECRET = "integration-test-session-secret-32-bytes";
|
||||
|
||||
describe("integration worker environment", () => {
|
||||
it(
|
||||
"replaces ambient database and encryption configuration with test values",
|
||||
async () => {
|
||||
expect(process.env.DATABASE_URL).toContain("healthlog_test");
|
||||
expect(process.env.DATABASE_URL).not.toContain("ambient_database");
|
||||
expect(process.env.ENCRYPTION_KEY).toBe(TEST_ENCRYPTION_KEY);
|
||||
expect(process.env.ENCRYPTION_KEYS).toBeUndefined();
|
||||
expect(process.env.ENCRYPTION_ACTIVE_KEY_ID).toBeUndefined();
|
||||
expect(process.env.API_TOKEN_HMAC_KEY).toBe(TEST_HMAC_KEY);
|
||||
expect(process.env.SESSION_SECRET).toBe(TEST_SESSION_SECRET);
|
||||
|
||||
const rows = await getPrismaClient().$queryRaw<
|
||||
Array<{ database_name: string }>
|
||||
>`
|
||||
SELECT current_database() AS database_name
|
||||
`;
|
||||
expect(rows).toEqual([{ database_name: "healthlog_test" }]);
|
||||
},
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,15 @@
|
||||
import { inject } from "vitest";
|
||||
|
||||
declare module "vitest" {
|
||||
export interface ProvidedContext {
|
||||
integrationDatabaseUrl: string;
|
||||
}
|
||||
}
|
||||
|
||||
process.env.DATABASE_URL = inject("integrationDatabaseUrl");
|
||||
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";
|
||||
@@ -18,6 +18,7 @@ import { execSync } from "node:child_process";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import type { TestProject } from "vitest/node";
|
||||
import {
|
||||
PostgreSqlContainer,
|
||||
type StartedPostgreSqlContainer,
|
||||
@@ -29,7 +30,9 @@ const PROJECT_ROOT = resolve(__dirname, "..", "..");
|
||||
|
||||
let container: StartedPostgreSqlContainer | null = null;
|
||||
|
||||
export default async function setup(): Promise<() => Promise<void>> {
|
||||
export default async function setup(
|
||||
project: TestProject,
|
||||
): Promise<() => Promise<void>> {
|
||||
container = await new PostgreSqlContainer("postgres:16-alpine")
|
||||
.withDatabase("healthlog_test")
|
||||
.withUsername("healthlog")
|
||||
@@ -38,10 +41,9 @@ export default async function setup(): Promise<() => Promise<void>> {
|
||||
|
||||
const url = container.getConnectionUri() + "?schema=public&pgbouncer=false";
|
||||
process.env.DATABASE_URL = url;
|
||||
project.provide("integrationDatabaseUrl", url);
|
||||
|
||||
// Provided so child processes (e.g. Prisma's CLI) inherit the URL.
|
||||
// Vitest also forwards process.env to test workers, so the
|
||||
// application Prisma singleton sees this URL when it loads.
|
||||
// The explicit child environment keeps Prisma CLI on the same container.
|
||||
execSync("pnpm db:migrate:deploy", {
|
||||
cwd: PROJECT_ROOT,
|
||||
stdio: "inherit",
|
||||
|
||||
@@ -1,39 +1,11 @@
|
||||
/**
|
||||
* Per-test helpers for the integration suite. The Postgres testcontainer
|
||||
* is started ONCE in `global-setup.ts` and torn down at the end of the
|
||||
* run, so test files only need:
|
||||
*
|
||||
* import { getPrismaClient, truncateAllTables } from "./setup";
|
||||
*
|
||||
* beforeEach(async () => { await truncateAllTables(getPrismaClient()); });
|
||||
*
|
||||
* No beforeAll(startTestDb) / afterAll(stopTestDb) is needed — the
|
||||
* container is alive whenever the test file is loaded, and Vitest's
|
||||
* worker inherits `process.env.DATABASE_URL` from globalSetup so the
|
||||
* application's Prisma singleton (`src/lib/db.ts`) connects to the
|
||||
* testcontainer transparently.
|
||||
* is started once in global-setup.ts and bridged into each worker by
|
||||
* environment-setup.ts before application modules load.
|
||||
*/
|
||||
import { prisma } from "@/lib/db";
|
||||
import type { PrismaClient } from "@/generated/prisma/client";
|
||||
|
||||
// v1.23 — every integration test imports this module. The free-text health-note
|
||||
// columns (mood + measurement) are now AES-256-GCM at rest, so any test that
|
||||
// writes/reads a note needs an encryption key. Crypto reads the key lazily on
|
||||
// first `encrypt()`, so a `??=` default here covers every test without touching
|
||||
// each file (individual tests that set their own key still win — `??=`).
|
||||
process.env.ENCRYPTION_KEY ??=
|
||||
"0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
|
||||
|
||||
// v1.30.32 — session cookies now carry a CSPRNG secret whose HMAC is what
|
||||
// lands in `sessions.token_hash`, so `createSession` needs this key exactly
|
||||
// like token issuance already did. It is a Core-required variable in
|
||||
// `scripts/env-manifest.json` (the app refuses to boot without it), so
|
||||
// defaulting it here matches production rather than papering over a gap.
|
||||
// Same `??=` treatment as the encryption key above: files that set their own
|
||||
// still win.
|
||||
process.env.API_TOKEN_HMAC_KEY ??=
|
||||
"fedcba9876543210fedcba9876543210fedcba9876543210fedcba9876543210";
|
||||
|
||||
/**
|
||||
* The application's Prisma singleton. Tests use this so any code
|
||||
* imported via `await import("@/lib/...")` shares the exact same client
|
||||
|
||||
Reference in new issue
Block a user