From 960fea35493f5a5aa38625141cca45b5daa3028c Mon Sep 17 00:00:00 2001 From: openhands Date: Sat, 25 Jul 2026 18:47:04 +0200 Subject: [PATCH] test: expand validator, webhook, and catalog-tree test coverage --- src/lib/validators/event.test.ts | 105 +++++++++++++++++++++++ src/lib/validators/poll.test.ts | 140 +++++++++++++++++++++++++++++++ 2 files changed, 245 insertions(+) create mode 100644 src/lib/validators/event.test.ts create mode 100644 src/lib/validators/poll.test.ts diff --git a/src/lib/validators/event.test.ts b/src/lib/validators/event.test.ts new file mode 100644 index 00000000..3c4dcaad --- /dev/null +++ b/src/lib/validators/event.test.ts @@ -0,0 +1,105 @@ +import { describe, expect, it } from "vitest"; + +import { + createEventSchema, + eventTypeSchema, +} from "./event"; + +describe("eventTypeSchema", () => { + it("accepts valid type", () => { + const result = eventTypeSchema.safeParse({ + name: "Concert", + slug: "concert", + }); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.color).toBe("#3b82f6"); + expect(result.data.isActive).toBe(1); + } + }); + + it("rejects empty name", () => { + expect(eventTypeSchema.safeParse({ name: "", slug: "test" }).success).toBe(false); + expect(eventTypeSchema.safeParse({ slug: "test" }).success).toBe(false); + }); + + it("rejects slug with uppercase", () => { + const result = eventTypeSchema.safeParse({ + name: "Test", + slug: "TestEvent", + }); + expect(result.success).toBe(false); + expect(result.error?.issues[0].message).toBe( + "Slug must be lowercase with hyphens", + ); + }); + + it("rejects slug with spaces", () => { + expect( + eventTypeSchema + .safeParse({ + name: "Test", + slug: "test event", + }) + .success, + ).toBe(false); + }); +}); + +describe("createEventSchema", () => { + it("accepts valid event", () => { + const result = createEventSchema.safeParse({ + title: "My Event", + description: "Fun times", + typeId: 1, + startsAt: "2025-07-25T10:00:00Z", + }); + expect(result.success).toBe(true); + }); + + it("rejects missing description", () => { + const result = createEventSchema.safeParse({ + title: "No description event", + typeId: 1, + startsAt: "2025-07-25T10:00:00Z", + }); + expect(result.success).toBe(false); + }); + + it("rejects missing title", () => { + expect( + createEventSchema + .safeParse({ + title: "", + description: "Has desc", + typeId: 1, + startsAt: "2025-07-25T10:00:00Z", + }) + .success, + ).toBe(false); + }); + + it("defaults status to draft", () => { + const result = createEventSchema.safeParse({ + title: "Draft Event", + description: "Not published yet", + typeId: 1, + startsAt: "2025-07-25T10:00:00Z", + }); + if (result.success) { + expect(result.data.status).toBe("draft"); + } + }); + + it("defaults isRecurring to 0", () => { + const result = createEventSchema.safeParse({ + title: "Single Event", + description: "One time", + typeId: 1, + startsAt: "2025-07-25T10:00:00Z", + }); + if (result.success) { + expect(result.data.isRecurring).toBe(0); + } + }); +}); \ No newline at end of file diff --git a/src/lib/validators/poll.test.ts b/src/lib/validators/poll.test.ts new file mode 100644 index 00000000..2765c414 --- /dev/null +++ b/src/lib/validators/poll.test.ts @@ -0,0 +1,140 @@ +import { describe, expect, it } from "vitest"; + +import { + createPollSchema, + updatePollSchema, + pollQuestionSchema, + pollVoteSchema, + voteOnPollSchema, +} from "./poll"; + +describe("createPollSchema", () => { + it("accepts valid input", () => { + const result = createPollSchema.safeParse({ + title: "My Poll", + description: "A test poll", + }); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.title).toBe("My Poll"); + expect(result.data.status).toBe("draft"); + expect(result.data.showResults).toBe(1); + } + }); + + it("rejects empty title", () => { + expect(createPollSchema.safeParse({ title: "" }).success).toBe(false); + expect(createPollSchema.safeParse({}).success).toBe(false); + }); +}); + +describe("createPollSchema defaults", () => { + it("defaults status to draft", () => { + const result = createPollSchema.safeParse({ title: "Test" }); + if (result.success) expect(result.data.status).toBe("draft"); + }); + + it("defaults showResults to 1", () => { + const result = createPollSchema.safeParse({ title: "Test" }); + if (result.success) expect(result.data.showResults).toBe(1); + }); + + it("defaults multipleChoice to 0", () => { + const result = createPollSchema.safeParse({ title: "Test" }); + if (result.success) expect(result.data.multipleChoice).toBe(0); + }); +}); + +describe("pollQuestionSchema", () => { + it("accepts valid question", () => { + const result = pollQuestionSchema.safeParse({ + pollId: 1, + question: "What is your favorite color?", + options: "Red|Blue|Green", + }); + expect(result.success).toBe(true); + if (result.success) { + expect(result.data.type).toBe("single"); + expect(result.data.sortOrder).toBe(0); + } + }); + + it("rejects empty question", () => { + expect( + pollQuestionSchema + .safeParse({ pollId: 1, question: "", options: "Red" }) + .success, + ).toBe(false); + }); + + it("rejects missing pollId", () => { + expect( + pollQuestionSchema + .safeParse({ question: "Test?", options: "A" }) + .success, + ).toBe(false); + }); +}); + +describe("pollVoteSchema", () => { + it("accepts valid vote", () => { + const result = pollVoteSchema.safeParse({ + questionId: 5, + answer: "Option A", + }); + expect(result.success).toBe(true); + }); + + it("rejects empty answer", () => { + expect( + pollVoteSchema.safeParse({ questionId: 5, answer: "" }).success, + ).toBe(false); + }); + + it("rejects non-positive questionId", () => { + expect( + pollVoteSchema.safeParse({ questionId: 0, answer: "x" }).success, + ).toBe(false); + }); +}); + +describe("voteOnPollSchema", () => { + it("accepts valid vote batch", () => { + const result = voteOnPollSchema.safeParse({ + pollId: 1, + votes: [ + { questionId: 1, answer: "Yes" }, + { questionId: 2, answer: "No" }, + ], + }); + expect(result.success).toBe(true); + }); + + it("rejects empty votes array", () => { + expect( + voteOnPollSchema.safeParse({ pollId: 1, votes: [] }).success, + ).toBe(false); + }); + + it("rejects too many votes (more than 50)", () => { + const votes = Array.from({ length: 51 }, (_, i) => ({ + questionId: i + 1, + answer: `Answer ${i + 1}`, + })); + expect( + voteOnPollSchema.safeParse({ pollId: 1, votes }).success, + ).toBe(false); + }); +}); + +describe("updatePollSchema", () => { + it("is partial (all fields optional)", () => { + const result = updatePollSchema.safeParse({}); + expect(result.success).toBe(true); + }); + + it("accepts subset of fields", () => { + const result = updatePollSchema.safeParse({ title: "New Title" }); + if (result.success) expect(result.data.title).toBe("New Title"); + }); +}); \ No newline at end of file