diff --git a/test/test-client.test.ts b/test/test-client.test.ts new file mode 100644 index 0000000..064964b --- /dev/null +++ b/test/test-client.test.ts @@ -0,0 +1,136 @@ +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); + }); +});