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("migrates_legacy_sync_id_to_local_and_removes_from_sync", async () => {55 sync[DEVICE_ID_KEY] = "legacy-roamed-id";56 const { getOrCreateDeviceId } = await import("./device-identity");5758 const id = await getOrCreateDeviceId();5960 expect(id).toBe("legacy-roamed-id");61 expect(local[DEVICE_ID_KEY]).toBe("legacy-roamed-id");62 expect(syncRemove).toHaveBeenCalledWith(DEVICE_ID_KEY);63 expect(sync[DEVICE_ID_KEY]).toBeUndefined();64 });6566 it("fresh_install_writes_id_to_local_only", async () => {67 const { getOrCreateDeviceId } = await import("./device-identity");6869 const id = await getOrCreateDeviceId();7071 expect(typeof id).toBe("string");72 expect(id.length).toBeGreaterThan(0);73 expect(local[DEVICE_ID_KEY]).toBe(id);74 expect(syncSet).not.toHaveBeenCalled();75 });7677 it("returns_a_stable_id_across_calls", async () => {78 const { getOrCreateDeviceId } = await import("./device-identity");7980 const first = await getOrCreateDeviceId();81 const second = await getOrCreateDeviceId();8283 expect(second).toBe(first);84 });85});