1import { describe, expect, it } from "vitest";2import { generatePassword } from "./generate";34describe("generatePassword", () => {5 it("produces a password of the requested length", () => {6 const pw = generatePassword({ length: 32 });7 expect(pw).toHaveLength(32);8 });910 it("omits forbidden characters from the symbol pool", () => {11 const pw = generatePassword({12 length: 200,13 includeSymbols: true,14 forbiddenChars: "^*!@",15 });16 for (const c of ["^", "*", "!", "@"]) {17 expect(pw).not.toContain(c);18 }19 });2021 it("omits forbidden characters from letter/number pools too", () => {22 23 const pw = generatePassword({24 length: 200,25 includeNumbers: true,26 includeSymbols: false,27 includeUppercase: false,28 forbiddenChars: "0",29 });30 expect(pw).not.toContain("0");31 });3233 it("falls back to a non-empty pool when every category is disabled", () => {34 const pw = generatePassword({35 length: 16,36 includeUppercase: false,37 includeLowercase: false,38 includeNumbers: false,39 includeSymbols: false,40 });41 expect(pw).toHaveLength(16);42 });4344 it("rejection sampling still yields exact length and only pool chars (audit INFO — modulo bias)", () => {45 46 47 for (let i = 0; i < 50; i++) {48 const pw = generatePassword({ length: 64 });49 expect(pw).toHaveLength(64);50 expect(pw).toMatch(/^[A-Za-z0-9!@#$%^&*]+$/);51 }52 });53});