feat: add ask_chatgpt prompt builder

This commit is contained in:
2026-06-11 15:37:42 +01:00
parent 4132aa4552
commit 008f38e630
4 changed files with 328 additions and 2 deletions
+216
View File
@@ -0,0 +1,216 @@
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.");
});
});