import { describe, it, expect } from "vitest"; import { ApiError } from "@/lib/api"; import { validateUsername, validatePassword, validateEmail, validateIdeaContent, validateRoomName, requireString, } from "@/lib/validation"; describe("validateUsername", () => { it("accepts 2-16 character names", () => { expect(validateUsername("ab")).toBe("ab"); expect(validateUsername("a".repeat(16))).toBe("a".repeat(16)); expect(validateUsername("用户名")).toBe("用户名"); }); it("trims whitespace", () => { expect(validateUsername(" hello ")).toBe("hello"); }); it("rejects names shorter than 2 chars", () => { expect(() => validateUsername("a")).toThrow(ApiError); expect(() => validateUsername("")).toThrow(ApiError); expect(() => validateUsername(" ")).toThrow(ApiError); }); it("rejects names longer than 16 chars", () => { expect(() => validateUsername("a".repeat(17))).toThrow(ApiError); }); }); describe("validatePassword", () => { it("accepts 6-128 character passwords", () => { expect(() => validatePassword("123456")).not.toThrow(); expect(() => validatePassword("a".repeat(128))).not.toThrow(); }); it("rejects passwords shorter than 6 chars", () => { expect(() => validatePassword("12345")).toThrow(ApiError); expect(() => validatePassword("")).toThrow(ApiError); }); it("rejects passwords longer than 128 chars", () => { expect(() => validatePassword("a".repeat(129))).toThrow(ApiError); }); it("uses custom label in error message", () => { expect(() => validatePassword("12345", "新密码")).toThrow("新密码至少 6 个字符"); }); }); describe("validateEmail", () => { it("accepts valid emails", () => { expect(() => validateEmail("test@example.com")).not.toThrow(); expect(() => validateEmail("user.name@domain.co")).not.toThrow(); }); it("rejects invalid emails", () => { expect(() => validateEmail("notanemail")).toThrow(ApiError); expect(() => validateEmail("@domain.com")).toThrow(ApiError); expect(() => validateEmail("user@")).toThrow(ApiError); expect(() => validateEmail("user @domain.com")).toThrow(ApiError); }); }); describe("validateIdeaContent", () => { it("accepts valid content and trims", () => { expect(validateIdeaContent(" 去公园 ")).toBe("去公园"); expect(validateIdeaContent("a")).toBe("a"); }); it("rejects empty/falsy content", () => { expect(() => validateIdeaContent("")).toThrow(ApiError); expect(() => validateIdeaContent(null)).toThrow(ApiError); expect(() => validateIdeaContent(undefined)).toThrow(ApiError); expect(() => validateIdeaContent(" ")).toThrow(ApiError); }); it("rejects non-string content", () => { expect(() => validateIdeaContent(123)).toThrow(ApiError); }); it("rejects content over 200 chars", () => { expect(() => validateIdeaContent("a".repeat(201))).toThrow(ApiError); expect(validateIdeaContent("a".repeat(200))).toBe("a".repeat(200)); }); }); describe("validateRoomName", () => { it("accepts valid room name", () => { expect(validateRoomName("我的房间")).toBe("我的房间"); }); it("uses fallback for empty name", () => { expect(validateRoomName("")).toBe("我们的周末"); expect(validateRoomName(null)).toBe("我们的周末"); expect(validateRoomName(undefined)).toBe("我们的周末"); }); it("uses custom fallback", () => { expect(validateRoomName("", "默认名")).toBe("默认名"); }); it("rejects names over 30 chars", () => { expect(() => validateRoomName("a".repeat(31))).toThrow(ApiError); expect(validateRoomName("a".repeat(30))).toBe("a".repeat(30)); }); }); describe("requireString", () => { it("returns string value when valid", () => { expect(requireString("hello", "field")).toBe("hello"); }); it("throws for empty/falsy values", () => { expect(() => requireString("", "字段")).toThrow("字段不能为空"); expect(() => requireString(null, "字段")).toThrow(ApiError); expect(() => requireString(undefined, "字段")).toThrow(ApiError); expect(() => requireString(" ", "字段")).toThrow(ApiError); }); it("throws for non-string values", () => { expect(() => requireString(123, "字段")).toThrow(ApiError); }); });