import { describe, expect, test } from "bun:test"; import { TAuthClient, generateKeyPair } from "../src/index"; import z from "zod"; const appDataSchema = z.object({ name: z.string(), value: z.string(), }); function createClient(overrides?: Partial[0]>) { const keys = generateKeyPair(); return new TAuthClient({ frontendUrl: "https://tauth.example.com/login", identifier: "test.app", privateKey: keys.private, publicKey: keys.public, saveSession: () => {}, redirectUrl: "https://test.app/callback", appData: appDataSchema, httpServer: { port: 0, hostname: "localhost" }, ...overrides, }); } describe("TAuthClient", () => { test("constructor throws if saveSession is not a function", () => { const keys = generateKeyPair(); expect(() => { new TAuthClient({ frontendUrl: "https://tauth.example.com/login", identifier: "test.app", privateKey: keys.private, publicKey: keys.public, saveSession: "not-a-function" as any, redirectUrl: "https://test.app/callback", appData: appDataSchema, httpServer: { port: 0, hostname: "localhost" }, }); }).toThrow("saveSession must be provided as a function"); }); test("constructor sets properties correctly", () => { const keys = generateKeyPair(); const saveSession = () => {}; const client = createClient({ frontendUrl: "https://tauth.example.com/login", identifier: "my-app", privateKey: keys.private, publicKey: keys.public, saveSession, redirectUrl: "https://my-app.com/callback", }); expect(client.frontendUrl).toBe("https://tauth.example.com/login"); expect(client.identifier).toBe("my-app"); expect(client.privateKey).toBe(keys.private); expect(client.publicKey).toBe(keys.public); expect(client.saveSession).toBe(saveSession); expect(client.redirectUrl.toString()).toBe("https://my-app.com/callback"); }); test("constructor uses default httpServer when not provided", () => { const keys = generateKeyPair(); const client = new TAuthClient({ frontendUrl: "https://tauth.example.com/login", identifier: "test.app", privateKey: keys.private, publicKey: keys.public, saveSession: () => {}, redirectUrl: "https://test.app/callback", appData: appDataSchema, httpServer: { port: 0, hostname: "localhost" }, }); expect(client.httpServer).toBeDefined(); expect(client.httpServer.port).toBe(0); }); test("generateLink produces correct URL without challenge", () => { const client = Object.create(TAuthClient.prototype) as TAuthClient; client.frontendUrl = "https://tauth.example.com/login"; client.identifier = "test-app"; client.redirectUrl = new URL("https://test.app/callback"); const link = client.generateLink(); const parsed = new URL(link); expect(parsed.origin).toBe("https://tauth.example.com"); expect(parsed.pathname).toBe("/login"); expect(parsed.searchParams.get("identifier")).toBe("test-app"); expect(parsed.searchParams.get("redirect")).toBe("https://test.app/callback"); expect(parsed.searchParams.has("challenge")).toBe(false); }); test("generateLink includes challenge when provided", () => { const client = Object.create(TAuthClient.prototype) as TAuthClient; client.frontendUrl = "https://tauth.example.com/login"; client.identifier = "test-app"; client.redirectUrl = new URL("https://test.app/callback"); const link = client.generateLink("abc123"); const parsed = new URL(link); expect(parsed.searchParams.get("challenge")).toBe("abc123"); expect(parsed.searchParams.get("identifier")).toBe("test-app"); }); test("schema is created from appData", () => { const client = createClient(); expect(client.schema).toBeDefined(); expect(client.schema.identification).toBeDefined(); expect(client.schema.challenge_response).toBeDefined(); expect(client.schema.load_app_data).toBeDefined(); expect(client.schema.save_app_data).toBeDefined(); }); test("clientMap is initialized empty", () => { const client = createClient(); expect(client.clientMap.size).toBe(0); }); test("generateKeyPair returns valid base64 key pair", () => { const keys = generateKeyPair(); expect(keys.private).toBeDefined(); expect(keys.public).toBeDefined(); expect(typeof keys.private).toBe("string"); expect(typeof keys.public).toBe("string"); const privBuf = Buffer.from(keys.private, "base64"); const pubBuf = Buffer.from(keys.public, "base64"); expect(privBuf.length).toBe(56); expect(pubBuf.length).toBe(56); }); });