import { describe, expect, it } from "vitest"; import { exportEntriesToPeachJson, importEntriesFromPeachJson, type PeachExportEnvelope, } from "./backup"; describe("shared backup", () => { const sampleEntries = [ { id: "login-1", type: "login" as const, title: "GitHub", username: "june", password: "secret123", url: "https://github.com", notes: "2fa enabled", createdAt: 1, modifiedAt: 1, }, ]; it("importEntriesFromPeachJson decrypts entries with the correct password", async () => { const envelope = await exportEntriesToPeachJson(sampleEntries, "export-pass"); const result = await importEntriesFromPeachJson(JSON.stringify(envelope), "export-pass"); expect(result.entries).toMatchObject([ { type: "login", title: "GitHub", username: "june", password: "secret123", url: "https://github.com", }, ]); }); it("importEntriesFromPeachJson rejects the wrong password", async () => { const envelope = await exportEntriesToPeachJson(sampleEntries, "export-pass"); await expect( importEntriesFromPeachJson(JSON.stringify(envelope), "wrong-pass") ).rejects.toThrow("INVALID_EXPORT_PASSWORD"); }); it("importEntriesFromPeachJson rejects malformed JSON", async () => { await expect(importEntriesFromPeachJson("{not json", "export-pass")).rejects.toThrow( "INVALID_PEACH_EXPORT" ); }); it("export includes trashed entries; import preserves deletedAt", async () => { const trashedAt = 1_700_000_010_000; const entries = [ ...sampleEntries, { id: "login-2", type: "login" as const, title: "Old Service", username: "june", password: "oldpass", url: "https://old.example.com", createdAt: 1, modifiedAt: 1, deletedAt: trashedAt, }, ]; const envelope = await exportEntriesToPeachJson(entries, "export-pass"); const { entries: imported } = await importEntriesFromPeachJson(JSON.stringify(envelope), "export-pass"); expect(imported).toHaveLength(2); const active = imported.filter((e) => !e.deletedAt); const trashed = imported.filter((e) => !!e.deletedAt); expect(active).toHaveLength(1); expect(trashed).toHaveLength(1); expect(trashed[0].title).toBe("Old Service"); expect(trashed[0].deletedAt).toBe(trashedAt); }); it("importEntriesFromPeachJson rejects envelopes with missing required fields", async () => { const envelope: Partial = { version: 2, type: "peach-export", kdf: "PBKDF2-SHA256", salt: "00", nonce: "00", ciphertext: "00", }; await expect( importEntriesFromPeachJson(JSON.stringify(envelope), "export-pass") ).rejects.toThrow("INVALID_PEACH_EXPORT"); }); it("MV-5: importEntriesFromPeachJson rejects an over-limit raw string with IMPORT_TOO_LARGE", async () => { const huge = "x".repeat(11 * 1024 * 1024); // 11 MiB > 10 MiB cap await expect(importEntriesFromPeachJson(huge, "export-pass")).rejects.toThrow( /IMPORT_TOO_LARGE/, ); }); });