import { describe, expect, it } from "vitest"; import { MAX_TASK_TITLE_LENGTH } from "@/model/taskFile"; import { validateFrontmatter } from "./validateFrontmatter"; const valid = `--- id: "550e8400-e29b-41d4-a716-446655440000" title: Fix login status: todo priority: high tags: [bug] assignee: "" created: 2026-07-12T10:00:00.000Z updated: 2026-07-12T10:00:00.000Z --- Body `; describe("validateFrontmatter", () => { it("accepts a valid task file", () => { expect(validateFrontmatter(valid)).toEqual([]); }); it("errors when frontmatter is missing", () => { const issues = validateFrontmatter("no frontmatter\n"); expect(issues).toHaveLength(1); expect(issues[0].severity).toBe("error"); expect(issues[0].message).toMatch(/frontmatter/i); }); it("errors when frontmatter is unclosed", () => { const issues = validateFrontmatter("---\ntitle: x\n"); expect(issues.some((i) => /not closed/i.test(i.message))).toBe(true); }); it("errors on invalid status", () => { const content = valid.replace("status: todo", "status: nope"); const issues = validateFrontmatter(content); const statusIssue = issues.find((i) => i.field === "status"); expect(statusIssue).toBeDefined(); expect(statusIssue!.severity).toBe("error"); expect(statusIssue!.range.startLine).toBeGreaterThan(0); }); it("errors on invalid priority", () => { const content = valid.replace("priority: high", "priority: urgent"); const issues = validateFrontmatter(content); expect(issues.some((i) => i.field === "priority")).toBe(true); }); it("errors when id is missing", () => { const content = valid.replace( 'id: "550e8400-e29b-41d4-a716-446655440000"\n', "", ); const issues = validateFrontmatter(content); expect( issues.some((i) => i.field === "id" && /missing/i.test(i.message)), ).toBe(true); }); it("errors when title exceeds max length", () => { const long = "x".repeat(MAX_TASK_TITLE_LENGTH + 1); const content = valid.replace("title: Fix login", `title: ${long}`); const issues = validateFrontmatter(content); const titleIssue = issues.find((i) => i.field === "title"); expect(titleIssue).toBeDefined(); expect(titleIssue!.message).toMatch(String(MAX_TASK_TITLE_LENGTH)); }); it("errors when tags is not an array", () => { const content = valid.replace("tags: [bug]", "tags: bug"); const issues = validateFrontmatter(content); expect(issues.some((i) => i.field === "tags")).toBe(true); }); it("warns on unknown fields", () => { const content = valid.replace( "updated: 2026-07-12T10:00:00.000Z", "updated: 2026-07-12T10:00:00.000Z\nfoo: bar", ); const issues = validateFrontmatter(content); const unknown = issues.find((i) => i.field === "foo"); expect(unknown).toBeDefined(); expect(unknown!.severity).toBe("warning"); }); it("errors on broken YAML", () => { const content = `--- title: [unterminated --- `; const issues = validateFrontmatter(content); expect(issues.some((i) => /yaml/i.test(i.message))).toBe(true); }); it("does not warn about unknown tags when knownTags is omitted", () => { const issues = validateFrontmatter(valid); expect(issues.filter((i) => i.field === "tags")).toEqual([]); }); it("warns when a tag is not in knownTags", () => { const content = valid.replace("tags: [bug]", "tags: [bug, nope]"); const issues = validateFrontmatter(content, { knownTags: ["bug"] }); const unknown = issues.filter( (i) => i.field === "tags" && i.severity === "warning", ); expect(unknown).toHaveLength(1); expect(unknown[0].message).toMatch(/nope/i); expect(unknown[0].code).toBe("unknown-tag"); expect(unknown[0].tag).toBe("nope"); }); it("does not warn when all tags are known", () => { const issues = validateFrontmatter(valid, { knownTags: ["bug", "frontend"], }); expect(issues.filter((i) => i.severity === "warning")).toEqual([]); }); it("warns for every tag when knownTags is empty array (config exists)", () => { const issues = validateFrontmatter(valid, { knownTags: [] }); expect( issues.some( (i) => i.field === "tags" && i.severity === "warning" && /bug/i.test(i.message), ), ).toBe(true); }); });