sync-padding.test.ts

2.3 KB · raw

1import { describe, expect, it } from "vitest";2import {3  padSyncFrame,4  syncFrameBucket,5  unpadSyncFrame,6} from "./sync-padding";78describe("sync frame padding (E7)", () => {9  it("round-trips payloads of every shape, including empty", () => {10    for (const size of [0, 1, 5, 251, 252, 253, 1000, 70_000]) {11      const payload = Uint8Array.from({ length: size }, (_, i) => (i * 31) & 0xff);12      expect(Array.from(unpadSyncFrame(padSyncFrame(payload)))).toEqual(13        Array.from(payload),14      );15    }16  });1718  it("collapses every small control frame to one indistinguishable size", () => {19    // The point of the exercise: a relay must not be able to tell a HELLO from20    // a REQUEST from a DONE by looking at the frame it is forwarding.21    const hello = new TextEncoder().encode('{"type":"SYNC_HELLO","vaultId":"' + "a".repeat(64) + '"}');22    const done = new TextEncoder().encode('{"type":"SYNC_DONE"}');23    expect(padSyncFrame(hello).length).toBe(padSyncFrame(done).length);24    expect(padSyncFrame(done).length).toBe(256);25  });2627  it("pins the bucket ladder — cross-platform KAT, must match peach-android", () => {28    const cases: Array<[number, number]> = [29      [0, 256],30      [252, 256],31      [253, 512],32      [1024, 2048],33      [65_532, 65_536],34      [65_533, 131_072],35      [200_000, 262_144],36    ];37    for (const [payload, expected] of cases) {38      expect(syncFrameBucket(4 + payload)).toBe(expected);39      expect(padSyncFrame(new Uint8Array(payload)).length).toBe(expected);40    }41  });4243  it("keeps large-frame overhead bounded rather than doubling", () => {44    // Doubling a 12 MiB frame would exceed the 24 MiB transport cap for no45    // extra privacy, so buckets go linear above 64 KiB.46    const twelveMiB = 12 * 1024 * 1024;47    const padded = syncFrameBucket(twelveMiB);48    expect(padded).toBeGreaterThanOrEqual(twelveMiB);49    expect(padded - twelveMiB).toBeLessThan(64 * 1024);50  });5152  it("rejects a frame whose declared length exceeds what it carries", () => {53    const forged = new Uint8Array(256);54    new DataView(forged.buffer).setUint32(0, 9999, false);55    expect(() => unpadSyncFrame(forged)).toThrow(/more payload than it carries/);56  });5758  it("rejects a frame shorter than its length header", () => {59    expect(() => unpadSyncFrame(new Uint8Array(3))).toThrow(/length header/);60  });61});