Files
chatgpt-mcp/test/prompts/review-plan.test.js
T

323 lines
13 KiB
JavaScript

import { describe, it, expect } from "vitest";
import { buildReviewPlanPrompt } from "../../src/prompts/review-plan.js";
describe("buildReviewPlanPrompt", () => {
// --- Basic output contract ---
it("returns a non-empty string given question-only input", () => {
const result = buildReviewPlanPrompt({ question: "Review this plan" });
expect(typeof result).toBe("string");
expect(result.length).toBeGreaterThan(0);
});
it("is deterministic — same input always produces the same output", () => {
const a = buildReviewPlanPrompt({ question: "Review this plan" });
const b = buildReviewPlanPrompt({ question: "Review this plan" });
expect(a).toBe(b);
});
// --- Composition with base prompt ---
it("includes the base system prompt content", () => {
const result = buildReviewPlanPrompt({ 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 = buildReviewPlanPrompt({ question: "Test" });
const parts = result.split("\n---\n");
expect(parts.length).toBe(2);
});
// --- review_plan-specific role instructions ---
it("contains review_plan-specific role instruction (plan reviewer)", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result).toContain("plan reviewer");
});
it("instructs Claude Code as the implementation agent", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result).toContain("Claude Code will implement your suggestions");
});
it("instructs ChatGPT that you do not implement anything yourself", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result).toContain("you do not implement anything yourself");
});
// --- Review dimensions ---
it("includes missing requirements dimension", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result.toLowerCase()).toContain("missing requirements");
});
it("includes risks dimension", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result.toLowerCase()).toContain("risks");
});
it("includes incorrect assumptions dimension", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result.toLowerCase()).toContain("incorrect assumptions");
});
it("includes security concerns dimension", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result.toLowerCase()).toContain("security concerns");
});
it("includes maintainability concerns dimension", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result.toLowerCase()).toContain("maintainability concerns");
});
it("includes over-engineering dimension", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result.toLowerCase()).toContain("over-engineering");
});
it("includes task decomposition dimension", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result.toLowerCase()).toContain("task decomposition");
});
it("includes pre-implementation validation dimension", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result.toLowerCase()).toContain("pre-implementation validation");
});
// --- Expected response structure ---
it("requests Summary section in response structure", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result).toMatch(/summary/i);
});
it("requests Risks section in response structure", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result).toMatch(/risks/i);
});
it("requests Missing Requirements section in response structure", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result).toMatch(/missing requirements/i);
});
it("requests Assumptions section in response structure", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result).toMatch(/assumptions/i);
});
it("requests Recommended Changes section in response structure", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result).toMatch(/recommended changes/i);
});
it("requests Suggested Next Steps section in response structure", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result).toMatch(/suggested next steps/i);
});
// --- Claude Code / reviewer guard rails ---
it("states Claude Code remains the implementation agent", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result).toContain("Claude Code remains the implementation agent");
});
it("states ChatGPT is acting only as a reviewer", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result).toContain("acting only as a reviewer");
});
it("instructs to use only the context provided", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result).toContain("only the context provided");
});
it("instructs not to assume access to the full repository", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result).toMatch(/do not assume access to the full repository/i);
});
// --- Question field ---
it("appends the input question verbatim in the task section", () => {
const result = buildReviewPlanPrompt({ question: "Review JWT migration plan" });
expect(result).toContain("Question: Review JWT migration plan");
});
// --- Optional fields appended correctly (with question) ---
it("appends Context when context is provided", () => {
const result = buildReviewPlanPrompt({
question: "Review this plan",
context: "We use EC2 user data for secrets.",
});
expect(result).toContain("Context: We use EC2 user data for secrets.");
});
it("does NOT append Context section when context is empty", () => {
const result = buildReviewPlanPrompt({ question: "Review this plan", context: "" });
expect(result).not.toContain("Context:");
});
it("appends Constraints list when constraints are provided", () => {
const result = buildReviewPlanPrompt({
question: "Review this plan",
constraints: ["must be safe", "must not add infra changes"],
});
expect(result).toContain("Constraints:");
expect(result).toContain("- must be safe");
expect(result).toContain("- must not add infra changes");
});
it("does NOT append Constraints section when constraints array is empty", () => {
const result = buildReviewPlanPrompt({ question: "Review this plan", constraints: [] });
expect(result).not.toContain("Constraints:");
});
it("appends Expected output when expectedOutput is provided", () => {
const result = buildReviewPlanPrompt({
question: "Review migration plan",
expectedOutput: "A structured review covering risks and missing requirements.",
});
expect(result).toContain("Expected output: A structured review covering risks and missing requirements.");
});
it("does NOT append Expected output section when expectedOutput is omitted", () => {
const result = buildReviewPlanPrompt({ question: "Review migration plan" });
expect(result).not.toContain("Expected output:");
});
it("does NOT append Expected output section when expectedOutput is empty", () => {
const result = buildReviewPlanPrompt({ question: "Test", expectedOutput: "" });
expect(result).not.toContain("Expected output:");
});
it("appends all optional fields alongside each other", () => {
const result = buildReviewPlanPrompt({
question: "Review auth migration plan",
context: "Current system uses session cookies.",
constraints: ["no infra changes", "must support existing IAM"],
expectedOutput: "A checklist-style review.",
projectSummary: "Internal inventory management system.",
taskSummary: "Migrating from sessions to JWT.",
});
expect(result).toContain("Question: Review auth migration plan");
expect(result).toContain("Context: Current system uses session cookies.");
expect(result).toContain("Constraints:");
expect(result).toContain("- no infra changes");
expect(result).toContain("- must support existing IAM");
expect(result).toContain("Expected output: A checklist-style review.");
expect(result).toContain("Project summary: Internal inventory management system.");
expect(result).toContain("Task summary: Migrating from sessions to JWT.");
});
it("does NOT append Project summary when projectSummary is omitted", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result).not.toContain("Project summary:");
});
it("does NOT append Task summary when taskSummary is omitted", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result).not.toContain("Task summary:");
});
// --- Tool-specific guard ---
it("does NOT contain ask_chatgpt specific content", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result.toLowerCase()).not.toContain("answer the question directly");
expect(result.toLowerCase()).not.toContain("second-opinion advisor");
});
it("does NOT contain review_code specific content", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result).not.toContain("review_code");
expect(result).not.toContain("patch");
expect(result).not.toContain("diff");
});
it("does NOT contain debug_issue specific content", () => {
const result = buildReviewPlanPrompt({ question: "Test" });
expect(result).not.toContain("debug_issue");
expect(result.toLowerCase()).not.toContain("stack trace");
});
// --- Input validation ---
it("throws TypeError when input is missing", () => {
expect(() => buildReviewPlanPrompt()).toThrow(TypeError);
expect(() => buildReviewPlanPrompt(null)).toThrow(TypeError);
});
it("throws TypeError when question is missing", () => {
expect(() => buildReviewPlanPrompt({ context: "something" })).toThrow(TypeError);
});
it("throws TypeError when question is empty", () => {
expect(() => buildReviewPlanPrompt({ question: "" })).toThrow(TypeError);
});
// --- Full integration test ---
it("returns a well-formed two-part prompt with all fields", () => {
const result = buildReviewPlanPrompt({
question: "Review JWT migration plan",
context: "We use session cookies and EC2 user data for secrets.",
constraints: ["no infra changes", "must support existing IAM"],
expectedOutput: "A checklist-style review with severity ratings.",
projectSummary: "Internal inventory management system with multi-tenant isolation.",
taskSummary: "Migrating from session-based auth to JWT.",
});
// 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).toContain("plan reviewer");
expect(result).toContain("Claude Code remains the implementation agent");
expect(result).toMatch(/do not assume access to the full repository/i);
// All 8 review dimensions present
expect(result.toLowerCase()).toContain("missing requirements");
expect(result.toLowerCase()).toContain("risks");
expect(result.toLowerCase()).toContain("incorrect assumptions");
expect(result.toLowerCase()).toContain("security concerns");
expect(result.toLowerCase()).toContain("maintainability concerns");
expect(result.toLowerCase()).toContain("over-engineering");
expect(result.toLowerCase()).toContain("task decomposition");
expect(result.toLowerCase()).toContain("pre-implementation validation");
// Expected response structure present
expect(result).toMatch(/summary/i);
expect(result).toMatch(/risks/i);
expect(result).toMatch(/missing requirements/i);
expect(result).toMatch(/assumptions/i);
expect(result).toMatch(/recommended changes/i);
expect(result).toMatch(/suggested next steps/i);
// All fields present verbatim
expect(result).toContain("Question: Review JWT migration plan");
expect(result).toContain("Context: We use session cookies and EC2 user data for secrets.");
expect(result).toContain("- no infra changes");
expect(result).toContain("- must support existing IAM");
expect(result).toContain("Expected output: A checklist-style review with severity ratings.");
expect(result).toContain("Project summary: Internal inventory management system with multi-tenant isolation.");
expect(result).toContain("Task summary: Migrating from session-based auth to JWT.");
// Does not contain other tool content
expect(result.toLowerCase()).not.toContain("answer the question directly");
expect(result.toLowerCase()).not.toContain("second-opinion advisor");
});
});