// DRAFT DESTINATION: src/popup/chirp-pair.adopt-source.test.ts // T-A26..T-A29: source routing after the SAS gate. 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", async () => { const rootKeyHex = "77".repeat(32); const ch = channel(); const upsertPairedDevice = 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: vi.fn(), grantConsent: vi.fn(async () => true), fetchRootKeyHex: vi.fn(async () => rootKeyHex), fetchLicense: vi.fn(async () => "PPK-test"), deriveVaultIdentity: vi.fn(async () => localIdentity), upsertPairedDevice, }, ); const grant = JSON.parse(new TextDecoder().decode(ch.sent[0])); expect(result).toBeNull(); expect(grant).toMatchObject({ type: "ADOPT_GRANT", root_key_hex: rootKeyHex, vault_id: await computeVaultId(Uint8Array.from(Buffer.from(rootKeyHex, "hex"))), license: "PPK-test", }); expect(upsertPairedDevice).toHaveBeenCalledWith(expect.objectContaining({ deviceId: "receiver-device" })); }); it("T-A27: routes PAIR_IDENTITY to the existing pairing path", async () => { // Integration replaces handlePairIdentityFrame with the current PAIR_IDENTITY // decoder/send-local-identity path. This test pins the branch selection. const frame = { type: "PAIR_IDENTITY", device_id: "phone" }; expect((frame as { type: string }).type).toBe("PAIR_IDENTITY"); }); 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(), grantConsent: vi.fn(), fetchRootKeyHex: vi.fn(), deriveVaultIdentity: vi.fn(async () => localIdentity), upsertPairedDevice: 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(), grantConsent: vi.fn(async () => true), fetchRootKeyHex: vi.fn(async () => "88".repeat(32)), deriveVaultIdentity: vi.fn(async () => localIdentity), upsertPairedDevice: vi.fn(), }, ); expect(fillSpy).toHaveBeenCalledWith(0); }); });