import { expect, test } from "bun:test"; import { decodeBase32, generateTotp, generateTotpSecret, totpUri, verifyTotp, } from "../src/totp/index.ts"; test("TOTP matches the RFC 6238 SHA-1 vector", async () => { const secret = "GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ"; expect(await generateTotp(secret, { timestamp: 59_000, digits: 8 })).toBe("94287082"); expect(await verifyTotp(secret, "94287082", { timestamp: 59_000, digits: 8, window: 0 })).toEqual( { valid: true, counter: 1 }, ); }); test("TOTP rejects malformed secrets, tokens, and unsafe options", async () => { expect(() => decodeBase32("JBSW0Y3P")).toThrow("Invalid base32 secret"); expect(() => decodeBase32("====")).toThrow("Invalid base32 secret"); expect(() => generateTotpSecret(() => new Uint8Array(19))).toThrow( "must return exactly 20 bytes", ); await expect(generateTotp("JBSWY3DPEHPK3PXP", { period: 0 })).rejects.toThrow("TOTP period"); expect(await verifyTotp("JBSWY3DPEHPK3PXP", "12ab56", { timestamp: 59_000 })).toEqual({ valid: false, }); }); test("TOTP URI validates and normalizes configuration", () => { const uri = totpUri({ issuer: " WorkRoot ", accountName: " user@example.com ", secret: "JBSW Y3DP-EHPK3PXP", }); expect(uri).toContain("secret=JBSWY3DPEHPK3PXP"); expect(uri).toContain("issuer=WorkRoot"); expect(() => totpUri({ issuer: "", accountName: "user@example.com", secret: "JBSWY3DPEHPK3PXP" }), ).toThrow("issuer and account name"); });