backup.test.ts

3.1 KB · raw

1import { describe, expect, it } from "vitest";23import {4  exportEntriesToPeachJson,5  importEntriesFromPeachJson,6  type PeachExportEnvelope,7} from "./backup";89describe("shared backup", () => {10  const sampleEntries = [11    {12      id: "login-1",13      type: "login" as const,14      title: "GitHub",15      username: "june",16      password: "secret123",17      url: "https://github.com",18      notes: "2fa enabled",19      createdAt: 1,20      modifiedAt: 1,21    },22  ];2324  it("importEntriesFromPeachJson decrypts entries with the correct password", async () => {25    const envelope = await exportEntriesToPeachJson(sampleEntries, "export-pass");2627    const result = await importEntriesFromPeachJson(JSON.stringify(envelope), "export-pass");28    expect(result.entries).toMatchObject([29      {30        type: "login",31        title: "GitHub",32        username: "june",33        password: "secret123",34        url: "https://github.com",35      },36    ]);37  });3839  it("importEntriesFromPeachJson rejects the wrong password", async () => {40    const envelope = await exportEntriesToPeachJson(sampleEntries, "export-pass");4142    await expect(43      importEntriesFromPeachJson(JSON.stringify(envelope), "wrong-pass")44    ).rejects.toThrow("INVALID_EXPORT_PASSWORD");45  });4647  it("importEntriesFromPeachJson rejects malformed JSON", async () => {48    await expect(importEntriesFromPeachJson("{not json", "export-pass")).rejects.toThrow(49      "INVALID_PEACH_EXPORT"50    );51  });5253  it("export includes trashed entries; import preserves deletedAt", async () => {54    const trashedAt = 1_700_000_010_000;55    const entries = [56      ...sampleEntries,57      {58        id: "login-2",59        type: "login" as const,60        title: "Old Service",61        username: "june",62        password: "oldpass",63        url: "https://old.example.com",64        createdAt: 1,65        modifiedAt: 1,66        deletedAt: trashedAt,67      },68    ];6970    const envelope = await exportEntriesToPeachJson(entries, "export-pass");71    const { entries: imported } = await importEntriesFromPeachJson(JSON.stringify(envelope), "export-pass");7273    expect(imported).toHaveLength(2);7475    const active = imported.filter((e) => !e.deletedAt);76    const trashed = imported.filter((e) => !!e.deletedAt);7778    expect(active).toHaveLength(1);79    expect(trashed).toHaveLength(1);80    expect(trashed[0].title).toBe("Old Service");81    expect(trashed[0].deletedAt).toBe(trashedAt);82  });8384  it("importEntriesFromPeachJson rejects envelopes with missing required fields", async () => {85    const envelope: Partial<PeachExportEnvelope> = {86      version: 2,87      type: "peach-export",88      kdf: "PBKDF2-SHA256",89      salt: "00",90      nonce: "00",91      ciphertext: "00",92    };9394    await expect(95      importEntriesFromPeachJson(JSON.stringify(envelope), "export-pass")96    ).rejects.toThrow("INVALID_PEACH_EXPORT");97  });9899  it("MV-5: importEntriesFromPeachJson rejects an over-limit raw string with IMPORT_TOO_LARGE", async () => {100    const huge = "x".repeat(11 * 1024 * 1024); // 11 MiB > 10 MiB cap101    await expect(importEntriesFromPeachJson(huge, "export-pass")).rejects.toThrow(102      /IMPORT_TOO_LARGE/,103    );104  });105});