recovery-pending.test.ts

3.1 KB · raw

1import { beforeEach, describe, expect, it, vi } from "vitest";23describe("recovery confirmation state", () => {4  let local: { get: ReturnType<typeof vi.fn>; set: ReturnType<typeof vi.fn>; remove: ReturnType<typeof vi.fn> };5  let session: { get: ReturnType<typeof vi.fn>; set: ReturnType<typeof vi.fn>; remove: ReturnType<typeof vi.fn> };6  let sessionValues: Record<string, unknown>;78  beforeEach(() => {9    vi.resetModules();10    sessionValues = {};11    local = {12      get: vi.fn(async () => ({})),13      set: vi.fn(async () => undefined),14      remove: vi.fn(async () => undefined),15    };16    session = {17      get: vi.fn(async (key: string) => ({ [key]: sessionValues[key] })),18      set: vi.fn(async (values: Record<string, unknown>) => Object.assign(sessionValues, values)),19      remove: vi.fn(async (key: string) => { delete sessionValues[key]; }),20    };21    vi.stubGlobal("chrome", { storage: { local, session } });22  });2324  it("stores the pending exemption in session storage, never local storage", async () => {25    const pending = await import("./recovery-pending");2627    await pending.setRecoveryConfirmPending();2829    expect(session.set).toHaveBeenCalledWith({30      peach_recovery_pending: expect.any(Number),31    });32    expect(local.set).not.toHaveBeenCalled();33    await expect(pending.isRecoveryConfirmPending()).resolves.toBe(true);34  });3536  it("clears the session-owned exemption", async () => {37    const pending = await import("./recovery-pending");38    await pending.setRecoveryConfirmPending();3940    await pending.clearRecoveryConfirmPending();4142    expect(session.remove).toHaveBeenCalledWith("peach_recovery_pending");43    await expect(pending.isRecoveryConfirmPending()).resolves.toBe(false);44    expect(local.remove).not.toHaveBeenCalled();45  });4647  it("uses background memory when session storage is unavailable", async () => {48    vi.stubGlobal("chrome", { storage: { local } });49    const pending = await import("./recovery-pending");5051    await pending.setRecoveryConfirmPending();5253    await expect(pending.isRecoveryConfirmPending()).resolves.toBe(true);54    await pending.clearRecoveryConfirmPending();55    await expect(pending.isRecoveryConfirmPending()).resolves.toBe(false);56    expect(local.set).not.toHaveBeenCalled();57  });5859  it("expires the recovery exemption after its hard deadline", async () => {60    vi.useFakeTimers();61    try {62      const pending = await import("./recovery-pending");63      await pending.setRecoveryConfirmPending();6465      await vi.advanceTimersByTimeAsync(10 * 60 * 1000 + 1);6667      await expect(pending.isRecoveryConfirmPending()).resolves.toBe(false);68      expect(session.remove).toHaveBeenCalledWith("peach_recovery_pending");69    } finally {70      vi.useRealTimers();71    }72  });7374  it("preserves the recovery obligation when the vault locks", async () => {75    const pending = await import("./recovery-pending");76    const vault = await import("./vault");77    await pending.setRecoveryConfirmPending();7879    await vault.lock();8081    expect(await pending.isRecoveryConfirmPending()).toBe(true);82    expect(sessionValues.peach_recovery_pending).toBeDefined();83  });84});