import { beforeEach, describe, expect, it, vi } from "vitest"; type MessageListener = (message: unknown) => void; type DisconnectListener = () => void; function installChromeMock() { const messageListeners: MessageListener[] = []; const disconnectListeners: DisconnectListener[] = []; const port = { postMessage: vi.fn(), disconnect: vi.fn(() => { for (const listener of disconnectListeners) listener(); }), onMessage: { addListener: vi.fn((listener: MessageListener) => { messageListeners.push(listener); }), }, onDisconnect: { addListener: vi.fn((listener: DisconnectListener) => { disconnectListeners.push(listener); }), }, }; const connectNative = vi.fn(() => port); vi.stubGlobal("chrome", { runtime: { connectNative, }, }); return { connectNative, port, emit(message: unknown) { for (const listener of messageListeners) listener(message); }, disconnect() { for (const listener of disconnectListeners) listener(); }, }; } describe("sync native host client", () => { beforeEach(() => { vi.resetModules(); vi.useRealTimers(); vi.unstubAllGlobals(); }); it("requests the native identity using the Rust host protocol", async () => { const chromeMock = installChromeMock(); const sync = await import("./sync"); const promise = sync.getNativeIdentity(); expect(chromeMock.connectNative).toHaveBeenCalledWith("com.peachpasswords.sync"); expect(chromeMock.port.postMessage).toHaveBeenCalledWith({ type: "GET_IDENTITY" }); chromeMock.emit({ type: "IDENTITY", public_key_hex: "aa".repeat(32) }); await expect(promise).resolves.toBe("aa".repeat(32)); }); it("sends trusted-device control messages with snake_case fields", async () => { const chromeMock = installChromeMock(); const sync = await import("./sync"); const promise = sync.trustDevice("bb".repeat(32), "June's iPhone"); expect(chromeMock.port.postMessage).toHaveBeenCalledWith({ type: "TRUST_DEVICE", public_key_hex: "bb".repeat(32), device_name: "June's iPhone", }); chromeMock.emit({ type: "TRUST_DEVICE_OK" }); await expect(promise).resolves.toBeUndefined(); }); it("maps native peer discovery events for subscribers", async () => { const chromeMock = installChromeMock(); const sync = await import("./sync"); const handler = vi.fn(); sync.connectSyncHost(); sync.onPeerDiscovered(handler); chromeMock.emit({ type: "PEER_DISCOVERED", peer: { host: "100.64.0.2", port: 49422, x25519_public_key: "remote-key", device_id: "device-id", name: "June's MacBook", pairing: true, }, }); expect(handler).toHaveBeenCalledWith({ host: "100.64.0.2", port: 49422, x25519PublicKey: "remote-key", deviceId: "device-id", name: "June's MacBook", pairing: true, }); }); it("never sends private key material in CONNECT requests", async () => { const chromeMock = installChromeMock(); const sync = await import("./sync"); const promise = sync.connectToPeer("100.64.0.2", 49422, "remote-key"); expect(chromeMock.port.postMessage).toHaveBeenCalledWith({ type: "CONNECT", host: "100.64.0.2", port: 49422, remote_x25519_public_key: "remote-key", local_x25519_private_key: "", }); chromeMock.emit({ type: "CONNECTED", connection_id: "conn-1" }); await expect(promise).resolves.toBe("conn-1"); }); it("rejects pending requests when the native host disconnects", async () => { const chromeMock = installChromeMock(); const sync = await import("./sync"); const promise = sync.getNativeStatus(); chromeMock.disconnect(); await expect(promise).rejects.toThrow("sync native host disconnected"); }); });