import { Database } from "bun:sqlite"; import { ACCOUNT_SCHEMA, REPO_SCHEMA } from "./schema.gen"; import { ACCOUNT_SCHEMA_EXTRA, REPO_SCHEMA_EXTRA } from "./schema-extra"; import type { ScopedDb, ScopedStatement, ScopedValue } from "./types"; /** * Test double for `~/server/scoped-db`: every repo and account gets its own in-memory bun:sqlite with the real * scoped schema. Use with `mock.module("/scoped-db", () => memoryScopes().module)`. */ export function memoryScopes() { const databases = new Map(); const open = (key: string, schema: readonly string[]) => { let db = databases.get(key); if (!db) { db = new Database(":memory:"); for (const statement of schema) db.run(statement); databases.set(key, db); } return db; }; const make = (scopeId: string, db: Database): ScopedDb => { const meta = (result: { changes: number; lastInsertRowid: number | bigint }) => ({ meta: { changes: result.changes, last_row_id: Number(result.lastInsertRowid) }, }); return { scopeId, async first(sql: string, ...params: ScopedValue[]) { return (db.query(sql).get(...params) as T | null) ?? null; }, async all(sql: string, ...params: ScopedValue[]) { return db.query(sql).all(...params) as T[]; }, async run(sql: string, ...params: ScopedValue[]) { return meta(db.query(sql).run(...params)); }, async batch(statements: ScopedStatement[]) { return db.transaction(() => statements.map((statement) => meta(db.query(statement.sql).run(...statement.params))), )(); }, }; }; const repoSchema = [...REPO_SCHEMA, ...REPO_SCHEMA_EXTRA]; const accountSchema = [...ACCOUNT_SCHEMA, ...ACCOUNT_SCHEMA_EXTRA]; const repoDb = (repoId: string) => make(repoId, open(`repo:${repoId}`, repoSchema)); const accountDb = (accountId: string) => make(accountId, open(`account:${accountId}`, accountSchema)); return { repoDb, accountDb, raw: (kind: "repo" | "account", id: string) => open(`${kind}:${id}`, kind === "repo" ? repoSchema : accountSchema), module: { repoDb, accountDb, stmt: (sql: string, ...params: ScopedValue[]): ScopedStatement => ({ sql, params }), }, }; }