import { mock } from "bun:test"; import { memoryScopes } from "../scoped-db/testing"; import type { ScopedValue } from "../scoped-db/types"; /** * In-memory RepoState/AccountState stores and scope locators for tests: call `install()` before importing the * module under test, `reset()` in beforeEach, and seed rows with `repo(scope)` / `account(id)` (bun:sqlite). */ export function scopedTestState() { let scopes = memoryScopes(); const locators = new Map(); const key = (kind: string, value: string) => `${kind}\u0000${value}`; const state = { install(): void { mock.module("../scoped-db", () => ({ repoDb: (id: string) => scopes.repoDb(id), accountDb: (id: string) => scopes.accountDb(id), stmt: (sql: string, ...params: ScopedValue[]) => ({ sql, params }), })); mock.module("../scoped-db/locators", () => ({ locate: async (kind: string, value: string) => locators.get(key(kind, value)) ?? null, registerLocator: async (kind: string, value: string, scopeId: string) => { locators.set(key(kind, value), scopeId); }, registerLocators: async (entries: Array<[string, string, string]>) => { for (const [kind, value, scopeId] of entries) locators.set(key(kind, value), scopeId); }, forgetLocator: async (kind: string, value: string) => { locators.delete(key(kind, value)); }, forgetScope: async (scopeId: string) => { for (const [entry, scope] of locators) if (scope === scopeId) locators.delete(entry); }, locatedScopes: async (kind: string) => [ ...new Set([...locators].filter(([entry]) => entry.startsWith(`${kind}\u0000`)).map(([, scope]) => scope)), ], locatedScopesFrom: async (kind: string, from: string, limit: number) => [ ...new Set( [...locators] .filter(([entry, scope]) => entry.startsWith(`${kind}\u0000`) && scope >= from) .map(([, scope]) => scope), ), ] .sort() .slice(0, limit), locatorsBefore: async (kind: string, before: string, limit: number) => [...locators] .filter(([entry]) => entry.startsWith(`${kind}\u0000`) && entry.slice(kind.length + 1) < before) .map(([entry, scope_id]) => ({ key: entry.slice(kind.length + 1), scope_id })) .sort((left, right) => left.key.localeCompare(right.key)) .slice(0, limit), locatedScopesAmong: async (kind: string, scopeIds: string[]) => [ ...new Set( [...locators] .filter(([entry, scope]) => entry.startsWith(`${kind}\u0000`) && scopeIds.includes(scope)) .map(([, scope]) => scope), ), ], locatedByPrefix: async (kind: string, prefix: string) => [ ...new Set( [...locators].filter(([entry]) => entry.startsWith(`${kind}\u0000${prefix}`)).map(([, scope]) => scope), ), ], })); }, reset(): void { scopes = memoryScopes(); locators.clear(); }, repo: (scope: string) => scopes.raw("repo", scope), account: (id: string) => scopes.raw("account", id), locate: (kind: string, value: string) => locators.get(key(kind, value)) ?? null, register(kind: string, value: string, scopeId: string): void { locators.set(key(kind, value), scopeId); }, insert(scope: string, table: string, row: Record): void { const defaults: Record> = { issues: { author_id: "u1", title: "t", state: "open", is_pull: 1, created_at: 0, updated_at: 0, repo_id: scope, }, pull_requests: { repo_id: scope, head_sha: "sha", base_ref: "main", base_sha: "base" }, site_deployments: { environment: "preview", source: "branch", ref: "r", sha: "s", status: "active", created_at: 0, }, workflow_runs: { repo_id: scope, source_repo_id: scope, workflow_path: "w", workflow_name: "w", title: "t" }, push_events: { repo_id: scope, before_sha: "0", created_at: 0 }, }; const full = { ...defaults[table], ...row }; const columns = Object.keys(full); state .repo(scope) .query(`INSERT INTO ${table} (${columns.join(", ")}) VALUES (${columns.map(() => "?").join(", ")})`) .run(...(Object.values(full) as ScopedValue[])); }, /** Inserts a site row into its scope and registers its `site` and `site_host` locators. */ site(row: Record & { id: string }): string { const scope = typeof row.repo_id === "string" ? row.repo_id : `site:${row.id}`; const full = { label: row.id, source: "branch", branch: "main", created_at: 0, updated_at: 0, ...row }; const columns = Object.keys(full); state .repo(scope) .query(`INSERT INTO sites (${columns.join(", ")}) VALUES (${columns.map(() => "?").join(", ")})`) .run(...(Object.values(full) as ScopedValue[])); locators.set(key("site", row.id), scope); locators.set(key("site_host", String(full.label).toLowerCase()), row.id); return scope; }, }; return state; }