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"); }); });