sync-wire.test.ts

2.4 KB · raw

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 messages through base64url JSON", () => {8    const message: SyncMessage = {9      type: "SYNC_HELLO",10      deviceId: "phone-\u2603",11      clockMs: 1_778_000_000_000,12      vaultVersion: 4,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 manifest and encrypted entry payloads", () => {23    const messages: SyncMessage[] = [24      {25        type: "SYNC_MANIFEST",26        entries: [{ id: "entry-1", modifiedAt: 42, deletedAt: 45, hash: "abc123" }],27      },28      {29        type: "SYNC_ENTRIES",30        entries: [31          {32            id: "entry-1",33            modifiedAt: 42,34            ciphertext: "ciphertext",35            nonce: "nonce",36            tag: "tag",37          },38        ],39      },40    ];4142    for (const message of messages) {43      expect(decodeSyncMessage(encodeSyncMessage(message))).toEqual(message);44    }45  });4647  it("validates message-specific required fields", () => {48    expect(isSyncMessage({ type: "SYNC_REQUEST", ids: ["a", "b"] })).toBe(true);49    expect(isSyncMessage({ type: "PAIR_ACCEPT", edPublicKey: "key", name: "Laptop" })).toBe(50      true51    );52    expect(isSyncMessage({ type: "SYNC_REQUEST", ids: [7] })).toBe(false);53    expect(isSyncMessage({ type: "SYNC_HELLO", deviceId: "phone", clockMs: "now" })).toBe(54      false55    );56    expect(isSyncMessage({ type: "SYNC_MANIFEST", entries: [{ id: "a" }] })).toBe(false);57    expect(isSyncMessage({ type: "UNKNOWN" })).toBe(false);58  });5960  it("rejects malformed wire payloads with a stable error", () => {61    const malformedJson = btoa("{").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/g, "");62    const unknownType = btoa(JSON.stringify({ type: "BOGUS" }))63      .replace(/\+/g, "-")64      .replace(/\//g, "_")65      .replace(/=+$/g, "");6667    expect(() => decodeSyncMessage("@@@")).toThrow("invalid sync message");68    expect(() => decodeSyncMessage("A")).toThrow("invalid sync message");69    expect(() => decodeSyncMessage(malformedJson)).toThrow("invalid sync message");70    expect(() => decodeSyncMessage(unknownType)).toThrow("invalid sync message");71  });72});