import { describe, expect, it } from 'vitest' import { APP_SUBPROTOCOL, ContractDecodeError, TOKEN_SUBPROTOCOL_PREFIX, decodeBase64UrlBytes, encodeBase64UrlBytes, encodeBase64UrlString, decodeBase64UrlString, encodeTokenSubprotocol, extractTokenFromSubprotocols, } from '../src/index.js' describe('base64url (isomorphic)', () => { it('KAT: encodes "hello" to "aGVsbG8" (no padding)', () => { expect(encodeBase64UrlString('hello')).toBe('aGVsbG8') }) it('KAT: uses url-safe "-" and "_" instead of "+" and "/"', () => { expect(encodeBase64UrlBytes(Uint8Array.of(0xfb, 0xff))).toBe('-_8') }) it('round-trips arbitrary bytes', () => { const bytes = Uint8Array.from({ length: 40 }, (_, i) => (i * 37) & 0xff) expect([...decodeBase64UrlBytes(encodeBase64UrlBytes(bytes))]).toEqual([...bytes]) }) it('round-trips a unicode string', () => { const s = 'café-☕-relay' expect(decodeBase64UrlString(encodeBase64UrlString(s))).toBe(s) }) it('rejects non-alphabet characters', () => { expect(() => decodeBase64UrlBytes('abc*def')).toThrow(ContractDecodeError) }) }) describe('§4.3 FIX 5 token subprotocol transport', () => { it('encodeTokenSubprotocol prefixes with term.token. + base64url', () => { expect(encodeTokenSubprotocol('hello')).toBe(`${TOKEN_SUBPROTOCOL_PREFIX}aGVsbG8`) }) it('round-trips a raw token through the subprotocol list', () => { const raw = 'v4.public.aBcD_-123' const entry = encodeTokenSubprotocol(raw) expect(extractTokenFromSubprotocols([APP_SUBPROTOCOL, entry])).toBe(raw) }) it('returns null when no token entry is present', () => { expect(extractTokenFromSubprotocols([APP_SUBPROTOCOL])).toBeNull() }) it('ignores the app subprotocol and picks the token entry regardless of order', () => { const entry = encodeTokenSubprotocol('tok') expect(extractTokenFromSubprotocols([entry, APP_SUBPROTOCOL])).toBe('tok') }) it('throws when the token entry is malformed base64url', () => { expect(() => extractTokenFromSubprotocols([`${TOKEN_SUBPROTOCOL_PREFIX}not*valid`])).toThrow( ContractDecodeError, ) }) it('APP_SUBPROTOCOL is the single frozen value', () => { expect(APP_SUBPROTOCOL).toBe('term.relay.v1') }) })