spb/worthdoing Public
Autonomous investigation agent that discovers, challenges, and ranks things genuinely worth doing — Claude + Firecrawl, Next.js 16, PostgreSQL
TypeScript 91.5%
SQL 5.8%
CSS 2.2%
1/**2 * WorthDoing.ai3 * Author: Simon-Pierre Boucher4 * Contact: contact@spboucher.ai5 * File: tests/tool-schemas.test.ts6 * Description: Tool contract tests — Zod validation boundaries and Anthropic tool definition consistency.7 */8import { describe, it, expect } from "vitest";9import { toolSchemas, anthropicTools } from "@/lib/agent/tools";1011const UUID = "3f0a9b1c-2d4e-4f6a-8b9c-0d1e2f3a4b5c";1213describe("tool schemas", () => {14 it("every Anthropic tool definition has a matching Zod schema", () => {15 const defNames = anthropicTools.map((t) => t.name).sort();16 const schemaNames = Object.keys(toolSchemas).sort();17 expect(defNames).toEqual(schemaNames);18 });1920 it("search_web rejects empty and over-long queries", () => {21 expect(toolSchemas.search_web.safeParse({ query: "a", intent: "explore" }).success).toBe(false);22 expect(toolSchemas.search_web.safeParse({ query: "x".repeat(401), intent: "explore" }).success).toBe(false);23 expect(toolSchemas.search_web.safeParse({ query: "local ai pain points", intent: "falsify" }).success).toBe(true);24 });2526 it("search_web rejects unknown intents", () => {27 expect(toolSchemas.search_web.safeParse({ query: "test query", intent: "vibes" }).success).toBe(false);28 });2930 it("scrape_page requires a valid URL", () => {31 expect(toolSchemas.scrape_page.safeParse({ url: "not-a-url", reason: "check" }).success).toBe(false);32 expect(toolSchemas.scrape_page.safeParse({ url: "https://example.com/x", reason: "check signal" }).success).toBe(true);33 });3435 it("hypothesis confidence is bounded to [0,1]", () => {36 const base = { title: "Test hypothesis", statement: "Falsifiable statement here", rationale: "because" };37 expect(toolSchemas.create_hypothesis.safeParse({ ...base, confidence: 1.2 }).success).toBe(false);38 expect(toolSchemas.create_hypothesis.safeParse({ ...base, confidence: -0.1 }).success).toBe(false);39 expect(toolSchemas.create_hypothesis.safeParse({ ...base, confidence: 0.5 }).success).toBe(true);40 });4142 it("save_evidence requires a real quote and valid source id", () => {43 expect(44 toolSchemas.save_evidence.safeParse({45 source_id: "not-a-uuid",46 kind: "support",47 quote: "long enough quote text",48 summary: "summary",49 strength: 0.5,50 }).success,51 ).toBe(false);52 expect(53 toolSchemas.save_evidence.safeParse({54 source_id: UUID,55 kind: "contradict",56 quote: "a sufficiently long quote from the page",57 summary: "counter-signal",58 strength: 0.7,59 links: [{ hypothesis_id: UUID, relation: "contradicts", weight: 0.8 }],60 }).success,61 ).toBe(true);62 });6364 it("create_opportunity demands at least 5 scores and 2 evidence links", () => {65 const score = (dimension: string) => ({66 dimension,67 score: 70,68 confidence: 0.8,69 reasoning: "grounded in evidence",70 evidence_ids: [UUID],71 });72 const base = {73 title: "Test opportunity",74 summary: "s".repeat(30),75 problem: "p".repeat(30),76 why_now: "w".repeat(30),77 risks: "r".repeat(30),78 skeptic_case: "sk".repeat(30),79 evidence: [80 { evidence_id: UUID, role: "demand" },81 { evidence_id: UUID, role: "risk" },82 ],83 };84 expect(85 toolSchemas.create_opportunity.safeParse({86 ...base,87 scores: [score("demand"), score("neglectedness"), score("feasibility")],88 }).success,89 ).toBe(false);90 expect(91 toolSchemas.create_opportunity.safeParse({92 ...base,93 scores: [score("demand"), score("neglectedness"), score("feasibility"), score("why_now"), score("impact")],94 }).success,95 ).toBe(true);96 });9798 it("all custom tool input schemas forbid additional properties (strict boundary)", () => {99 for (const tool of anthropicTools) {100 const schema = tool.input_schema as { additionalProperties?: boolean };101 expect(schema.additionalProperties, `${tool.name} must set additionalProperties: false`).toBe(false);102 }103 });104});105