import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import fs from "fs"; import os from "os"; import path from "path"; const tempDirs: string[] = []; const originalEnv = { ...process.env }; function makeTempDir(): string { const dir = fs.mkdtempSync(path.join(os.tmpdir(), "termix-db-guard-")); tempDirs.push(dir); return dir; } beforeEach(() => { vi.resetModules(); }); afterEach(() => { process.env = { ...originalEnv }; for (const dir of tempDirs.splice(0)) { fs.rmSync(dir, { recursive: true, force: true }); } }); describe("unreadable encrypted database guard", () => { it("refuses to start and leaves the file untouched", async () => { const dataDir = makeTempDir(); const encryptedDbPath = path.join(dataDir, "db.sqlite.encrypted"); // A truncated write: the file exists but its header cannot be parsed. const damaged = Buffer.from("not-a-valid-encrypted-database-header"); fs.writeFileSync(encryptedDbPath, damaged); process.env.DATA_DIR = dataDir; process.env.DB_FILE_ENCRYPTION = "true"; process.env.DATABASE_KEY = "a".repeat(64); delete process.env.ALLOW_UNREADABLE_DB_RESET; const { initializeDatabase } = await import("../../database/db/index.js"); await expect(initializeDatabase()).rejects.toThrow( /not a valid encrypted database/, ); // the damaged file must survive for recovery expect(fs.readFileSync(encryptedDbPath)).toEqual(damaged); }); it("names the opt-out environment variable in the error", async () => { const dataDir = makeTempDir(); fs.writeFileSync(path.join(dataDir, "db.sqlite.encrypted"), "damaged"); process.env.DATA_DIR = dataDir; process.env.DB_FILE_ENCRYPTION = "true"; process.env.DATABASE_KEY = "a".repeat(64); delete process.env.ALLOW_UNREADABLE_DB_RESET; const { initializeDatabase, ALLOW_UNREADABLE_DB_RESET_ENV } = await import("../../database/db/index.js"); await expect(initializeDatabase()).rejects.toThrow( new RegExp(ALLOW_UNREADABLE_DB_RESET_ENV), ); }); });