1import { describe, expect, it } from "vitest";2import { generatePassword, generateSmartPassword } 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 });43});4445describe("generateSmartPassword", () => {46 it("clamps to policy.maxLength", () => {47 const pw = generateSmartPassword({ maxLength: 12 });48 expect(pw.length).toBeLessThanOrEqual(12);49 });5051 it("raises to policy.minLength", () => {52 const pw = generateSmartPassword({ minLength: 32 });53 expect(pw.length).toBeGreaterThanOrEqual(32);54 });5556 it("excludes forbidden chars (Oracle-style)", () => {57 const pw = generateSmartPassword({58 maxLength: 40,59 forbiddenChars: "`~<>\\",60 });61 expect(pw.length).toBeLessThanOrEqual(40);62 for (const c of ["`", "~", "<", ">", "\\"]) {63 expect(pw).not.toContain(c);64 }65 });6667 it("honors forbidsSymbols", () => {68 const pw = generateSmartPassword({ forbidsSymbols: true });69 expect(pw).toMatch(/^[A-Za-z0-9]+$/);70 });7172 it("defaults to length 20 when policy is empty", () => {73 const pw = generateSmartPassword({});74 expect(pw).toHaveLength(20);75 });7677 it("rejection sampling still yields exact length and only pool chars (audit INFO — modulo bias)", () => {78 79 80 for (let i = 0; i < 50; i++) {81 const pw = generatePassword({ length: 64 });82 expect(pw).toHaveLength(64);83 expect(pw).toMatch(/^[A-Za-z0-9!@#$%^&*]+$/);84 }85 });86});