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("decodes a base64url session key for the requested connection", async () => { const chromeMock = installChromeMock(); const sync = await import("./sync"); const promise = sync.getNativeSessionKey("conn-1"); expect(chromeMock.port.postMessage).toHaveBeenCalledWith({ type: "GET_SESSION_KEY", connection_id: "conn-1", }); // 32 bytes of 0x07 → URL-safe base64 with no padding. const expected = new Uint8Array(32).fill(7); let binary = ""; for (const byte of expected) binary += String.fromCharCode(byte); const key = btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, ""); chromeMock.emit({ type: "SESSION_KEY", connection_id: "conn-1", key }); await expect(promise).resolves.toEqual(expected); }); it("rejects a session key returned for the wrong connection", async () => { const chromeMock = installChromeMock(); const sync = await import("./sync"); const promise = sync.getNativeSessionKey("conn-1"); chromeMock.emit({ type: "SESSION_KEY", connection_id: "other", key: "AA".repeat(0), }); await expect(promise).rejects.toThrow("unexpected connection"); }); 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"); }); });