1import { beforeEach, describe, expect, it, vi } from "vitest";23456type StorageShape = Record<string, unknown>;7const DEVICE_ID_KEY = "peach_device_id";89describe("device-identity getOrCreateDeviceId (PCH-01-004)", () => {10 let local: StorageShape;11 let sync: StorageShape;12 let localSet: ReturnType<typeof vi.fn>;13 let syncSet: ReturnType<typeof vi.fn>;14 let syncRemove: ReturnType<typeof vi.fn>;1516 function makeArea(store: StorageShape, set: ReturnType<typeof vi.fn>, remove?: ReturnType<typeof vi.fn>) {17 return {18 get: vi.fn(async (key?: string | string[]) => {19 if (!key) return store;20 if (typeof key === "string") return { [key]: store[key] };21 return key.reduce<Record<string, unknown>>((r, k) => {22 r[k] = store[k];23 return r;24 }, {});25 }),26 set,27 remove: remove ?? vi.fn(async () => undefined),28 };29 }3031 beforeEach(() => {32 vi.resetModules();33 local = {};34 sync = {};35 localSet = vi.fn(async (next: Record<string, unknown>) => {36 Object.assign(local, next);37 });38 syncSet = vi.fn(async (next: Record<string, unknown>) => {39 Object.assign(sync, next);40 });41 syncRemove = vi.fn(async (key: string) => {42 delete sync[key];43 });4445 const chromeMock = {46 storage: {47 local: makeArea(local, localSet),48 sync: makeArea(sync, syncSet, syncRemove),49 },50 } as unknown as typeof chrome;51 (globalThis as { chrome?: typeof chrome }).chrome = chromeMock;52 });5354 it("fresh_install_writes_id_to_local_only", async () => {55 const vault = await import("./vault");56 await vault.createVault("identity-test-password");57 const { getOrCreateDeviceId } = await import("./device-identity");5859 const id = await getOrCreateDeviceId();6061 expect(typeof id).toBe("string");62 expect(id.length).toBeGreaterThan(0);63 expect(local[DEVICE_ID_KEY]).toBeUndefined();64 expect(local["peach_local_identity_v2"]).toBeDefined();65 expect(syncSet).not.toHaveBeenCalled();66 });6768 it("returns_a_stable_id_across_calls", async () => {69 const vault = await import("./vault");70 await vault.createVault("identity-test-password");71 const { getOrCreateDeviceId } = await import("./device-identity");7273 const first = await getOrCreateDeviceId();74 const second = await getOrCreateDeviceId();7576 expect(second).toBe(first);77 });7879 it("replaces_a_copied_local_id_when_adoption_reports_a_collision", async () => {80 const vault = await import("./vault");81 await vault.createVault("identity-test-password");82 const { getOrCreateDeviceId, rotateDeviceId } = await import("./device-identity");8384 const original = await getOrCreateDeviceId();85 const fresh = await rotateDeviceId();8687 expect(fresh).not.toBe(original);88 expect(local["peach_local_identity_v2"]).toBeDefined();89 expect(await getOrCreateDeviceId()).toBe(fresh);90 expect(syncSet).not.toHaveBeenCalled();91 });92});