import { describe, it, expect } from "vitest"; import { buildAskChatGptPrompt } from "../../src/prompts/ask-chatgpt.js"; describe("buildAskChatGptPrompt", () => { // --- Basic output contract --- it("returns a non-empty string given question-only input", () => { const result = buildAskChatGptPrompt({ question: "Is this OK?" }); expect(typeof result).toBe("string"); expect(result.length).toBeGreaterThan(0); }); it("is deterministic — same input always produces the same output", () => { const a = buildAskChatGptPrompt({ question: "Is this OK?" }); const b = buildAskChatGptPrompt({ question: "Is this OK?" }); expect(a).toBe(b); }); // --- Composition with base prompt --- it("includes the base system prompt content", () => { const result = buildAskChatGptPrompt({ question: "Test" }); expect(result).toContain("second-opinion assistant"); expect(result).toContain("Claude Code"); expect(result).toContain("not responsible for editing files"); }); it("contains a '---' separator between base and tool-specific sections", () => { const result = buildAskChatGptPrompt({ question: "Test" }); const parts = result.split("\n---\n"); expect(parts.length).toBe(2); }); // --- ask_chatgpt-specific role instructions --- it("contains ask_chatgpt-specific role instructions (second-opinion advisor)", () => { const result = buildAskChatGptPrompt({ question: "Test" }); expect(result.toLowerCase()).toContain("second-opinion advisor"); }); it("instructs ChatGPT to answer the question directly", () => { const result = buildAskChatGptPrompt({ question: "Test" }); expect(result).toMatch(/answer\s+the\s+question\s+direct/i); }); it("includes section about identifying assumptions", () => { const result = buildAskChatGptPrompt({ question: "Test" }); expect(result.toLowerCase()).toContain("assumptions"); }); it("includes section about identifying risks", () => { const result = buildAskChatGptPrompt({ question: "Test" }); expect(result.toLowerCase()).toContain("risks"); }); it("includes section about suggesting safer approaches", () => { const result = buildAskChatGptPrompt({ question: "Test" }); expect(result).toMatch(/safer\s+approaches?/i); }); it("includes section about stating uncertainty clearly", () => { const result = buildAskChatGptPrompt({ question: "Test" }); expect(result.toLowerCase()).toContain("uncertainty"); }); // --- Question field --- it("appends the input question verbatim in the task section", () => { const result = buildAskChatGptPrompt({ question: "What auth method should I use?" }); expect(result).toContain("Question: What auth method should I use?"); }); // --- Optional fields appended correctly (with question) --- it("appends Context when context is provided", () => { const result = buildAskChatGptPrompt({ question: "Review this", context: "The code catches all errors.", }); expect(result).toContain("Context: The code catches all errors."); }); it("does NOT append Context section when context is empty", () => { const result = buildAskChatGptPrompt({ question: "Review this", context: "" }); expect(result).not.toContain("Context:"); }); it("appends Constraints list when constraints are provided", () => { const result = buildAskChatGptPrompt({ question: "Review this", constraints: ["must be safe", "must be fast"], }); expect(result).toContain("Constraints:"); expect(result).toContain("- must be safe"); expect(result).toContain("- must be fast"); }); it("does NOT append Constraints section when constraints array is empty", () => { const result = buildAskChatGptPrompt({ question: "Review this", constraints: [] }); expect(result).not.toContain("Constraints:"); }); // --- expectedOutput (approved test additions) --- it("appends Expected output when expectedOutput is provided", () => { const result = buildAskChatGptPrompt({ question: "Polling vs webhooks?", expectedOutput: "A comparison table with pros/cons and a recommendation.", }); expect(result).toContain("Expected output: A comparison table with pros/cons and a recommendation."); }); it("does NOT append Expected output section when expectedOutput is omitted", () => { const result = buildAskChatGptPrompt({ question: "Polling vs webhooks?" }); expect(result).not.toContain("Expected output:"); }); it("does NOT append Expected output section when expectedOutput is empty", () => { const result = buildAskChatGptPrompt({ question: "Polling vs webhooks?", expectedOutput: "" }); expect(result).not.toContain("Expected output:"); }); it("appends all optional fields alongside expectedOutput", () => { const result = buildAskChatGptPrompt({ question: "Should I use JWT or sessions?", context: "Current API uses token auth.", constraints: ["no external deps"], expectedOutput: "A one-paragraph recommendation.", projectSummary: "Internal inventory tool.", taskSummary: "Migrating authentication.", }); expect(result).toContain("Question: Should I use JWT or sessions?"); expect(result).toContain("Context: Current API uses token auth."); expect(result).toContain("Constraints:"); expect(result).toContain("- no external deps"); expect(result).toContain("Expected output: A one-paragraph recommendation."); expect(result).toContain("Project summary: Internal inventory tool."); expect(result).toContain("Task summary: Migrating authentication."); }); it("does NOT append Project summary when projectSummary is omitted", () => { const result = buildAskChatGptPrompt({ question: "Test" }); expect(result).not.toContain("Project summary:"); }); it("does NOT append Task summary when taskSummary is omitted", () => { const result = buildAskChatGptPrompt({ question: "Test" }); expect(result).not.toContain("Task summary:"); }); // --- Tool-specific guard --- it("does NOT contain review_plan specific content", () => { const result = buildAskChatGptPrompt({ question: "Test" }); expect(result).not.toContain("review-plan"); expect(result).not.toContain("missing steps"); expect(result).not.toContain("scope creep"); }); it("does NOT contain review_code specific content", () => { const result = buildAskChatGptPrompt({ question: "Test" }); expect(result).not.toContain("review_code"); expect(result).not.toContain("patch"); expect(result).not.toContain("diff"); }); // --- Input validation --- it("throws TypeError when input is missing", () => { expect(() => buildAskChatGptPrompt()).toThrow(TypeError); expect(() => buildAskChatGptPrompt(null)).toThrow(TypeError); }); it("throws TypeError when question is missing", () => { expect(() => buildAskChatGptPrompt({ context: "something" })).toThrow(TypeError); }); it("throws TypeError when question is empty", () => { expect(() => buildAskChatGptPrompt({ question: "" })).toThrow(TypeError); }); // --- Full integration test --- it("returns a well-formed two-part prompt with all fields", () => { const result = buildAskChatGptPrompt({ question: "Is this approach safe?", context: "We log errors to console.error.", constraints: ["no stack traces", "no raw data"], expectedOutput: "A concise risk assessment.", projectSummary: "Private VPC REST API.", taskSummary: "Hardening error logging.", }); // Base section present expect(result).toContain("second-opinion assistant"); expect(result).toContain("Rules:"); // Separator expect(result).toMatch(/\n---\n/); // Tool-specific section present expect(result).toMatch(/answer\s+the\s+question\s+direct/i); expect(result).toContain("assumptions"); expect(result).toContain("risks"); expect(result).toContain("safer approaches"); expect(result).toContain("uncertainty"); // All fields present verbatim expect(result).toContain("Question: Is this approach safe?"); expect(result).toContain("Context: We log errors to console.error."); expect(result).toContain("- no stack traces"); expect(result).toContain("Expected output: A concise risk assessment."); expect(result).toContain("Project summary: Private VPC REST API."); expect(result).toContain("Task summary: Hardening error logging."); }); });