65 lines
2.4 KiB
TypeScript
65 lines
2.4 KiB
TypeScript
import { DatabaseSync } from 'node:sqlite';
|
|
import { join } from 'node:path';
|
|
import { findAppointment, openDb, type Appointment } from '@when/db';
|
|
|
|
import { workerDirectory } from './paths.ts';
|
|
|
|
// Specs are also imported for collection, before global setup. Paths are only used
|
|
// inside workers, where Playwright provides TEST_WORKER_INDEX and inherits the run dir.
|
|
const dataDirectory =
|
|
process.env.TEST_WORKER_INDEX === undefined ? '' : join(workerDirectory(), 'data');
|
|
export const APP_DB_PATH = dataDirectory ? join(dataDirectory, 'when.sqlite') : '';
|
|
const QUEUE_DB_PATH = dataDirectory ? join(dataDirectory, 'openworkflow.sqlite') : '';
|
|
|
|
export function resetDatabases(): void {
|
|
workerDirectory(); // Fail rather than silently falling back to a developer database.
|
|
for (const [path, tables] of [
|
|
[APP_DB_PATH, ['appointments', 'external_calendar_busy', 'service_status']],
|
|
[QUEUE_DB_PATH, ['workflow_signals', 'workflow_schedules', 'step_attempts', 'workflow_runs']]
|
|
] as const) {
|
|
const db = new DatabaseSync(path);
|
|
try {
|
|
// Keep migration bookkeeping and schemas; no background worker runs in E2E.
|
|
db.exec('PRAGMA foreign_keys = ON; BEGIN; PRAGMA defer_foreign_keys = ON;');
|
|
for (const table of tables) db.exec(`DELETE FROM ${table}`);
|
|
db.exec('COMMIT');
|
|
} finally {
|
|
db.close();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* The stored row behind an appointment page. The pages render a lossy view of it —
|
|
* a status heading says nothing about the audit trail or the guest's own answers —
|
|
* so anything a write is supposed to persist is asserted here instead.
|
|
*/
|
|
export async function readAppointment(id: string): Promise<Appointment> {
|
|
const db = openDb(APP_DB_PATH);
|
|
|
|
try {
|
|
const row = await findAppointment(db, id);
|
|
if (!row) throw new Error(`no appointment "${id}" in the fixture database`);
|
|
return row;
|
|
} finally {
|
|
await db.destroy();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Runs enqueued under an idempotency key. No worker runs during E2E, so a queued run
|
|
* is as far as the guest's email and the calendar push can be observed — but it is the
|
|
* difference between accepting an appointment and merely recording that we did.
|
|
*/
|
|
export function readQueuedWorkflows(idempotencyKey: string): { workflow_name: string }[] {
|
|
const db = new DatabaseSync(QUEUE_DB_PATH);
|
|
|
|
try {
|
|
return db
|
|
.prepare('SELECT workflow_name FROM workflow_runs WHERE idempotency_key = ?')
|
|
.all(idempotencyKey) as unknown as { workflow_name: string }[];
|
|
} finally {
|
|
db.close();
|
|
}
|
|
}
|