import { describe, expect, it, vi } from 'vitest' import { encodePairingOffer, decodePairingOffer, parsePairingCode, type PairingOffer } from './pairing' import { PAIRING_INPUT_MAX_CHARACTERS } from './mobile-pairing-protocol-limits' describe('pairing offer', () => { const offer: PairingOffer = { v: 2, endpoint: 'ws://192.168.1.10:6768', deviceToken: 'abcdef1234567890abcdef1234567890abcdef1234567890', publicKeyB64: 'dGVzdC1wdWJsaWMta2V5LWJhc2U2NC1lbmNvZGVk' } it('encode then decode round-trips correctly', () => { const url = encodePairingOffer(offer) expect(url).toMatch(/^orca:\/\/pair\?code=/) const decoded = decodePairingOffer(url) expect(decoded).toEqual(offer) }) it('decodes in browser runtimes without Buffer', () => { const url = encodePairingOffer(offer) const nodeBuffer = Buffer let decoded: PairingOffer | null = null try { vi.stubGlobal('Buffer', undefined) decoded = decodePairingOffer(url) } finally { vi.stubGlobal('Buffer', nodeBuffer) } expect(decoded).toEqual(offer) }) it('preserves partial padding consistently across Node and browser runtimes', () => { const url = encodePairingOffer(offer) const code = new URLSearchParams(url.slice(url.indexOf('?') + 1)).get('code')! expect(code.length % 4).toBe(2) const malformedCode = `${code}=` const nodeDecoded = parsePairingCode(malformedCode) const nodeBuffer = Buffer let browserDecoded: PairingOffer | null = null try { vi.stubGlobal('Buffer', undefined) browserDecoded = parsePairingCode(malformedCode) } finally { vi.stubGlobal('Buffer', nodeBuffer) } expect({ nodeDecoded, browserDecoded }).toEqual({ nodeDecoded: offer, browserDecoded: offer }) }) it('preserves non-zero pad bits consistently across Node and browser runtimes', () => { const url = encodePairingOffer(offer) const code = new URLSearchParams(url.slice(url.indexOf('?') + 1)).get('code')! expect(code.length % 4).toBe(2) const alias = `${code.slice(0, -1)}${base64Alias(code.at(-1)!)}` const nodeDecoded = parsePairingCode(alias) const nodeBuffer = Buffer let browserDecoded: PairingOffer | null = null try { vi.stubGlobal('Buffer', undefined) browserDecoded = parsePairingCode(alias) } finally { vi.stubGlobal('Buffer', nodeBuffer) } expect({ nodeDecoded, browserDecoded }).toEqual({ nodeDecoded: offer, browserDecoded: offer }) }) it('accepts canonical base64 padding', () => { const url = encodePairingOffer(offer) const code = new URLSearchParams(url.slice(url.indexOf('?') + 1)).get('code')! expect(code.length % 4).toBe(2) expect(parsePairingCode(`${code}==`)).toEqual(offer) }) it('preserves optional device scope metadata', () => { const scopedOffer: PairingOffer = { ...offer, scope: 'mobile' } expect(decodePairingOffer(encodePairingOffer(scopedOffer))).toEqual(scopedOffer) }) it('round-trips a TLS reverse-proxy endpoint with an explicit port and path', () => { const proxiedOffer = { ...offer, endpoint: 'wss://proxy.example:443/orca/runtime' } expect(decodePairingOffer(encodePairingOffer(proxiedOffer))).toEqual(proxiedOffer) }) it('encoded URL uses base64url (no +, /, or = characters)', () => { const url = encodePairingOffer(offer) const code = new URLSearchParams(url.slice(url.indexOf('?') + 1)).get('code')! expect(code).not.toMatch(/[+/=]/) }) it('rejects URLs with wrong scheme', () => { expect(() => decodePairingOffer('https://example.com#abc')).toThrow('Invalid pairing URL') }) it('rejects orca URLs outside the exact pairing route', () => { const url = encodePairingOffer(offer) const code = new URLSearchParams(url.slice(url.indexOf('?') + 1)).get('code')! expect(parsePairingCode(`orca://pairing?code=${code}`)).toBeNull() expect(parsePairingCode(`orca://pair-extra?code=${code}`)).toBeNull() expect(() => decodePairingOffer(`orca://pairing?code=${code}`)).toThrow('Invalid pairing URL') }) it('rejects URLs without a pairing code', () => { expect(() => decodePairingOffer('orca://pair')).toThrow('Invalid pairing URL') }) it('decodes legacy hash URLs', () => { const url = encodePairingOffer(offer) const code = new URLSearchParams(url.slice(url.indexOf('?') + 1)).get('code')! expect(decodePairingOffer(`orca://pair#${code}`)).toEqual(offer) }) it('rejects oversized whole URLs before parsing', () => { const url = encodePairingOffer(offer) const oversizedUrl = `${url}&ignored=${'A'.repeat(PAIRING_INPUT_MAX_CHARACTERS)}` expect(() => decodePairingOffer(oversizedUrl)).toThrow( 'Invalid pairing URL: pairing code exceeds safe size' ) }) it('rejects payloads with missing fields', () => { const partial = { v: 2, endpoint: 'ws://host:1234' } const base64 = Buffer.from(JSON.stringify(partial)).toString('base64') expect(() => decodePairingOffer(`orca://pair#${base64}`)).toThrow() }) it('rejects payloads with wrong version', () => { const wrong = { ...offer, v: 1 } const base64 = Buffer.from(JSON.stringify(wrong)).toString('base64') expect(() => decodePairingOffer(`orca://pair#${base64}`)).toThrow() }) it('rejects payloads with missing publicKeyB64', () => { const wrong = { v: 2, endpoint: 'ws://host:1234', deviceToken: 'tok' } const base64 = Buffer.from(JSON.stringify(wrong)).toString('base64') expect(() => decodePairingOffer(`orca://pair#${base64}`)).toThrow() }) }) describe('parsePairingCode', () => { const offer: PairingOffer = { v: 2, endpoint: 'ws://192.168.1.10:6768', deviceToken: 'token-abc', publicKeyB64: 'pubkey-xyz' } it('parses a full orca://pair# URL', () => { const url = encodePairingOffer(offer) expect(parsePairingCode(url)).toEqual(offer) }) it('parses a bare base64url payload (without scheme prefix)', () => { const url = encodePairingOffer(offer) const base64url = new URLSearchParams(url.slice(url.indexOf('?') + 1)).get('code')! expect(parsePairingCode(base64url)).toEqual(offer) }) it('tolerates surrounding whitespace from clipboard', () => { const url = encodePairingOffer(offer) expect(parsePairingCode(` ${url}\n`)).toEqual(offer) }) it('rejects oversized whole raw input before trimming', () => { const url = encodePairingOffer(offer) const code = new URLSearchParams(url.slice(url.indexOf('?') + 1)).get('code')! expect(parsePairingCode(`${code}${' '.repeat(PAIRING_INPUT_MAX_CHARACTERS)}`)).toBeNull() }) it('returns null for empty input', () => { expect(parsePairingCode('')).toBeNull() expect(parsePairingCode(' ')).toBeNull() }) it('returns null for garbage input', () => { expect(parsePairingCode('not a pairing code')).toBeNull() expect(parsePairingCode('https://example.com')).toBeNull() }) it('returns null for valid base64 of unrelated JSON', () => { const bogus = Buffer.from(JSON.stringify({ hello: 'world' })).toString('base64') expect(parsePairingCode(bogus)).toBeNull() }) }) function base64Alias(character: string): string { const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_' return alphabet[alphabet.indexOf(character) + 1]! }