136 lines
4.6 KiB
TypeScript
136 lines
4.6 KiB
TypeScript
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<ConstructorParameters<typeof TAuthClient>[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);
|
|
});
|
|
});
|