1import { describe, expect, it } from "vitest";23import { decodeSyncMessage, encodeSyncMessage, isSyncMessage } from "./sync-wire";4import type { SyncMessage } from "./sync-types";56describe("sync wire codec", () => {7 it("round-trips SYNC_VAULT_SEND through base64url JSON", () => {8 const message: SyncMessage = {9 type: "SYNC_VAULT_SEND",10 vault_a: "ciphertext-a",11 vault_b: "ciphertext-b",12 sent_at: 1_778_000_000_000,13 };1415 const encoded = encodeSyncMessage(message);1617 expect(encoded).toMatch(/^[A-Za-z0-9_-]+$/);18 expect(encoded).not.toMatch(/[+/=]/);19 expect(decodeSyncMessage(encoded)).toEqual(message);20 });2122 it("round-trips ACK / ERROR / pairing messages", () => {23 const messages: SyncMessage[] = [24 { type: "SYNC_VAULT_ACK", received_at: 42 },25 { type: "SYNC_ERROR", reason: "vault_locked" },26 {27 type: "PAIR_REQUEST",28 public_key_hex: "ab".repeat(32),29 device_name: "June's Laptop",30 },31 {32 type: "PAIR_ACCEPT",33 public_key_hex: "cd".repeat(32),34 device_name: "June's Phone",35 },36 { type: "PAIR_REJECT", reason: "user_declined" },37 ];3839 for (const message of messages) {40 expect(decodeSyncMessage(encodeSyncMessage(message))).toEqual(message);41 }42 });4344 it("validates message-specific required fields", () => {45 expect(46 isSyncMessage({47 type: "SYNC_VAULT_SEND",48 vault_a: "a",49 vault_b: "b",50 sent_at: 1,51 }),52 ).toBe(true);53 expect(54 isSyncMessage({55 type: "PAIR_ACCEPT",56 public_key_hex: "deadbeef",57 device_name: "Phone",58 }),59 ).toBe(true);6061 62 expect(63 isSyncMessage({ type: "SYNC_VAULT_SEND", vault_a: "a", vault_b: "b" }),64 ).toBe(false);65 66 expect(isSyncMessage({ type: "SYNC_VAULT_ACK", received_at: "now" })).toBe(false);67 68 expect(isSyncMessage({ type: "SYNC_ERROR" })).toBe(false);69 70 expect(isSyncMessage({ type: "PAIR_REQUEST", public_key_hex: "abc" })).toBe(false);71 72 expect(isSyncMessage({ type: "UNKNOWN" })).toBe(false);73 });7475 it("rejects malformed wire payloads with a stable error", () => {76 const malformedJson = btoa("{").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");77 const unknownType = btoa(JSON.stringify({ type: "BOGUS" }))78 .replace(/\+/g, "-")79 .replace(/\//g, "_")80 .replace(/=+$/g, "");8182 expect(() => decodeSyncMessage("@@@")).toThrow("invalid sync message");83 expect(() => decodeSyncMessage("A")).toThrow("invalid sync message");84 expect(() => decodeSyncMessage(malformedJson)).toThrow("invalid sync message");85 expect(() => decodeSyncMessage(unknownType)).toThrow("invalid sync message");86 });87});