Files
edr-platform/e2e-hr-finance/fixtures/db.ts
2026-08-25 00:11:39 +03:00

60 lines
2.2 KiB
TypeScript

import { createRequire } from "node:module";
import * as path from "node:path";
/**
* `pg` is not a dependency of this directory and pnpm's strict layout means a
* bare `import "pg"` does not resolve here. Borrow it from the app that already
* owns a Postgres client rather than adding a duplicate declaration of the same
* driver to the workspace root.
*/
const requireFrom = createRequire(
path.join(__dirname, "..", "..", "apps", "edr-hr-api", "package.json"),
);
const { Client } = requireFrom("pg") as typeof import("pg");
/**
* Direct database reads for assertions.
*
* The suite's doctrine (docs/hr-finance-ui-e2e-matrix.md) is that every scenario
* checks the DOM, the network response AND the database row, and fails if the
* three disagree. A UI that renders the right number from a stale cache, or an
* API that answers 200 without writing, both pass a DOM-only test.
*
* READ-ONLY by intent. Fixtures are created through the product's own endpoints
* or the seed scripts, never here — a test that sets up state with raw SQL stops
* proving the code path that normally creates it.
*/
const config = {
host: process.env.DB_HOST ?? "localhost",
port: Number(process.env.DB_PORT ?? 5432),
user: process.env.DB_USER ?? "postgres",
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME ?? "smart_office_e2e",
};
export async function query<T = Record<string, unknown>>(
sql: string,
params: unknown[] = [],
): Promise<T[]> {
if (config.database === "smart_office_prod") {
// The suite must never read prod as if it were the fixture database: an
// assertion that passes there is meaningless and the mistake is silent.
throw new Error("Refusing to run e2e assertions against smart_office_prod");
}
const client = new Client(config);
await client.connect();
try {
const { rows } = await client.query(sql, params);
return rows as T[];
} finally {
await client.end();
}
}
/** Convenience for the common "one row, one column" assertion. */
export async function scalar<T = string>(sql: string, params: unknown[] = []): Promise<T | null> {
const rows = await query<Record<string, T>>(sql, params);
if (!rows.length) return null;
return Object.values(rows[0])[0] ?? null;
}