/** * Unit tests for passkey-helpers utility functions */ import type { PasskeyRow } from "../../utils/passkey-helpers.js"; import { describe, expect, test } from "bun:test"; import { base64urlToUint8Array, formatPasskeyDate, parsePasskeyRow, uint8ArrayToBase64url, } from "../../utils/passkey-helpers.js"; describe("base64urlToUint8Array", () => { test("converts base64url string to Uint8Array", () => { // "Hello" in base64url const base64url = "SGVsbG8"; const result = base64urlToUint8Array(base64url); expect(result).toBeInstanceOf(Uint8Array); expect(Buffer.from(result).toString()).toBe("Hello"); }); test("handles empty string", () => { const result = base64urlToUint8Array(""); expect(result).toHaveLength(0); }); test("handles base64url without padding", () => { // "abc" in base64url (no padding needed) const base64url = "YWJj"; const result = base64urlToUint8Array(base64url); expect(Buffer.from(result).toString()).toBe("abc"); }); test("handles binary data with special characters", () => { // Binary data that would have + and / in standard base64 const original = new Uint8Array([0xff, 0xfe, 0xfd, 0xfc]); const base64url = Buffer.from(original).toString("base64url"); const result = base64urlToUint8Array(base64url); expect(result).toEqual(original); }); }); describe("uint8ArrayToBase64url", () => { test("converts Uint8Array to base64url string", () => { // "Hello" as bytes const bytes = new Uint8Array([72, 101, 108, 108, 111]); const result = uint8ArrayToBase64url(bytes); expect(result).toBe("SGVsbG8"); }); test("handles empty array", () => { const result = uint8ArrayToBase64url(new Uint8Array([])); expect(result).toBe(""); }); test("produces URL-safe output (no +, /, or =)", () => { // Binary data that would produce + and / in standard base64 const bytes = new Uint8Array([0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa]); const result = uint8ArrayToBase64url(bytes); expect(result).not.toContain("+"); expect(result).not.toContain("/"); expect(result).not.toContain("="); }); test("roundtrips correctly with base64urlToUint8Array", () => { const original = new Uint8Array([1, 2, 3, 4, 5, 100, 200, 255]); const encoded = uint8ArrayToBase64url(original); const decoded = base64urlToUint8Array(encoded); expect(decoded).toEqual(original); }); }); describe("parsePasskeyRow", () => { const createMockRow = (overrides: Partial = {}): PasskeyRow => ({ id: 1, user_id: 100, credential_id: new Uint8Array([1, 2, 3, 4, 5]), public_key: new Uint8Array([10, 20, 30, 40, 50]), webauthn_user_id: "webauthn-user-123", counter: "42", device_type: "multiDevice", backup_eligible: true, backup_status: true, transports: ["internal", "hybrid"], rpid: "localhost", name: "Test Passkey", last_used_at: new Date("2024-01-15T10:00:00Z"), created_at: new Date("2024-01-01T00:00:00Z"), ...overrides, }); test("converts DB row to ParsedPasskey format", () => { const row = createMockRow(); const result = parsePasskeyRow(row); expect(result.id).toBe(1); expect(result.credentialId).toBe(uint8ArrayToBase64url(row.credential_id)); expect(result.publicKey).toBeInstanceOf(Uint8Array); expect(result.counter).toBe(42); expect(result.deviceType).toBe("multiDevice"); expect(result.backupEligible).toBe(true); expect(result.backupStatus).toBe(true); expect(result.transports).toEqual(["internal", "hybrid"]); expect(result.rpid).toBe("localhost"); expect(result.name).toBe("Test Passkey"); }); test("handles string counter", () => { const row = createMockRow({ counter: "100" }); const result = parsePasskeyRow(row); expect(result.counter).toBe(100); }); test("handles number counter", () => { const row = createMockRow({ counter: 200 }); const result = parsePasskeyRow(row); expect(result.counter).toBe(200); }); test("handles bigint counter", () => { const row = createMockRow({ counter: BigInt(300) }); const result = parsePasskeyRow(row); expect(result.counter).toBe(300); }); test("handles null transports", () => { const row = createMockRow({ transports: null }); const result = parsePasskeyRow(row); expect(result.transports).toBeNull(); }); test("handles null last_used_at", () => { const row = createMockRow({ last_used_at: null }); const result = parsePasskeyRow(row); expect(result.lastUsedAt).toBeNull(); }); test("preserves created_at date", () => { const createdAt = new Date("2024-06-15T12:00:00Z"); const row = createMockRow({ created_at: createdAt }); const result = parsePasskeyRow(row); expect(result.createdAt).toEqual(createdAt); }); test("handles singleDevice device type", () => { const row = createMockRow({ device_type: "singleDevice" }); const result = parsePasskeyRow(row); expect(result.deviceType).toBe("singleDevice"); }); }); describe("formatPasskeyDate", () => { test("formats date with month, day, year, and time", () => { const date = new Date("2024-01-15T10:30:00"); const result = formatPasskeyDate(date); // Should contain month abbreviation expect(result).toContain("Jan"); // Should contain day expect(result).toContain("15"); // Should contain year expect(result).toContain("2024"); }); test("includes time component", () => { const date = new Date("2024-06-20T14:45:00"); const result = formatPasskeyDate(date); // Should contain time in some format expect(result).toMatch(/\d{1,2}:\d{2}/); }); test("handles different months", () => { const dates = [ { date: new Date("2024-03-01"), month: "Mar" }, { date: new Date("2024-07-15"), month: "Jul" }, { date: new Date("2024-12-31"), month: "Dec" }, ]; for (const { date, month } of dates) { const result = formatPasskeyDate(date); expect(result).toContain(month); } }); });