import { describe, it, expect, vi, beforeAll, afterAll } from "vitest"; import fs from "fs"; import os from "os"; import path from "path"; import crypto from "crypto"; const FIXED_KEY = crypto.createHash("sha256").update("test-db-key").digest(); // Mock SystemCrypto so the encryptor uses a deterministic key and never touches // the real key material / filesystem-managed .env. vi.mock("../../utils/system-crypto.js", () => ({ SystemCrypto: { getInstance: () => ({ getDatabaseKey: async () => FIXED_KEY, }), }, })); const { DatabaseFileEncryption } = await import("../../utils/database-file-encryption.js"); let tmpDir: string; beforeAll(() => { tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "termix-dbenc-")); }); afterAll(() => { fs.rmSync(tmpDir, { recursive: true, force: true }); }); describe("DatabaseFileEncryption buffer round-trip", () => { it("encrypts a buffer to a file and decrypts it back identically", async () => { const plaintext = Buffer.from("SQLite format 3 some db bytes here"); const target = path.join(tmpDir, "db.sqlite.encrypted"); await DatabaseFileEncryption.encryptDatabaseFromBuffer(plaintext, target); expect(fs.existsSync(target)).toBe(true); const decrypted = await DatabaseFileEncryption.decryptDatabaseToBuffer(target); expect(decrypted.equals(plaintext)).toBe(true); }); it("recognizes its own encrypted files", async () => { const target = path.join(tmpDir, "db2.encrypted"); await DatabaseFileEncryption.encryptDatabaseFromBuffer( Buffer.from("hello"), target, ); expect(DatabaseFileEncryption.isEncryptedDatabaseFile(target)).toBe(true); }); it("does not treat arbitrary files as encrypted", () => { const plainPath = path.join(tmpDir, "plain.txt"); fs.writeFileSync(plainPath, "just some text"); expect(DatabaseFileEncryption.isEncryptedDatabaseFile(plainPath)).toBe( false, ); }); it("fails to decrypt when the ciphertext is tampered with", async () => { const target = path.join(tmpDir, "db3.encrypted"); await DatabaseFileEncryption.encryptDatabaseFromBuffer( Buffer.from("important data"), target, ); const buf = fs.readFileSync(target); // Flip the final byte (inside the ciphertext) to break the GCM auth tag. buf[buf.length - 1] = buf[buf.length - 1] ^ 0xff; fs.writeFileSync(target, buf); await expect( DatabaseFileEncryption.decryptDatabaseToBuffer(target), ).rejects.toThrow(); }); });