import { describe, expect, it } from "vitest"; import { decodeSyncMessage, encodeSyncMessage, isSyncMessage } from "./sync-wire"; import type { SyncMessage } from "./sync-types"; describe("sync wire codec", () => { it("round-trips sync messages through base64url JSON", () => { const message: SyncMessage = { type: "SYNC_HELLO", deviceId: "phone-\u2603", clockMs: 1_778_000_000_000, vaultVersion: 4, }; const encoded = encodeSyncMessage(message); expect(encoded).toMatch(/^[A-Za-z0-9_-]+$/); expect(encoded).not.toMatch(/[+/=]/); expect(decodeSyncMessage(encoded)).toEqual(message); }); it("round-trips manifest and encrypted entry payloads", () => { const messages: SyncMessage[] = [ { type: "SYNC_MANIFEST", entries: [{ id: "entry-1", modifiedAt: 42, deletedAt: 45, hash: "abc123" }], }, { type: "SYNC_ENTRIES", entries: [ { id: "entry-1", modifiedAt: 42, ciphertext: "ciphertext", nonce: "nonce", tag: "tag", }, ], }, ]; for (const message of messages) { expect(decodeSyncMessage(encodeSyncMessage(message))).toEqual(message); } }); it("validates message-specific required fields", () => { expect(isSyncMessage({ type: "SYNC_REQUEST", ids: ["a", "b"] })).toBe(true); expect(isSyncMessage({ type: "PAIR_ACCEPT", edPublicKey: "key", name: "Laptop" })).toBe( true ); expect(isSyncMessage({ type: "SYNC_REQUEST", ids: [7] })).toBe(false); expect(isSyncMessage({ type: "SYNC_HELLO", deviceId: "phone", clockMs: "now" })).toBe( false ); expect(isSyncMessage({ type: "SYNC_MANIFEST", entries: [{ id: "a" }] })).toBe(false); expect(isSyncMessage({ type: "UNKNOWN" })).toBe(false); }); it("rejects malformed wire payloads with a stable error", () => { const malformedJson = btoa("{").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, ""); const unknownType = btoa(JSON.stringify({ type: "BOGUS" })) .replace(/\+/g, "-") .replace(/\//g, "_") .replace(/=+$/g, ""); expect(() => decodeSyncMessage("@@@")).toThrow("invalid sync message"); expect(() => decodeSyncMessage("A")).toThrow("invalid sync message"); expect(() => decodeSyncMessage(malformedJson)).toThrow("invalid sync message"); expect(() => decodeSyncMessage(unknownType)).toThrow("invalid sync message"); }); });