import { describe, expect, it, vi } from "vitest"; import { computeVaultId } from "../shared/sync-identity"; import { routeSourcePostSASFrame } from "./chirp-pair"; import type { LocalIdentity, PairingTransport } from "../shared/chirp-handshake"; const localIdentity: LocalIdentity = { deviceId: "source-device", deviceName: "Source", deviceType: "extension", longTermPublicKeyHex: "ab".repeat(32), signingPublicKeyHex: "cd".repeat(32), }; function channel(): PairingTransport & { sent: Uint8Array[]; close: ReturnType } { const sent: Uint8Array[] = []; return { sent, close: vi.fn(async () => {}), async send(msg) { sent.push(msg); }, async receive() { return new TextEncoder().encode(JSON.stringify({ type: "ADOPT_IDENTITY", device_id: "receiver-device", device_name: "Receiver", device_type: "extension", long_term_public_key_hex: "ef".repeat(32), })); }, }; } describe("source-side ADOPT_REQUEST routing", () => { it("T-A26: routes ADOPT_REQUEST to grantAdoption after SAS confirmation and consent", async () => { const rootKeyHex = "77".repeat(32); const ch = channel(); const completePairing = vi.fn(); const result = await routeSourcePostSASFrame( { sas: "895245", frame: { type: "ADOPT_REQUEST", device_id: "receiver-ephemeral", device_name: "Receiver", device_type: "extension", }, channel: ch, }, localIdentity, { confirmSAS: vi.fn(async () => true), completePairing, grantAdoptionConsent: vi.fn(async () => true), fetchAdoptionMaterial: vi.fn(async () => ({ rootKeyHex, vaultId: await computeVaultId(Uint8Array.from(Buffer.from(rootKeyHex, "hex"))), license: "PPK-test", })), onProgress: vi.fn(), }, ); const grant = JSON.parse(new TextDecoder().decode(ch.sent[0])); expect(result).toEqual(expect.objectContaining({ deviceId: "receiver-device" })); expect(grant).toMatchObject({ type: "ADOPT_GRANT", root_key_hex: rootKeyHex, vault_id: await computeVaultId(Uint8Array.from(Buffer.from(rootKeyHex, "hex"))), license: "PPK-test", }); expect(completePairing).toHaveBeenCalledWith(expect.objectContaining({ deviceId: "receiver-device" })); }); it("T-A27: routes PAIR_IDENTITY to the existing pairing path", async () => { const ch = channel(); const completePairing = vi.fn(); const result = await routeSourcePostSASFrame( { sas: "895245", frame: { type: "PAIR_IDENTITY", device_id: "phone", device_name: "Phone", device_type: "android", long_term_public_key_hex: "aa".repeat(32), }, channel: ch, }, localIdentity, { confirmSAS: vi.fn(async () => true), completePairing, onProgress: vi.fn(), }, ); const response = JSON.parse(new TextDecoder().decode(ch.sent[0])); expect(response.type).toBe("PAIR_IDENTITY"); expect(result).toEqual(expect.objectContaining({ deviceId: "phone" })); expect(completePairing).toHaveBeenCalledWith(expect.objectContaining({ deviceId: "phone" })); }); it("T-A28: unknown frame closes the channel gracefully", async () => { const ch = channel(); await expect(routeSourcePostSASFrame( { sas: "895245", frame: { type: "WHAT_IS_THIS" }, channel: ch }, localIdentity, { confirmSAS: vi.fn(async () => true), completePairing: vi.fn(), onProgress: vi.fn(), }, )).rejects.toThrow("unexpected_pairing_frame"); expect(ch.close).toHaveBeenCalledTimes(1); }); it("T-A29: source root key bytes are zeroed after grantAdoption regardless of outcome", async () => { const fillSpy = vi.spyOn(Uint8Array.prototype, "fill"); await routeSourcePostSASFrame( { sas: "895245", frame: { type: "ADOPT_REQUEST", device_id: "receiver-ephemeral", device_name: "Receiver", device_type: "extension", }, channel: channel(), }, localIdentity, { confirmSAS: vi.fn(async () => true), completePairing: vi.fn(), grantAdoptionConsent: vi.fn(async () => true), fetchAdoptionMaterial: vi.fn(async () => ({ rootKeyHex: "88".repeat(32), vaultId: await computeVaultId(Uint8Array.from(Buffer.from("88".repeat(32), "hex"))), })), onProgress: vi.fn(), }, ); expect(fillSpy).toHaveBeenCalledWith(0); }); });