import { describe, expect, it } from "vitest"; import { generatePassphrase, generatePassword } from "./generate"; describe("generatePassword", () => { it("produces a password of the requested length", () => { const pw = generatePassword({ length: 32 }); expect(pw).toHaveLength(32); }); it("omits forbidden characters from the symbol pool", () => { const pw = generatePassword({ length: 200, includeSymbols: true, forbiddenChars: "^*!@", }); for (const c of ["^", "*", "!", "@"]) { expect(pw).not.toContain(c); } }); it("omits forbidden characters from letter/number pools too", () => { // Defensive: if a site ever bans a digit, honor it. const pw = generatePassword({ length: 200, includeNumbers: true, includeSymbols: false, includeUppercase: false, forbiddenChars: "0", }); expect(pw).not.toContain("0"); }); it("falls back to a non-empty pool when every category is disabled", () => { const pw = generatePassword({ length: 16, includeUppercase: false, includeLowercase: false, includeNumbers: false, includeSymbols: false, }); expect(pw).toHaveLength(16); }); it("rejection sampling still yields exact length and only pool chars (audit INFO — modulo bias)", () => { // The rejection loop must terminate and never under/over-fill regardless of // how many CSPRNG draws land in the discarded high bucket. for (let i = 0; i < 50; i++) { const pw = generatePassword({ length: 64 }); expect(pw).toHaveLength(64); expect(pw).toMatch(/^[A-Za-z0-9!@#$%^&*]+$/); } }); }); describe("generatePassphrase", () => { it("defaults to five words separated by hyphens", () => { const passphrase = generatePassphrase(); expect(passphrase.split("-")).toHaveLength(5); }); it.each([4, 8, 12])("generates %i words", (wordCount) => { const passphrase = generatePassphrase({ wordCount, separator: " " }); expect(passphrase.split(" ")).toHaveLength(wordCount); }); it("supports the separator choices exposed by the UI", () => { for (const separator of ["-", " ", ".", "_"] as const) { expect(generatePassphrase({ wordCount: 4, separator }).split(separator)).toHaveLength(4); } }); });