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>( sql: string, params: unknown[] = [], ): Promise { 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(sql: string, params: unknown[] = []): Promise { const rows = await query>(sql, params); if (!rows.length) return null; return Object.values(rows[0])[0] ?? null; }