import { describe, expect, it } from "vitest"; import { padSyncFrame, syncFrameBucket, unpadSyncFrame, } from "./sync-padding"; describe("sync frame padding (E7)", () => { it("round-trips payloads of every shape, including empty", () => { for (const size of [0, 1, 5, 251, 252, 253, 1000, 70_000]) { const payload = Uint8Array.from({ length: size }, (_, i) => (i * 31) & 0xff); expect(Array.from(unpadSyncFrame(padSyncFrame(payload)))).toEqual( Array.from(payload), ); } }); it("collapses every small control frame to one indistinguishable size", () => { // The point of the exercise: a relay must not be able to tell a HELLO from // a REQUEST from a DONE by looking at the frame it is forwarding. const hello = new TextEncoder().encode('{"type":"SYNC_HELLO","vaultId":"' + "a".repeat(64) + '"}'); const done = new TextEncoder().encode('{"type":"SYNC_DONE"}'); expect(padSyncFrame(hello).length).toBe(padSyncFrame(done).length); expect(padSyncFrame(done).length).toBe(256); }); it("pins the bucket ladder — cross-platform KAT, must match peach-android", () => { const cases: Array<[number, number]> = [ [0, 256], [252, 256], [253, 512], [1024, 2048], [65_532, 65_536], [65_533, 131_072], [200_000, 262_144], ]; for (const [payload, expected] of cases) { expect(syncFrameBucket(4 + payload)).toBe(expected); expect(padSyncFrame(new Uint8Array(payload)).length).toBe(expected); } }); it("keeps large-frame overhead bounded rather than doubling", () => { // Doubling a 12 MiB frame would exceed the 24 MiB transport cap for no // extra privacy, so buckets go linear above 64 KiB. const twelveMiB = 12 * 1024 * 1024; const padded = syncFrameBucket(twelveMiB); expect(padded).toBeGreaterThanOrEqual(twelveMiB); expect(padded - twelveMiB).toBeLessThan(64 * 1024); }); it("rejects a frame whose declared length exceeds what it carries", () => { const forged = new Uint8Array(256); new DataView(forged.buffer).setUint32(0, 9999, false); expect(() => unpadSyncFrame(forged)).toThrow(/more payload than it carries/); }); it("rejects a frame shorter than its length header", () => { expect(() => unpadSyncFrame(new Uint8Array(3))).toThrow(/length header/); }); });