import { describe, it, expect, beforeEach, afterEach } from "vitest" import { Shrimp, plainHasher, bcryptHasher, UserModule, MemoryShrimpCrud, ShrimpFieldTypes } from "../src/main" import type { IPasswordHasher } from "../src/main" describe("PasswordHasher 抽象", () => { const original = Shrimp.passwordHasher afterEach(() => { Shrimp.passwordHasher = original }) it("默认是 bcryptHasher", () => { expect(Shrimp.passwordHasher).toBe(bcryptHasher) }) it("plainHasher: hash 原样返回,verify 字符串等值", async () => { expect(await plainHasher.hash("secret")).toBe("secret") expect(await plainHasher.verify("secret", "secret")).toBe(true) expect(await plainHasher.verify("secret", "other")).toBe(false) }) it("通过替换 Shrimp.passwordHasher 让 Password 字段走自定义哈希", async () => { const calls: string[] = [] const custom: IPasswordHasher = { async hash(plain) { calls.push("hash:" + plain); return "X:" + plain }, async verify(plain, hashed) { calls.push("verify:" + plain + ":" + hashed); return hashed === "X:" + plain }, } Shrimp.passwordHasher = custom const shrimp = new Shrimp() const userCrud = new MemoryShrimpCrud<{ _id: string; username: string; password: string; active: boolean; roleId?: string[] }>() const userModule = new UserModule(shrimp, userCrud as any, [ { key: "username", label: "用户名", type: ShrimpFieldTypes.String, required: true, editable: "addOnly" }, { key: "password", label: "密码", type: ShrimpFieldTypes.Password, required: true, tableHide: true }, { key: "active", label: "启用", type: ShrimpFieldTypes.Boolean, defaultValue: true }, ]) await userModule.add({ username: "alice", password: "pw123", active: true, roleId: [] } as any) const stored = (await userCrud.list({})).list[0] expect(stored.password).toBe("X:pw123") expect(calls).toContain("hash:pw123") const ok = await userModule.getUser("alice", "pw123") expect(ok.username).toBe("alice") expect(calls.some(c => c.startsWith("verify:pw123:X:pw123"))).toBe(true) }) it("plainHasher 端到端: 在不装 bcrypt 的场景下完成登录校验", async () => { Shrimp.passwordHasher = plainHasher const shrimp = new Shrimp() const userCrud = new MemoryShrimpCrud<{ _id: string; username: string; password: string; active: boolean; roleId?: string[] }>() const userModule = new UserModule(shrimp, userCrud as any, [ { key: "username", label: "用户名", type: ShrimpFieldTypes.String, required: true, editable: "addOnly" }, { key: "password", label: "密码", type: ShrimpFieldTypes.Password, required: true, tableHide: true }, { key: "active", label: "启用", type: ShrimpFieldTypes.Boolean, defaultValue: true }, ]) await userModule.add({ username: "bob", password: "open", active: true, roleId: [] } as any) const user = await userModule.getUser("bob", "open") expect(user.username).toBe("bob") await expect(userModule.getUser("bob", "wrong")).rejects.toThrow() }) })