12345678910import { beforeEach, describe, expect, it, vi } from "vitest";1112type StorageShape = Record<string, unknown>;1314describe("codex handlers", () => {15 let storage: StorageShape;1617 beforeEach(() => {18 vi.resetModules();19 vi.doUnmock("./vault");20 vi.doUnmock("./license");21 storage = {};2223 const chromeMock = {24 storage: {25 local: {26 get: vi.fn(async (key?: string | string[]) => {27 if (!key) return storage;28 if (typeof key === "string") return { [key]: storage[key] };29 return key.reduce<Record<string, unknown>>((acc, k) => {30 acc[k] = storage[k];31 return acc;32 }, {});33 }),34 set: vi.fn(async (next: Record<string, unknown>) => {35 Object.assign(storage, next);36 }),37 remove: vi.fn(async (keys: string | string[]) => {38 const list = Array.isArray(keys) ? keys : [keys];39 for (const k of list) delete storage[k];40 }),41 },42 },43 } as unknown as typeof chrome;44 (globalThis as { chrome?: typeof chrome }).chrome = chromeMock;45 });4647 it("handleGetCodexVaultData rejects free users with pro_required", async () => {48 vi.doMock("./license", () => ({ isProUser: vi.fn(async () => false) }));49 vi.doMock("./vault", () => ({50 isUnlocked: () => true,51 getEntries: () => [],52 getRecoveryWords: async () => null,53 }));54 const { handleGetCodexVaultData } = await import("./codex-handlers");55 const result = await handleGetCodexVaultData();56 expect(result).toEqual({ ok: false, error: "pro_required" });57 });5859 it("handleGetCodexVaultData returns VAULT_LOCKED for locked vault", async () => {60 vi.doMock("./license", () => ({ isProUser: vi.fn(async () => true) }));61 vi.doMock("./vault", () => ({62 isUnlocked: () => false,63 getEntries: () => {64 throw new Error("vault locked");65 },66 getRecoveryWords: async () => {67 throw new Error("vault locked");68 },69 }));70 const { handleGetCodexVaultData } = await import("./codex-handlers");71 const result = await handleGetCodexVaultData();72 expect(result.ok).toBe(false);73 expect(result.error).toMatch(/^VAULT_LOCKED/);74 });7576 it("handleEstimateCodexPages returns >=1 page for a real slotA blob", async () => {77 vi.doMock("./license", () => ({ isProUser: vi.fn(async () => true) }));78 vi.doMock("./vault", () => ({})); 79 80 81 storage.peach_vault_a = {82 version: 1,83 salt: "00".repeat(16),84 nonce: "00".repeat(12),85 86 ciphertext: "ab".repeat(1024),87 tag: "00".repeat(16),88 };89 storage.peach_vault_b = {90 version: 1,91 salt: "11".repeat(16),92 nonce: "11".repeat(12),93 ciphertext: "cd".repeat(1024),94 tag: "11".repeat(16),95 };96 const { handleEstimateCodexPages } = await import("./codex-handlers");97 const result = await handleEstimateCodexPages();98 expect(result.ok).toBe(true);99 expect(typeof result.pages).toBe("number");100 expect(result.pages!).toBeGreaterThanOrEqual(1);101 });102103 it("handleRestoreFromCodex rejects an invalid blob with INVALID_VAULT_BLOB", async () => {104 vi.doMock("./license", () => ({ isProUser: vi.fn(async () => false) }));105 const unlockFromCodex = vi.fn();106 vi.doMock("./vault", () => ({107 unlockFromCodex,108 isUnlocked: () => false,109 getEntries: () => [],110 }));111 vi.doMock("../shared/native", () => ({112 isNativePlatform: () => false,113 nativePlugin: () => null,114 }));115116 const { handleRestoreFromCodex } = await import("./codex-handlers");117 const result = await handleRestoreFromCodex({118 vaultBlob: "this-is-not-valid-base64url-json!!!",119 timestamp: 0,120 verifyCode: "PX-AAAA-BBBB",121 vaultFingerprint: "0".repeat(16),122 entryCount: 0,123 masterPassphrase: "ignored",124 });125 expect(result).toEqual({ ok: false, error: "INVALID_VAULT_BLOB" });126 127 128 expect(unlockFromCodex).not.toHaveBeenCalled();129 });130});