feat: add ask_chatgpt prompt builder
This commit is contained in:
@@ -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.");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user