import { beforeEach, describe, expect, it, vi } from "vitest"; import type { StoredVault } from "../shared/types"; type StorageShape = Record; describe("vault duress adoption helpers", () => { let storage: StorageShape; beforeEach(() => { vi.resetModules(); storage = {}; const chromeMock = { storage: { local: { get: vi.fn(async (key?: string | string[]) => { if (!key) return storage; if (typeof key === "string") { return { [key]: storage[key] }; } return key.reduce>((result, entry) => { result[entry] = storage[entry]; return result; }, {}); }), set: vi.fn(async (next: Record) => { Object.assign(storage, next); }), remove: vi.fn(async (keys: string | string[]) => { const list = Array.isArray(keys) ? keys : [keys]; for (const key of list) { delete storage[key]; } }), }, }, alarms: { clear: vi.fn(), create: vi.fn(), onAlarm: { addListener: vi.fn() }, }, idle: { setDetectionInterval: vi.fn(), onStateChanged: { addListener: vi.fn() }, }, } as unknown as typeof chrome; (globalThis as { chrome?: typeof chrome }).chrome = chromeMock; vi.stubGlobal("fetch", vi.fn(async () => { throw new Error("network disabled in tests"); })); }); it("exportDuressSlotForAdoption returns null when duress is not configured", async () => { const vault = await import("./vault"); await vault.createVault("correct horse battery staple"); expect(await vault.isDuressEnabled()).toBe(false); expect(await vault.exportDuressSlotForAdoption()).toBeNull(); }); it("exportDuressSlotForAdoption returns JSON of slot B when duress is configured", async () => { const vault = await import("./vault"); await vault.createVault("correct horse battery staple"); await vault.setupDuress("decoy-password-67890"); expect(await vault.isDuressEnabled()).toBe(true); const result = await vault.exportDuressSlotForAdoption(); expect(result).not.toBeNull(); const parsed = JSON.parse(result!) as StoredVault; expect(parsed.version).toBe(1); expect(typeof parsed.salt).toBe("string"); expect(typeof parsed.ciphertext).toBe("string"); expect(typeof parsed.tag).toBe("string"); }); it("installAdoptedDuressSlot writes slot B with matching sealed bytes", async () => { const vault = await import("./vault"); await vault.createVault("correct horse battery staple"); await vault.setupDuress("decoy-password-67890"); const sourceJson = await vault.exportDuressSlotForAdoption(); expect(sourceJson).not.toBeNull(); // Reset to simulate a fresh receiver vault (no duress configured yet) vi.resetModules(); storage = { ...storage }; // keep existing storage const vault2 = await import("./vault"); // Install the source's duress slot on the receiver await vault2.installAdoptedDuressSlot(sourceJson!); // Verify the stored slot B matches the source exactly (passthrough) const receiverSlotB = storage.peach_vault_b as StoredVault; const sourceParsed = JSON.parse(sourceJson!) as StoredVault; expect(receiverSlotB.version).toBe(sourceParsed.version); expect(receiverSlotB.salt).toBe(sourceParsed.salt); expect(receiverSlotB.nonce).toBe(sourceParsed.nonce); expect(receiverSlotB.ciphertext).toBe(sourceParsed.ciphertext); expect(receiverSlotB.tag).toBe(sourceParsed.tag); }); it("installAdoptedDuressSlot rejects invalid JSON", async () => { const vault = await import("./vault"); await vault.createVault("correct horse battery staple"); await expect( vault.installAdoptedDuressSlot("not-json"), ).rejects.toThrow("invalid_duress_slot_shape"); }); it("installAdoptedDuressSlot rejects a valid JSON object with wrong shape", async () => { const vault = await import("./vault"); await vault.createVault("correct horse battery staple"); await expect( vault.installAdoptedDuressSlot(JSON.stringify({ version: 2, foo: "bar" })), ).rejects.toThrow("invalid_duress_slot_shape"); }); });