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

466 lines
20 KiB
JavaScript

import { describe, it, expect } from "vitest";
import { buildArchitectureReviewPrompt } from "../../src/prompts/architecture-review.js";
describe("buildArchitectureReviewPrompt", () => {
// --- Basic output contract ---
it("returns a non-empty string given question-only input", () => {
const result = buildArchitectureReviewPrompt({ question: "Should we use a managed vector database?" });
expect(typeof result).toBe("string");
expect(result.length).toBeGreaterThan(0);
});
it("is deterministic — same input always produces the same output", () => {
const a = buildArchitectureReviewPrompt({ question: "Should we use a managed vector database?" });
const b = buildArchitectureReviewPrompt({ question: "Should we use a managed vector database?" });
expect(a).toBe(b);
});
// --- Explicit separator ---
it("uses an explicit \\n---\\n separator between base and tool sections", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).toContain("\n\n---\n\n");
});
it("contains exactly one separator (two-part prompt)", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
const parts = result.split("\n\n---\n\n");
expect(parts.length).toBe(2);
});
// --- Composition with base prompt ---
it("includes the base system prompt content", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).toContain("second-opinion assistant");
expect(result).toContain("Claude Code");
expect(result).toContain("not responsible for editing files");
});
// --- Architecture advisor role instructions ---
it("contains architecture advisor role instruction", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).toContain("architecture advisor");
});
it("instructs Claude Code as the implementation agent", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).toContain("Claude Code will implement your suggestions");
});
it("instructs ChatGPT that you do not implement anything yourself", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).toContain("you do not implement anything yourself");
});
it("states ChatGPT is acting only as an architecture advisor", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).toMatch(/acting\s+only\s+as\s+an?\s+architecture\s+advisor/i);
});
// --- Guard rails ---
it("states Claude Code remains the implementation agent", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).toContain("Claude Code remains the implementation agent");
});
it("instructs ChatGPT to use only supplied context and files", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).toMatch(/use\s+only\s+the?\s+supplied\s+context/i);
});
it("instructs ChatGPT not to assume repository-wide context", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).toMatch(/not\s+assume\s+repository-wide\s+context/i);
});
it("instructs to prefer simple, incremental architecture", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).toMatch(/prefer\s+simple.*incremental.*architecture/i);
});
it("instructs to avoid large rewrites unless justified", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).toMatch(/avoid\s+large\s+rewrites.*unless.*justified/i);
});
// --- Review dimensions (10) ---
it("contains Simplicity dimension instruction", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result.toLowerCase()).toContain("simplicity");
});
it("contains Maintainability dimension instruction", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result.toLowerCase()).toContain("maintainability");
});
it("contains Security dimension instruction", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).toMatch(/security/i);
});
it("contains Scalability dimension instruction", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result.toLowerCase()).toContain("scalability");
});
it("contains Operational burden dimension instruction", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result.toLowerCase()).toContain("operational burden");
});
it("contains Vendor lock-in dimension instruction", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result.toLowerCase()).toContain("vendor lock-in");
});
it("contains Local-first / self-hosted fit dimension instruction", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result.toLowerCase()).toMatch(/local-?first.*self-?hosted\s+fit|self-?hosted.*local-?first/i);
});
it("contains Integration risk dimension instruction", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result.toLowerCase()).toContain("integration risk");
});
it("contains Migration risk dimension instruction", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result.toLowerCase()).toContain("migration risk");
});
it("contains Appropriateness for current project stage dimension instruction", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).toMatch(/appropriateness.*project\s+stage|current\s+project\s+stage/i);
});
// --- Response structure (8 sections) ---
it("requests Summary section in response structure", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).toMatch(/summary/i);
});
it("requests Recommendation section in response structure", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).toMatch(/recommendation/i);
});
it("requests Trade-offs section in response structure", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).toMatch(/trade-?offs?/i);
});
it("requests Risks section in response structure", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).toMatch(/risks/i);
});
it("requests Future Extension Paths section in response structure", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).toMatch(/future\s+extension\s+paths/i);
});
it("requests Simpler Alternatives section in response structure", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result.toLowerCase()).toContain("simpler alternatives");
});
it("requests Implementation Notes section in response structure", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).toMatch(/implementation\s+notes/i);
});
it("requests Decision Confidence section in response structure", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).toMatch(/decision\s+confidence/i);
});
// --- Question field ---
it("appends the input question verbatim in the task section", () => {
const result = buildArchitectureReviewPrompt({ question: "Should we migrate to S3?" });
expect(result).toContain("Question: Should we migrate to S3?");
});
// --- Optional fields appended correctly (with question) ---
it("appends Context when context is provided", () => {
const result = buildArchitectureReviewPrompt({
question: "Should we use a managed vector DB?",
context: "Current stack uses PostgreSQL with pgvector for search.",
});
expect(result).toContain("Context: Current stack uses PostgreSQL with pgvector for search.");
});
it("does NOT append Context section when context is empty", () => {
const result = buildArchitectureReviewPrompt({ question: "Test", context: "" });
expect(result).not.toContain("Context:");
});
it("appends Constraints list when constraints are provided", () => {
const result = buildArchitectureReviewPrompt({
question: "Should we use a managed vector DB?",
constraints: ["Must be self-hostable", "Budget under $100/mo"],
});
expect(result).toContain("Constraints:");
expect(result).toContain("- Must be self-hostable");
expect(result).toContain("- Budget under $100/mo");
});
it("does NOT append Constraints section when constraints array is empty", () => {
const result = buildArchitectureReviewPrompt({ question: "Test", constraints: [] });
expect(result).not.toContain("Constraints:");
});
it("appends Expected output when expectedOutput is provided", () => {
const result = buildArchitectureReviewPrompt({
question: "Should we use a managed vector DB?",
expectedOutput: "A trade-off analysis comparing options.",
});
expect(result).toContain(
"Expected output: A trade-off analysis comparing options."
);
});
it("does NOT append Expected output section when expectedOutput is omitted", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).not.toContain("Expected output:");
});
it("does NOT append Expected output section when expectedOutput is empty", () => {
const result = buildArchitectureReviewPrompt({ question: "Test", expectedOutput: "" });
expect(result).not.toContain("Expected output:");
});
it("appends all optional fields alongside each other", () => {
const result = buildArchitectureReviewPrompt({
question: "Should we migrate to S3?",
context: "Current stack uses local disk with Express.",
constraints: ["Must be deployable without Kubernetes", "Budget under $100/mo"],
expectedOutput: "A comparison of S3, MinIO, and shared NFS.",
projectSummary: "Internal document management system for a 50-person team.",
taskSummary: "Migrating from local filesystem to scalable object storage.",
});
expect(result).toContain("Question: Should we migrate to S3?");
expect(result).toContain("Context: Current stack uses local disk with Express.");
expect(result).toContain("Constraints:");
expect(result).toContain("- Must be deployable without Kubernetes");
expect(result).toContain("- Budget under $100/mo");
expect(result).toContain("Expected output: A comparison of S3, MinIO, and shared NFS.");
expect(result).toContain(
"Project summary: Internal document management system for a 50-person team."
);
expect(result).toContain(
"Task summary: Migrating from local filesystem to scalable object storage."
);
});
it("does NOT append Project summary when projectSummary is omitted", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).not.toContain("Project summary:");
});
it("does NOT append Task summary when taskSummary is omitted", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).not.toContain("Task summary:");
});
// --- relevantFiles (present) ---
it("includes file section header when relevantFiles is provided", () => {
const result = buildArchitectureReviewPrompt({
question: "Should we migrate to S3?",
relevantFiles: [
{ path: "src/storage/local.js", content: "fs.writeFileSync(path, data);" },
],
});
expect(result).toContain("Relevant files:");
});
it("includes file path, language, and content when relevantFiles has language", () => {
const result = buildArchitectureReviewPrompt({
question: "Should we migrate to S3?",
relevantFiles: [
{ path: "src/storage/local.js", language: "javascript", content: "fs.writeFileSync(path, data);" },
],
});
expect(result).toContain("File: src/storage/local.js");
expect(result).toContain("Language: javascript");
expect(result).toContain("fs.writeFileSync(path, data);");
});
it("includes file content without language header when language is omitted", () => {
const result = buildArchitectureReviewPrompt({
question: "Should we migrate to S3?",
relevantFiles: [
{ path: "src/storage/local.js", content: "fs.writeFileSync(path, data);" },
],
});
expect(result).toContain("File: src/storage/local.js");
expect(result).not.toContain("Language:");
expect(result).toContain("fs.writeFileSync(path, data);");
});
it("includes multiple files when relevantFiles has multiple entries", () => {
const result = buildArchitectureReviewPrompt({
question: "Should we migrate to S3?",
relevantFiles: [
{ path: "src/storage/local.js", language: "javascript", content: "fs.writeFileSync(path, data);" },
{ path: "src/middleware/upload.js", language: "javascript", content: "app.post('/upload', handler);" },
],
});
expect(result).toContain("File: src/storage/local.js");
expect(result).toContain("File: src/middleware/upload.js");
expect(result).toContain("fs.writeFileSync(path, data);");
expect(result).toContain("app.post('/upload', handler);");
});
// --- relevantFiles (absent/empty) ---
it("does NOT include Relevant files section when relevantFiles is omitted", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).not.toContain("Relevant files:");
});
it("does NOT include Relevant files section when relevantFiles is empty array", () => {
const result = buildArchitectureReviewPrompt({ question: "Test", relevantFiles: [] });
expect(result).not.toContain("Relevant files:");
});
// --- Tool-specific guard ---
it("does NOT contain debug_issue specific content", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).not.toContain("Likely Causes");
expect(result).not.toContain("Fast Checks");
});
it("does NOT contain review_plan specific content", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result).not.toContain("missing steps");
expect(result).not.toContain("scope creep");
});
it("does NOT contain review_code specific content", () => {
const result = buildArchitectureReviewPrompt({ question: "Test" });
expect(result.toLowerCase()).not.toContain("issues by severity");
expect(result.toLowerCase()).not.toContain("suggested fixes");
});
// --- Input validation ---
it("throws TypeError when input is missing, null, or undefined", () => {
expect(() => buildArchitectureReviewPrompt()).toThrow(TypeError);
expect(() => buildArchitectureReviewPrompt(null)).toThrow(TypeError);
expect(() => buildArchitectureReviewPrompt(undefined)).toThrow(TypeError);
});
it("throws TypeError when question is missing", () => {
expect(() => buildArchitectureReviewPrompt({ context: "something" })).toThrow(TypeError);
});
it("throws TypeError when question is empty", () => {
expect(() => buildArchitectureReviewPrompt({ question: "" })).toThrow(TypeError);
});
// --- Full integration test ---
it("returns a well-formed two-part prompt with all fields and relevantFiles", () => {
const result = buildArchitectureReviewPrompt({
question: "Should we migrate our file storage from local filesystem to an object store?",
context: "Current system stores uploads on the application server's disk. We're getting disk space alerts and need multi-server support.",
constraints: ["Must be deployable without Kubernetes", "Cost must not exceed current $50/mo by more than 50%"],
expectedOutput: "A comparison of S3, MinIO, and shared NFS with trade-off analysis.",
projectSummary: "Internal document management system for a mid-sized company.",
taskSummary: "Migrating from local disk storage to a scalable file storage solution.",
relevantFiles: [
{
path: "src/storage/local-filesystem.js",
language: "javascript",
content: `const fs = require('fs');\n\nfunction saveFile(name, data) {\n fs.writeFileSync(\`./uploads/\${name}\`, data);\n}`,
},
{
path: "src/middleware/upload.js",
content: `app.post('/upload', upload.single('file'), (req, res) => { ... });`,
},
],
});
// Base section present
expect(result).toContain("second-opinion assistant");
expect(result).toContain("Rules:");
// Explicit separator — exactly one
const parts = result.split("\n\n---\n\n");
expect(parts.length).toBe(2);
// Tool-specific section present (second part)
const toolSection = parts[1];
expect(toolSection).toContain("architecture advisor");
expect(toolSection).toContain("Claude Code remains the implementation agent");
expect(toolSection).toMatch(/use\s+only\s+the?\s+supplied\s+context/i);
// Guard rails present
expect(toolSection).toMatch(/prefer\s+simple.*incremental.*architecture/i);
expect(toolSection).toMatch(/avoid\s+large\s+rewrites.*unless.*justified/i);
// All 10 review dimensions
expect(toolSection.toLowerCase()).toContain("simplicity");
expect(toolSection.toLowerCase()).toContain("maintainability");
expect(toolSection).toMatch(/security/i);
expect(toolSection.toLowerCase()).toContain("scalability");
expect(toolSection.toLowerCase()).toContain("operational burden");
expect(toolSection.toLowerCase()).toContain("vendor lock-in");
expect(toolSection.toLowerCase()).toMatch(/local-?first.*self-?hosted\s+fit|self-?hosted.*local-?first/i);
expect(toolSection.toLowerCase()).toContain("integration risk");
expect(toolSection.toLowerCase()).toContain("migration risk");
expect(toolSection).toMatch(/appropriateness.*project\s+stage|current\s+project\s+stage/i);
// All 8 response structure sections
expect(toolSection).toMatch(/summary/i);
expect(toolSection).toMatch(/recommendation/i);
expect(toolSection).toMatch(/trade-?offs?/i);
expect(toolSection).toMatch(/risks/i);
expect(toolSection).toMatch(/future\s+extension\s+paths/i);
expect(toolSection.toLowerCase()).toContain("simpler alternatives");
expect(toolSection).toMatch(/implementation\s+notes/i);
expect(toolSection).toMatch(/decision\s+confidence/i);
// All fields present verbatim
expect(result).toContain("Question: Should we migrate our file storage from local filesystem to an object store?");
expect(result).toContain("Context: Current system stores uploads on the application server's disk. We're getting disk space alerts and need multi-server support.");
expect(result).toContain("Constraints:");
expect(result).toContain("- Must be deployable without Kubernetes");
expect(result).toContain("- Cost must not exceed current $50/mo by more than 50%");
expect(result).toContain(
"Expected output: A comparison of S3, MinIO, and shared NFS with trade-off analysis."
);
expect(result).toContain(
"Project summary: Internal document management system for a mid-sized company."
);
expect(result).toContain(
"Task summary: Migrating from local disk storage to a scalable file storage solution."
);
// Relevant files present
expect(toolSection).toContain("Relevant files:");
expect(toolSection).toContain("File: src/storage/local-filesystem.js");
expect(toolSection).toContain("Language: javascript");
expect(toolSection).toContain("fs.writeFileSync");
expect(toolSection).toContain("File: src/middleware/upload.js");
// Does not contain other tool content
expect(toolSection).not.toContain("Likely Causes");
expect(toolSection).not.toContain("Fast Checks");
expect(toolSection).not.toContain("missing steps");
expect(toolSection).not.toContain("scope creep");
expect(toolSection.toLowerCase()).not.toContain("issues by severity");
});
});