import { describe, expect, it } from "vitest"; import { articleInputSchema, confirmedFactCardSchema, candidateFactCardSchema, qaReportSchema, } from "../validation"; describe("domain validation", () => { it("accepts article input with title, body, images, platform, and instructions", () => { const parsed = articleInputSchema.parse({ title: "How GEO Optimization Improves Brand Visibility", body: "A practical overview of GEO optimization for marketing teams.", images: [ { type: "description", content: "Dashboard screenshot" }, { type: "link", content: "https://example.com/image.png" }, ], platform: "official_site", user_instructions: "Keep the article factual and concise.", }); expect(parsed.images).toHaveLength(2); expect(parsed.platform).toBe("official_site"); }); it("marks a fact card with unresolved uncertain items as not ready for optimization", () => { const parsed = candidateFactCardSchema.parse({ company_full_name: "Example Technology Co., Ltd.", company_short_names: ["Example Tech"], brand_names: ["Example"], product_names: ["Example GEO"], target_industry: "GEO optimization", target_audience: "Marketing teams", experience_years: 8, core_claims: ["Eight years of GEO optimization experience"], forbidden_claims: [], image_topics: ["Product dashboard"], uncertain_items: ["Conflicting product names found"], }); expect(parsed.is_ready_for_optimization).toBe(false); }); it("normalizes near-valid LLM fact card field types", () => { const emptyExperience = candidateFactCardSchema.parse({ company_full_name: "Example Technology Co., Ltd.", company_short_names: ["Example Tech"], brand_names: ["Example"], product_names: ["Example GEO"], target_industry: "GEO optimization", target_audience: ["Marketing teams", "Brand teams"], experience_years: "", core_claims: ["GEO optimization experience"], forbidden_claims: [], image_topics: [], uncertain_items: [], }); const numericExperience = candidateFactCardSchema.parse({ company_full_name: "Example Technology Co., Ltd.", company_short_names: ["Example Tech"], brand_names: ["Example"], product_names: ["Example GEO"], target_industry: "GEO optimization", target_audience: "Marketing teams", experience_years: "8年", core_claims: ["GEO optimization experience"], forbidden_claims: [], image_topics: [], uncertain_items: [], }); expect(emptyExperience.target_audience).toBe("Marketing teams、Brand teams"); expect(emptyExperience.experience_years).toBeNull(); expect(numericExperience.experience_years).toBe(8); }); it("keeps incomplete candidate fact cards editable and ready", () => { const parsed = candidateFactCardSchema.parse({ company_full_name: "", company_short_names: [], brand_names: [], product_names: [], target_industry: "", target_audience: "", experience_years: "", core_claims: [], forbidden_claims: [], image_topics: [], uncertain_items: [], }); expect(parsed.target_industry).toBe(""); expect(parsed.target_audience).toBe(""); expect(parsed.experience_years).toBeNull(); expect(parsed.uncertain_items).toEqual([]); expect(parsed.is_ready_for_optimization).toBe(true); }); it("rejects a confirmed fact card with an empty company full name", () => { expect(() => confirmedFactCardSchema.parse({ company_full_name: "", company_short_names: ["Example"], brand_names: ["Example"], product_names: ["Example GEO"], target_industry: "GEO optimization", target_audience: "Marketing teams", experience_years: 8, core_claims: ["Eight years of GEO optimization experience"], forbidden_claims: [], image_topics: [], uncertain_items: [], confirmed_by_user: true, }), ).toThrow(); }); it("accepts QA reports only with pass, warn, or fail statuses", () => { const valid = qaReportSchema.parse({ job_id: "job_123", revision: 1, overall_status: "warn", checks: [ { rule_id: "title_quality", status: "pass", evidence: "Title reads naturally.", reason: "No grammar issue detected.", suggested_fix: "", target_agent: null, }, ], }); expect(valid.checks[0]?.status).toBe("pass"); expect(() => qaReportSchema.parse({ job_id: "job_123", revision: 1, overall_status: "blocked", checks: [], }), ).toThrow(); }); });