import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; export interface Fixture { root: string; shared: string; staging: string; t5: string; dbPath: string; cleanup: () => Promise; } /** * Build a zombies/ tree in a temp dir and point env at it BEFORE the code * under test is imported. Returns paths + a cleanup fn. */ export async function newFixture(files: Record = {}, servers: string[] = ['t5_kino_1']): Promise { const root = await mkdtemp(join(tmpdir(), 'cherry-')); const shared = join(root, 'shared/scripts'); const staging = join(shared, 'betas'); const t5 = join(root, 't5'); const dbPath = join(root, 'cherry.db'); await mkdir(staging, { recursive: true }); await mkdir(t5, { recursive: true }); for (const [rel, body] of Object.entries(files)) { const p = join(shared, rel); await mkdir(join(p, '..'), { recursive: true }); await writeFile(p, body); } for (const s of servers) { await mkdir(join(t5, s, 'runtime/plutonium/storage/t5/raw/scripts/sp'), { recursive: true }); } process.env.SHARED_SCRIPTS = shared; process.env.SERVERS_ROOT = t5; process.env.REL_DEST = 'runtime/plutonium/storage/t5/raw/scripts/sp'; process.env.DB_PATH = dbPath; process.env.DEPLOY_KEEP_DAYS = '7'; process.env.HTTP_TIMEOUT_MS = '2000'; return { root, shared, staging, t5, dbPath, cleanup: async () => { // On Windows the SQLite handle keeps a lock on cherry.db-shm/wal // and rmdir refuses to remove the temp tree. Close the singleton // (if this test imported it) before removing the dir. Fresh // dynamic import so we grab whatever handle the test opened. try { const mod = (await import('../../src/lib/server/db/index.js')) as { closeDb: () => void; }; mod.closeDb(); } catch { /* db module was never loaded — nothing to close */ } await rm(root, { recursive: true, force: true, maxRetries: 5, retryDelay: 50 }); } }; } /** * Fresh module-level state. Vitest caches modules per file — this forces a * re-import so the tests can rebuild the singleton DB handle against a new * fixture. */ export async function importFresh(mod: string): Promise { return (await import(`${mod}?t=${Date.now()}${Math.random()}`)) as T; }