/** * Creates an isolated in-memory SQLite DB with the full schema applied. * Use this in tests instead of the shared `db` singleton. */ import Database, { type Database as BetterSqlite3Database } from 'better-sqlite3'; import { drizzle } from 'drizzle-orm/better-sqlite3'; import * as schema from '../db/schema.js'; type DrizzleDb = ReturnType>; export function makeTestDb(): DrizzleDb & { rawSqlite: BetterSqlite3Database } { const sqlite = new Database(':memory:'); sqlite.pragma('foreign_keys = ON'); sqlite.exec(` CREATE TABLE IF NOT EXISTS users ( id TEXT PRIMARY KEY, email TEXT NOT NULL UNIQUE, name TEXT, image TEXT, google_id TEXT UNIQUE, role TEXT NOT NULL DEFAULT 'user', consent_given INTEGER NOT NULL DEFAULT 0, consent_at TEXT, created_at TEXT NOT NULL, deleted_at TEXT ); CREATE TABLE IF NOT EXISTS integration_tokens ( id TEXT PRIMARY KEY, user_id TEXT NOT NULL REFERENCES users(id), provider TEXT NOT NULL, access_token TEXT NOT NULL, refresh_token TEXT, expires_at TEXT, connected_at TEXT NOT NULL, token_status TEXT NOT NULL DEFAULT 'active', UNIQUE(user_id, provider) ); CREATE TABLE IF NOT EXISTS tip_feedback ( id TEXT PRIMARY KEY, user_id TEXT NOT NULL REFERENCES users(id), tip_id TEXT NOT NULL, action TEXT NOT NULL, source_id TEXT, dwell_ms INTEGER, reward_milli INTEGER, created_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS tip_views ( id TEXT PRIMARY KEY, user_id TEXT NOT NULL REFERENCES users(id), tip_id TEXT NOT NULL, served_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS push_subscriptions ( id TEXT PRIMARY KEY, user_id TEXT NOT NULL REFERENCES users(id), endpoint TEXT NOT NULL UNIQUE, p256dh TEXT NOT NULL, auth TEXT NOT NULL, created_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS sessions ( id TEXT PRIMARY KEY, user_id TEXT NOT NULL REFERENCES users(id), expires_at TEXT NOT NULL, created_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS admin_actions ( id TEXT PRIMARY KEY, admin_id TEXT NOT NULL REFERENCES users(id), action TEXT NOT NULL, target_type TEXT, target_id TEXT, detail TEXT, created_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS tip_scores ( id TEXT PRIMARY KEY, user_id TEXT NOT NULL REFERENCES users(id), tip_id TEXT NOT NULL, policy TEXT NOT NULL, ml_score INTEGER, features_json TEXT, candidate_count INTEGER, latency_ms INTEGER, served_at TEXT NOT NULL, prompt_version TEXT, llm_model TEXT, tip_kind TEXT ); CREATE TABLE IF NOT EXISTS saved_queries ( id TEXT PRIMARY KEY, admin_id TEXT NOT NULL REFERENCES users(id), name TEXT NOT NULL, sql TEXT NOT NULL, created_at TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS user_profile_features ( user_id TEXT NOT NULL REFERENCES users(id), name TEXT NOT NULL, value REAL, value_text TEXT, updated_at TEXT NOT NULL, ttl_sec INTEGER NOT NULL, PRIMARY KEY (user_id, name) ); CREATE TABLE IF NOT EXISTS sim_runs ( id TEXT PRIMARY KEY, policy_a TEXT NOT NULL, policy_b TEXT NOT NULL, n_users INTEGER NOT NULL, n_rounds INTEGER NOT NULL, tasks_per_round INTEGER NOT NULL DEFAULT 8, use_llm INTEGER NOT NULL DEFAULT 0, status TEXT NOT NULL DEFAULT 'pending', summary_json TEXT, winner TEXT, persona_breakdown_json TEXT, created_at TEXT NOT NULL, finished_at TEXT ); CREATE TABLE IF NOT EXISTS agent_outputs ( id TEXT PRIMARY KEY, user_id TEXT NOT NULL REFERENCES users(id), agent_id TEXT NOT NULL, prompt_text TEXT NOT NULL, signals_snapshot TEXT, computed_at TEXT NOT NULL, expires_at TEXT NOT NULL, agent_version TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS sim_events ( id TEXT PRIMARY KEY, run_id TEXT NOT NULL REFERENCES sim_runs(id), round INTEGER NOT NULL, user_id TEXT NOT NULL, persona TEXT NOT NULL, policy TEXT NOT NULL, tip_content TEXT NOT NULL, priority INTEGER NOT NULL, is_overdue INTEGER NOT NULL, action TEXT NOT NULL, dwell_ms INTEGER, reward_milli INTEGER NOT NULL, hour INTEGER NOT NULL, day_of_week INTEGER NOT NULL, created_at TEXT NOT NULL ); `); const db = drizzle(sqlite, { schema }); // `sqlite` is exposed as `rawSqlite` so tests that mock `../db/index.js` // can provide the same `{ db, rawSqlite }` shape as the prod module. return Object.assign(db, { rawSqlite: sqlite }); } export type TestDb = ReturnType;