1import { describe, expect, it } from "vitest";2import { TOTP } from "otpauth";34import { generateTOTP, parseOtpauthURI } from "./totp";56789const SECRET = "JBSWY3DPEHPK3PXP";1011describe("generateTOTP (PCH-01-019)", () => {12 it("honours SHA256 / 8-digit / 60s params", () => {13 const { code, remaining } = generateTOTP(SECRET, "SHA256", 8, 60);14 const expected = new TOTP({ secret: SECRET, algorithm: "SHA256", digits: 8, period: 60 }).generate();15 expect(code).toBe(expected);16 expect(code.length).toBe(8);17 expect(remaining).toBeGreaterThan(0);18 expect(remaining).toBeLessThanOrEqual(60);19 });2021 it("legacy bare seed (no params) generates the SHA-1 / 6 / 30 default", () => {22 const { code, remaining } = generateTOTP(SECRET);23 const expected = new TOTP({ secret: SECRET, algorithm: "SHA1", digits: 6, period: 30 }).generate();24 expect(code).toBe(expected);25 expect(code.length).toBe(6);26 expect(remaining).toBeLessThanOrEqual(30);27 });2829 it("undefined optional args fall through to defaults", () => {30 const a = generateTOTP(SECRET, undefined, undefined, undefined);31 const b = generateTOTP(SECRET);32 expect(a.code).toBe(b.code);33 });34});3536describe("parseOtpauthURI (PCH-01-019)", () => {37 it("extracts algorithm, digits, and period", () => {38 const parsed = parseOtpauthURI(39 `otpauth://totp/Example:alice?secret=${SECRET}&algorithm=SHA256&digits=8&period=60&issuer=Example`,40 );41 expect(parsed).toMatchObject({42 secret: SECRET,43 algorithm: "SHA256",44 digits: 8,45 period: 60,46 });47 });4849 it("defaults a bare otpauth URI to SHA1 / 6 / 30", () => {50 const parsed = parseOtpauthURI(`otpauth://totp/Example:alice?secret=${SECRET}&issuer=Example`);51 expect(parsed).toMatchObject({ secret: SECRET, algorithm: "SHA1", digits: 6, period: 30 });52 });5354 it("returns null for non-otpauth input", () => {55 expect(parseOtpauthURI("not-a-uri")).toBeNull();56 });57});