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 { getOrCreateDeviceId } = await import("./device-identity");5657 const id = await getOrCreateDeviceId();5859 expect(typeof id).toBe("string");60 expect(id.length).toBeGreaterThan(0);61 expect(local[DEVICE_ID_KEY]).toBe(id);62 expect(syncSet).not.toHaveBeenCalled();63 });6465 it("returns_a_stable_id_across_calls", async () => {66 const { getOrCreateDeviceId } = await import("./device-identity");6768 const first = await getOrCreateDeviceId();69 const second = await getOrCreateDeviceId();7071 expect(second).toBe(first);72 });7374 it("replaces_a_copied_local_id_when_adoption_reports_a_collision", async () => {75 local[DEVICE_ID_KEY] = "copied-browser-id";76 const { getOrCreateDeviceId, rotateDeviceId } = await import("./device-identity");7778 const fresh = await rotateDeviceId();7980 expect(fresh).not.toBe("copied-browser-id");81 expect(local[DEVICE_ID_KEY]).toBe(fresh);82 expect(await getOrCreateDeviceId()).toBe(fresh);83 expect(syncSet).not.toHaveBeenCalled();84 });85});