Initial Commit
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
import { describe, expect, it } from "@suaveplan/testing/runner";
|
||||
import { loadDictionaries } from "../dicts/load-dictionaries.js";
|
||||
import {
|
||||
PasswordStrengthError,
|
||||
PWSTRENGTH_CODES,
|
||||
} from "../errors/error-codes.js";
|
||||
import { StrengthResultSchema } from "../schemas/schemas.js";
|
||||
import { assess } from "./assess.js";
|
||||
|
||||
describe("assess", () => {
|
||||
it("full pipeline returns canonical shape", { timeout: 5000 }, () => {
|
||||
const r = assess("Password1!");
|
||||
const parsed = StrengthResultSchema.safeParse(r);
|
||||
expect(parsed.success).toBe(true);
|
||||
expect(r.score).toBeLessThanOrEqual(1);
|
||||
});
|
||||
|
||||
it("accepts 199-character input", { timeout: 5000 }, () => {
|
||||
const r = assess("a".repeat(199));
|
||||
expect(r.password.length).toBe(199);
|
||||
});
|
||||
|
||||
it("rejects 200-character input", { timeout: 5000 }, () => {
|
||||
expect(() => assess("a".repeat(200))).toThrowError(
|
||||
PasswordStrengthError
|
||||
);
|
||||
});
|
||||
|
||||
it("rejects 200-character input with INPUT_TOO_LONG code", {
|
||||
timeout: 5000,
|
||||
}, () => {
|
||||
try {
|
||||
assess("a".repeat(200));
|
||||
} catch (e) {
|
||||
expect((e as PasswordStrengthError).code).toBe(
|
||||
PWSTRENGTH_CODES.INPUT_TOO_LONG
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("userInputs surface as ranked matches", { timeout: 5000 }, () => {
|
||||
const r = assess("hello-acme-corp", { userInputs: ["acme-corp"] });
|
||||
expect(
|
||||
r.sequence.some(
|
||||
(m) =>
|
||||
m.pattern === "dictionary" &&
|
||||
m.dictionaryName === "user_inputs"
|
||||
)
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("userInputs become a ranked dictionary", { timeout: 5000 }, () => {
|
||||
const r = assess("alice@example.com-pwd", {
|
||||
userInputs: ["alice@example.com"],
|
||||
});
|
||||
const hit = r.sequence.find(
|
||||
(m) =>
|
||||
m.pattern === "dictionary" && m.dictionaryName === "user_inputs"
|
||||
);
|
||||
expect(hit).toBeDefined();
|
||||
expect(hit?.pattern === "dictionary" && hit.matchedWord).toBe(
|
||||
"alice@example.com"
|
||||
);
|
||||
});
|
||||
|
||||
it("custom dictionaries take precedence", { timeout: 5000 }, () => {
|
||||
const dicts = loadDictionaries({
|
||||
passwords: new Map([["trustno1", 1]]),
|
||||
});
|
||||
const r = assess("trustno1", { dictionaries: dicts });
|
||||
expect(r.score).toBe(0);
|
||||
});
|
||||
|
||||
it("rejects malformed AssessOptions", { timeout: 5000 }, () => {
|
||||
expect(() =>
|
||||
assess("pwd", { userInputs: 42 as unknown as string[] })
|
||||
).toThrowError(PasswordStrengthError);
|
||||
});
|
||||
|
||||
it("emits feedback with warning + suggestions", { timeout: 5000 }, () => {
|
||||
const r = assess("password");
|
||||
expect(r.feedback.warning.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it("returns guesses ≥ 1", { timeout: 5000 }, () => {
|
||||
const r = assess("anything-random-here");
|
||||
expect(r.guesses).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
|
||||
it("records non-negative calcTimeMs", { timeout: 5000 }, () => {
|
||||
const r = assess("abc");
|
||||
expect(r.calcTimeMs).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
|
||||
it("accepts custom graphs option", { timeout: 5000 }, () => {
|
||||
const r = assess("hello", { graphs: { qwerty: {} } });
|
||||
expect(r).toBeDefined();
|
||||
});
|
||||
|
||||
it("accepts custom referenceYear option", { timeout: 5000 }, () => {
|
||||
const r = assess("1985", { referenceYear: 2026 });
|
||||
expect(r.score).toBeLessThanOrEqual(1);
|
||||
});
|
||||
|
||||
it("accepts empty userInputs", { timeout: 5000 }, () => {
|
||||
const r = assess("hello", { userInputs: [] });
|
||||
expect(r).toBeDefined();
|
||||
});
|
||||
|
||||
it("deduplicates userInputs", { timeout: 5000 }, () => {
|
||||
const r = assess("acme", {
|
||||
userInputs: ["acme", "acme", "acme corp"],
|
||||
});
|
||||
expect(r).toBeDefined();
|
||||
});
|
||||
|
||||
it("handles empty input", { timeout: 5000 }, () => {
|
||||
const r = assess("");
|
||||
expect(r.guesses).toBe(1);
|
||||
});
|
||||
|
||||
it("accepts custom l33tTable", { timeout: 5000 }, () => {
|
||||
const r = assess("p4ssword", { l33tTable: { a: ["4"] } });
|
||||
expect(r).toBeDefined();
|
||||
});
|
||||
|
||||
it("accepts feedback resolver via opts.feedback", { timeout: 5000 }, () => {
|
||||
const r = assess("password", {
|
||||
feedback: { resolver: (key) => `RESOLVED:${key}` },
|
||||
});
|
||||
expect(r.feedback.warning.startsWith("RESOLVED:")).toBe(true);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user