import { describe, expect, test } from "bun:test"; import { ValidationError, firstValidationError, parseOrThrow, validationResponse, validationSummary, v, type InferSchema, type ObjectSchema, } from "../src/index.ts"; describe("validation helper kit", () => { test("throws a structured validation error", () => { const schema = v.object({ email: v.string().email() }); expect(() => parseOrThrow(schema, { email: "invalid" })).toThrow(ValidationError); const value = parseOrThrow(schema, { email: "user@example.com" }); const email: string = value.email; expect(value).toEqual({ email }); }); test("preserves literal unions from oneOf", () => { interface ContactInput { topic: "general" | "security" | "billing" | "integration"; priority: 1 | 2 | 3; } const schema: ObjectSchema = v.object({ topic: v.string().oneOf(["general", "security", "billing", "integration"]), priority: v.number().oneOf([1, 2, 3]), }); const value = parseOrThrow(schema, { topic: "security", priority: 2 }); const topic: ContactInput["topic"] = value.topic; const priority: ContactInput["priority"] = value.priority; expect({ topic, priority }).toEqual({ topic: "security", priority: 2 }); const _inferredSchema = v.object({ topic: v.string().oneOf(["general", "security"]), }); type Inferred = InferSchema; const inferredTopic: Inferred["topic"] = "general"; expect(inferredTopic).toBe("general"); const dynamicOptions: string[] = ["draft", "published"]; const dynamicSchema = v.object({ status: v.string().oneOf(dynamicOptions) }); const dynamicValue: string = parseOrThrow(dynamicSchema, { status: "draft", }).status; expect(dynamicValue).toBe("draft"); }); test("normalizes error summaries and HTTP responses", async () => { const errors = { email: "Email is invalid", password: "Password is required" }; expect(firstValidationError(errors)).toBe("Email is invalid"); expect(validationSummary(errors)).toHaveLength(2); expect((await validationResponse({ ok: false, value: {}, errors }).json()).ok).toBe(false); }); });