import Database from 'better-sqlite3'; import { fileURLToPath } from 'node:url'; /** * Read only access to the stack's database, used to assert the rows a flow was supposed * to write and to read the emailed verification code (which development prints to the * server console rather than sending). Only valid against a local SQLite stack. */ const DB_PATH = process.env['E2E_DB_PATH'] ?? fileURLToPath(new URL('../apps/api/data/app.db', import.meta.url)); function open(): Database.Database { return new Database(DB_PATH, { readonly: true, fileMustExist: true }); } function query(run: (db: Database.Database) => T): T { const db = open(); try { return run(db); } finally { db.close(); } } /** better-auth stores the code as `:` against the recipient's address. */ export function readVerificationOtp(email: string): string { return query((db) => { const row = db .prepare('select value from verification where identifier = ? order by createdAt desc limit 1') .get(`email-verification-otp-${email}`) as { value: string } | undefined; if (!row) throw new Error(`no verification code was issued for ${email}`); return (row.value.split(':')[0] ?? '').trim(); }); } export function readProfile(email: string): Record | undefined { return query( (db) => db .prepare( 'select p.* from profiles p join user u on u.id = p.user_id where u.email = ?', ) .get(email) as Record | undefined, ); } export function readAuditActions(email: string): string[] { return query((db) => { const rows = db .prepare( 'select a.action from audit_log a join user u on u.id = a.subject_user_id where u.email = ? order by a.created_at', ) .all(email) as { action: string }[]; return rows.map((row) => row.action); }); } export function readConsents(email: string): { kind: string; revoked: boolean }[] { return query((db) => { const rows = db .prepare( 'select c.kind, c.revoked_at from consents c join user u on u.id = c.user_id where u.email = ?', ) .all(email) as { kind: string; revoked_at: string | null }[]; return rows.map((row) => ({ kind: row.kind, revoked: row.revoked_at !== null })); }); } /** * The newest month that has confirmed documents but no declaration yet. * * Approving is a one way door, so a test that always used the same period would only pass * on a freshly seeded database. Picking an unused month lets the declaration flow run * repeatably against a stack that has already been exercised. */ export function findMonthWithoutDeclaration(email: string): string | null { return query((db) => { const row = db .prepare( `select substr(d.issue_date, 1, 7) as period from documents d join user u on u.id = d.user_id where u.email = ? and d.status = 'confirmed' and not exists ( select 1 from declarations dec where dec.user_id = d.user_id and dec.form_code = '120' and dec.period = substr(d.issue_date, 1, 7) ) group by period order by period desc limit 1`, ) .get(email) as { period: string } | undefined; return row?.period ?? null; }); }