Skip to main content
Glama

documcp

by tosin2013
validate-content.test.ts42.9 kB
import { describe, it, expect, beforeEach, afterEach } from "@jest/globals"; import * as fs from "fs/promises"; import * as path from "path"; import { handleValidateDiataxisContent, validateGeneralContent, } from "../../src/tools/validate-content.js"; import { ValidationResult } from "../../src/tools/validate-content.js"; describe("Content Validation Tool", () => { const testTempDir = path.join(__dirname, "../../.tmp/test-validation"); beforeEach(async () => { // Create test directory await fs.mkdir(testTempDir, { recursive: true }); }); afterEach(async () => { // Clean up test directory try { await fs.rm(testTempDir, { recursive: true }); } catch { // Ignore cleanup errors } }); describe("Application Code Validation", () => { it("should detect application code path correctly", async () => { // Create mock application structure const appDir = path.join(testTempDir, "mock-app"); await fs.mkdir(appDir, { recursive: true }); await fs.mkdir(path.join(appDir, "src"), { recursive: true }); await fs.writeFile( path.join(appDir, "package.json"), '{"name": "test-app"}', ); // Create TypeScript file without documentation const tsFile = path.join(appDir, "src", "index.ts"); await fs.writeFile( tsFile, ` export function undocumentedFunction(param: string): string { return param.toUpperCase(); } export const anotherFunction = (value: number) => { if (value < 0) { throw new Error('Invalid value'); } return value * 2; }; `.trim(), ); const result = await handleValidateDiataxisContent({ contentPath: appDir, validationType: "compliance", includeCodeValidation: true, }); expect(result).toBeDefined(); expect(result.issues).toBeDefined(); // Should find issues with undocumented exported functions const undocumentedIssues = result.issues.filter((issue) => issue.description.includes("lacks documentation"), ); expect(undocumentedIssues.length).toBeGreaterThan(0); // Should find issues with undocumented error throwing const errorDocIssues = result.issues.filter((issue) => issue.description.includes( "Error throwing code found without error documentation", ), ); expect(errorDocIssues.length).toBeGreaterThan(0); }); it("should validate application architecture structure", async () => { // Create mock application with missing directories const appDir = path.join(testTempDir, "incomplete-app"); await fs.mkdir(appDir, { recursive: true }); await fs.writeFile( path.join(appDir, "package.json"), '{"name": "incomplete-app"}', ); // Missing tools and types directories await fs.mkdir(path.join(appDir, "src"), { recursive: true }); await fs.writeFile( path.join(appDir, "src", "index.ts"), 'export const app = "test";', ); const result = await handleValidateDiataxisContent({ contentPath: appDir, validationType: "compliance", includeCodeValidation: false, }); const structureIssues = result.issues.filter( (issue) => issue.location.file === "application structure", ); expect(structureIssues.length).toBeGreaterThan(0); // Should suggest missing tools directory const toolsIssue = structureIssues.find((issue) => issue.description.includes("tools directory"), ); expect(toolsIssue).toBeDefined(); }); it("should validate README structure", async () => { const appDir = path.join(testTempDir, "readme-test"); await fs.mkdir(appDir, { recursive: true }); await fs.mkdir(path.join(appDir, "src"), { recursive: true }); await fs.writeFile( path.join(appDir, "package.json"), '{"name": "readme-test"}', ); await fs.writeFile( path.join(appDir, "src", "index.ts"), 'export const app = "test";', ); // Create README with missing sections await fs.writeFile( path.join(appDir, "README.md"), ` This is a project without proper structure. Some description here. `.trim(), ); const result = await handleValidateDiataxisContent({ contentPath: appDir, validationType: "compliance", includeCodeValidation: false, }); // Application validation should find issues const readmeIssues = result.issues.filter( (issue) => issue.location.file === "README.md", ); expect(readmeIssues.length).toBeGreaterThan(0); // Should find issues with README structure const structureIssue = readmeIssues.find((issue) => issue.description.includes("lacks essential sections"), ); expect(structureIssue).toBeDefined(); }); it("should detect properly documented functions", async () => { const appDir = path.join(testTempDir, "documented-app"); await fs.mkdir(appDir, { recursive: true }); await fs.mkdir(path.join(appDir, "src"), { recursive: true }); await fs.writeFile( path.join(appDir, "package.json"), '{"name": "documented-app"}', ); // Create well-documented TypeScript file const tsFile = path.join(appDir, "src", "documented.ts"); await fs.writeFile( tsFile, ` /** * Converts a string to uppercase * @param param - The input string * @returns The uppercase string */ export function documentedFunction(param: string): string { return param.toUpperCase(); } /** * Doubles a positive number * @param value - The input number (must be positive) * @returns The doubled value * @throws {Error} When value is negative */ export const wellDocumentedFunction = (value: number) => { if (value < 0) { throw new Error('Invalid value'); } return value * 2; }; `.trim(), ); const result = await handleValidateDiataxisContent({ contentPath: appDir, validationType: "compliance", includeCodeValidation: true, }); // Should have no undocumented issues since functions are properly documented const undocumentedIssues = result.issues.filter((issue) => issue.description.includes("lacks documentation"), ); expect(undocumentedIssues.length).toBe(0); // Should not complain about error documentation const errorDocIssues = result.issues.filter((issue) => issue.description.includes( "Error throwing code found without error documentation", ), ); expect(errorDocIssues.length).toBe(0); }); }); describe("Documentation Validation", () => { it("should detect documentation directory correctly", async () => { // Create mock documentation structure const docsDir = path.join(testTempDir, "docs"); await fs.mkdir(docsDir, { recursive: true }); await fs.mkdir(path.join(docsDir, "tutorials"), { recursive: true }); await fs.writeFile( path.join(docsDir, "tutorials", "tutorial1.md"), ` # Tutorial 1 This is a tutorial without prerequisites section. \`\`\`javascript console.log("hello") \`\`\` `.trim(), ); const result = await handleValidateDiataxisContent({ contentPath: docsDir, validationType: "compliance", includeCodeValidation: true, }); expect(result).toBeDefined(); // Should find Diataxis compliance issues const complianceIssues = result.issues.filter( (issue) => issue.category === "compliance", ); expect(complianceIssues.length).toBeGreaterThan(0); // Should find missing prerequisites in tutorial const prereqIssue = complianceIssues.find((issue) => issue.description.includes("prerequisites"), ); expect(prereqIssue).toBeDefined(); }); it("should validate link integrity", async () => { const docsDir = path.join(testTempDir, "docs-links"); await fs.mkdir(docsDir, { recursive: true }); // Create file with broken internal link await fs.writeFile( path.join(docsDir, "index.md"), ` # Documentation [Broken Link](./nonexistent.md) [Another Link](./other.md) `.trim(), ); // Create the referenced file await fs.writeFile(path.join(docsDir, "other.md"), "# Other Page"); const result = await handleValidateDiataxisContent({ contentPath: docsDir, validationType: "accuracy", includeCodeValidation: false, }); const linkIssues = result.issues.filter((issue) => issue.description.includes("Broken internal link"), ); expect(linkIssues.length).toBe(1); const brokenLink = linkIssues[0]; expect(brokenLink.description).toContain("nonexistent.md"); }); it("should validate code blocks in documentation", async () => { const docsDir = path.join(testTempDir, "docs-code"); await fs.mkdir(docsDir, { recursive: true }); await fs.writeFile( path.join(docsDir, "guide.md"), ` # Code Examples \`\`\`javascript // Missing semicolon console.log("test") \`\`\` \`\`\`json { "valid": "json" } \`\`\` \`\`\`json { "invalid": json } \`\`\` `.trim(), ); const result = await handleValidateDiataxisContent({ contentPath: docsDir, validationType: "all", includeCodeValidation: true, }); expect(result.codeValidation).toBeDefined(); expect(result.codeValidation!.exampleResults.length).toBeGreaterThan(0); // Should find JSON syntax error const jsonErrors = result.codeValidation!.exampleResults.filter((ex) => ex.issues.some((issue) => issue.description.includes("Invalid JSON")), ); expect(jsonErrors.length).toBeGreaterThan(0); }); }); describe("General Content Validation", () => { it("should validate general content with link checking", async () => { const contentDir = path.join(testTempDir, "general-content"); await fs.mkdir(contentDir, { recursive: true }); await fs.writeFile( path.join(contentDir, "page.md"), ` # Test Page [Good Link](./existing.md) [Bad Link](./missing.md) \`\`\`js console.log("missing semicolon") \`\`\` `.trim(), ); await fs.writeFile( path.join(contentDir, "existing.md"), "# Existing Page", ); const result = await validateGeneralContent({ contentPath: contentDir, validationType: "all", includeCodeValidation: true, }); expect(result.success).toBe(false); expect(result.brokenLinks.length).toBe(1); expect(result.brokenLinks[0]).toContain("missing.md"); expect(result.codeBlocksValidated).toBeGreaterThan(0); expect(result.codeErrors.length).toBeGreaterThan(0); expect(result.recommendations.length).toBeGreaterThan(0); }); it("should pass validation for clean content", async () => { const contentDir = path.join(testTempDir, "clean-content"); await fs.mkdir(contentDir, { recursive: true }); await fs.writeFile( path.join(contentDir, "clean.md"), ` # Clean Page [Good Link](./other.md) \`\`\`json { "valid": "json" } \`\`\` `.trim(), ); await fs.writeFile(path.join(contentDir, "other.md"), "# Other Page"); const result = await validateGeneralContent({ contentPath: contentDir, validationType: "all", includeCodeValidation: true, }); expect(result.success).toBe(true); expect(result.brokenLinks.length).toBe(0); expect(result.recommendations).toContain( "Content validation passed - no critical issues found", ); }); }); describe("Confidence Metrics", () => { it("should calculate confidence metrics correctly", async () => { const appDir = path.join(testTempDir, "confidence-test"); await fs.mkdir(appDir, { recursive: true }); await fs.mkdir(path.join(appDir, "src"), { recursive: true }); await fs.writeFile( path.join(appDir, "package.json"), '{"name": "confidence-test"}', ); await fs.writeFile( path.join(appDir, "src", "index.ts"), 'export const test = "value";', ); const result = await handleValidateDiataxisContent({ contentPath: appDir, validationType: "all", includeCodeValidation: true, }); expect(result.confidence).toBeDefined(); expect(result.confidence.overall).toBeGreaterThan(0); expect(result.confidence.overall).toBeLessThanOrEqual(100); expect(result.confidence.breakdown).toBeDefined(); expect(result.confidence.breakdown.technologyDetection).toBeDefined(); expect(result.confidence.breakdown.codeExampleRelevance).toBeDefined(); expect( result.confidence.breakdown.architecturalAssumptions, ).toBeDefined(); }); it("should provide recommendations based on confidence", async () => { const appDir = path.join(testTempDir, "recommendations-test"); await fs.mkdir(appDir, { recursive: true }); await fs.writeFile( path.join(appDir, "package.json"), '{"name": "recommendations-test"}', ); // Create content that will generate issues await fs.writeFile(path.join(appDir, "README.md"), "No proper structure"); const result = await handleValidateDiataxisContent({ contentPath: appDir, validationType: "all", includeCodeValidation: false, }); expect(result.recommendations).toBeDefined(); expect(result.recommendations.length).toBeGreaterThan(0); expect(result.nextSteps).toBeDefined(); expect(result.nextSteps.length).toBeGreaterThan(0); if (result.confidence.overall < 70) { expect( result.recommendations.some((rec) => rec.includes("comprehensive review"), ), ).toBe(true); } }); }); describe("Error Handling and Edge Cases", () => { it("should handle non-existent content path gracefully", async () => { const nonExistentPath = path.join(testTempDir, "does-not-exist"); const result = await handleValidateDiataxisContent({ contentPath: nonExistentPath, validationType: "all", includeCodeValidation: false, }); expect(result).toBeDefined(); // The function handles non-existent paths gracefully but may still succeed expect(result.confidence).toBeDefined(); }); it("should handle empty directory", async () => { const emptyDir = path.join(testTempDir, "empty-dir"); await fs.mkdir(emptyDir, { recursive: true }); const result = await handleValidateDiataxisContent({ contentPath: emptyDir, validationType: "all", includeCodeValidation: true, }); expect(result).toBeDefined(); expect(result.confidence.breakdown.architecturalAssumptions).toBeLessThan( 80, ); }); it("should handle project context loading with analysis ID", async () => { const appDir = path.join(testTempDir, "context-test"); await fs.mkdir(appDir, { recursive: true }); await fs.writeFile( path.join(appDir, "package.json"), '{"name": "context-test"}', ); // Create .documcp directory with analysis const docucmpDir = path.join(appDir, ".documcp", "analyses"); await fs.mkdir(docucmpDir, { recursive: true }); await fs.writeFile( path.join(docucmpDir, "test-analysis.json"), JSON.stringify({ metadata: { projectName: "test-project", primaryLanguage: "TypeScript", }, technologies: { framework: "React" }, dependencies: { packages: ["react", "typescript"] }, }), ); const result = await handleValidateDiataxisContent({ contentPath: appDir, analysisId: "test-analysis", validationType: "accuracy", includeCodeValidation: false, }); expect(result).toBeDefined(); expect(result.confidence).toBeDefined(); }); it("should handle missing analysis ID gracefully", async () => { const appDir = path.join(testTempDir, "missing-analysis"); await fs.mkdir(appDir, { recursive: true }); await fs.writeFile( path.join(appDir, "package.json"), '{"name": "missing-analysis"}', ); const result = await handleValidateDiataxisContent({ contentPath: appDir, analysisId: "non-existent-analysis", validationType: "accuracy", includeCodeValidation: false, }); expect(result).toBeDefined(); expect(result.confidence).toBeDefined(); }); it("should detect documentation directory correctly", async () => { const docsPath = path.join(testTempDir, "project", "docs"); await fs.mkdir(docsPath, { recursive: true }); await fs.writeFile(path.join(docsPath, "index.md"), "# Documentation"); const result = await handleValidateDiataxisContent({ contentPath: docsPath, validationType: "compliance", includeCodeValidation: false, }); expect(result).toBeDefined(); expect(result.confidence).toBeDefined(); // Documentation directory should be processed expect( result.confidence.breakdown.architecturalAssumptions, ).toBeGreaterThan(0); }); it("should handle different validation types", async () => { const appDir = path.join(testTempDir, "validation-types"); await fs.mkdir(appDir, { recursive: true }); await fs.writeFile( path.join(appDir, "test.md"), "# Test\n[broken link](./missing.md)", ); // Test accuracy only const accuracyResult = await handleValidateDiataxisContent({ contentPath: appDir, validationType: "accuracy", includeCodeValidation: false, }); expect(accuracyResult).toBeDefined(); // Test completeness only const completenessResult = await handleValidateDiataxisContent({ contentPath: appDir, validationType: "completeness", includeCodeValidation: false, }); expect(completenessResult).toBeDefined(); // Test compliance only const complianceResult = await handleValidateDiataxisContent({ contentPath: appDir, validationType: "compliance", includeCodeValidation: false, }); expect(complianceResult).toBeDefined(); }); it("should handle code validation failure scenarios", async () => { const appDir = path.join(testTempDir, "code-validation-fail"); await fs.mkdir(appDir, { recursive: true }); // Create markdown with broken code examples await fs.writeFile( path.join(appDir, "broken-code.md"), ` # Broken Code Examples \`\`\`javascript // Syntax error console.log("missing quote); \`\`\` \`\`\`json { "invalid": json } \`\`\` `.trim(), ); const result = await handleValidateDiataxisContent({ contentPath: appDir, validationType: "all", includeCodeValidation: true, }); expect(result.codeValidation).toBeDefined(); expect(result.codeValidation!.overallSuccess).toBe(false); expect( result.recommendations.some((rec) => rec.includes("Fix code examples")), ).toBe(true); }); it("should generate risk factors for critical issues", async () => { const appDir = path.join(testTempDir, "risk-factors"); await fs.mkdir(appDir, { recursive: true }); // Create content with multiple critical issues await fs.writeFile( path.join(appDir, "critical-issues.md"), ` # Critical Issues [Broken Link 1](./missing1.md) [Broken Link 2](./missing2.md) [Broken Link 3](./missing3.md) `.trim(), ); const result = await handleValidateDiataxisContent({ contentPath: appDir, validationType: "all", includeCodeValidation: false, }); expect(result.confidence.riskFactors).toBeDefined(); expect(result.confidence.riskFactors.length).toBeGreaterThan(0); const highRiskFactors = result.confidence.riskFactors.filter( (rf) => rf.type === "high", ); expect(highRiskFactors.length).toBeGreaterThan(0); }); it("should handle uncertainty flags and medium risk factors", async () => { const appDir = path.join(testTempDir, "uncertainty-test"); await fs.mkdir(appDir, { recursive: true }); // Create content that generates uncertainties await fs.writeFile( path.join(appDir, "uncertain.md"), ` # Uncertain Content This content has many ambiguous references and unclear instructions. Multiple areas need clarification for proper understanding. `.trim(), ); const result = await handleValidateDiataxisContent({ contentPath: appDir, validationType: "all", includeCodeValidation: false, }); // Manually add uncertainties to test the risk factor generation result.uncertainties = [ { area: "test1", severity: "high", description: "test", potentialImpact: "test", clarificationNeeded: "test", fallbackStrategy: "test", }, { area: "test2", severity: "high", description: "test", potentialImpact: "test", clarificationNeeded: "test", fallbackStrategy: "test", }, { area: "test3", severity: "high", description: "test", potentialImpact: "test", clarificationNeeded: "test", fallbackStrategy: "test", }, { area: "test4", severity: "high", description: "test", potentialImpact: "test", clarificationNeeded: "test", fallbackStrategy: "test", }, { area: "test5", severity: "high", description: "test", potentialImpact: "test", clarificationNeeded: "test", fallbackStrategy: "test", }, { area: "test6", severity: "high", description: "test", potentialImpact: "test", clarificationNeeded: "test", fallbackStrategy: "test", }, ]; expect(result.uncertainties.length).toBeGreaterThan(5); const highUncertainties = result.uncertainties.filter( (u) => u.severity === "high" || u.severity === "critical", ); expect(highUncertainties.length).toBeGreaterThan(0); }); it("should handle Diataxis structure analysis", async () => { const docsDir = path.join(testTempDir, "diataxis-structure"); await fs.mkdir(docsDir, { recursive: true }); // Create Diataxis structure await fs.mkdir(path.join(docsDir, "tutorials"), { recursive: true }); await fs.mkdir(path.join(docsDir, "how-to"), { recursive: true }); await fs.mkdir(path.join(docsDir, "reference"), { recursive: true }); await fs.mkdir(path.join(docsDir, "explanation"), { recursive: true }); await fs.writeFile( path.join(docsDir, "tutorials", "tutorial.md"), "# Tutorial", ); await fs.writeFile( path.join(docsDir, "how-to", "guide.md"), "# How-to Guide", ); await fs.writeFile( path.join(docsDir, "reference", "api.md"), "# API Reference", ); await fs.writeFile( path.join(docsDir, "explanation", "concept.md"), "# Explanation", ); const result = await handleValidateDiataxisContent({ contentPath: docsDir, validationType: "compliance", includeCodeValidation: false, }); expect(result).toBeDefined(); expect( result.confidence.breakdown.architecturalAssumptions, ).toBeGreaterThan(60); }); it("should handle successful validation with no issues", async () => { const cleanDir = path.join(testTempDir, "clean-validation"); await fs.mkdir(cleanDir, { recursive: true }); // Create clean content with no issues await fs.writeFile( path.join(cleanDir, "clean.md"), ` # Clean Documentation This is well-structured documentation with no issues. \`\`\`json { "valid": "json" } \`\`\` `.trim(), ); const result = await handleValidateDiataxisContent({ contentPath: cleanDir, validationType: "all", includeCodeValidation: true, }); // Should have minimal issues and good confidence expect(result.confidence.overall).toBeGreaterThan(0); expect(result.recommendations).toBeDefined(); expect(result.recommendations.length).toBeGreaterThan(0); }); it("should handle timeout scenarios", async () => { // Test timeout handling by creating a scenario that might take time const largeDir = path.join(testTempDir, "timeout-test"); await fs.mkdir(largeDir, { recursive: true }); // Create multiple markdown files to simulate processing time for (let i = 0; i < 5; i++) { await fs.writeFile( path.join(largeDir, `file${i}.md`), ` # File ${i} Content for file ${i} with some text. \`\`\`javascript console.log("File ${i}"); \`\`\` `.trim(), ); } const result = await handleValidateDiataxisContent({ contentPath: largeDir, validationType: "all", includeCodeValidation: true, }); expect(result).toBeDefined(); expect(result.confidence).toBeDefined(); }); it("should handle confidence levels and validation modes", async () => { const testDir = path.join(testTempDir, "confidence-levels"); await fs.mkdir(testDir, { recursive: true }); await fs.writeFile(path.join(testDir, "test.md"), "# Test Content"); // Test different confidence levels const strictResult = await handleValidateDiataxisContent({ contentPath: testDir, validationType: "all", includeCodeValidation: false, confidence: "strict", }); expect(strictResult).toBeDefined(); const moderateResult = await handleValidateDiataxisContent({ contentPath: testDir, validationType: "all", includeCodeValidation: false, confidence: "moderate", }); expect(moderateResult).toBeDefined(); const permissiveResult = await handleValidateDiataxisContent({ contentPath: testDir, validationType: "all", includeCodeValidation: false, confidence: "permissive", }); expect(permissiveResult).toBeDefined(); }); it("should handle TypeScript files without package.json", async () => { const tsDir = path.join(testTempDir, "typescript-only"); await fs.mkdir(tsDir, { recursive: true }); await fs.mkdir(path.join(tsDir, "src"), { recursive: true }); // Create TypeScript files without package.json await fs.writeFile( path.join(tsDir, "src", "app.ts"), ` export class TestClass { public method(): void { console.log('test'); } } `.trim(), ); const result = await handleValidateDiataxisContent({ contentPath: tsDir, validationType: "compliance", includeCodeValidation: false, }); expect(result).toBeDefined(); expect(result.confidence).toBeDefined(); }); it("should handle mixed content scenarios", async () => { const mixedDir = path.join(testTempDir, "mixed-content"); await fs.mkdir(mixedDir, { recursive: true }); await fs.mkdir(path.join(mixedDir, "src"), { recursive: true }); // Create both application and documentation content await fs.writeFile( path.join(mixedDir, "package.json"), '{"name": "mixed-app"}', ); await fs.writeFile( path.join(mixedDir, "src", "index.ts"), 'export const app = "test";', ); await fs.writeFile( path.join(mixedDir, "README.md"), ` # Mixed Content App ## Installation Run \`npm install\` ## Usage See the documentation. `.trim(), ); const result = await handleValidateDiataxisContent({ contentPath: mixedDir, validationType: "all", includeCodeValidation: true, }); expect(result).toBeDefined(); expect( result.confidence.breakdown.architecturalAssumptions, ).toBeGreaterThanOrEqual(60); }); it("should handle business context alignment scoring", async () => { const businessDir = path.join(testTempDir, "business-context"); await fs.mkdir(businessDir, { recursive: true }); // Create content with business context await fs.writeFile( path.join(businessDir, "business.md"), ` # Business Requirements This application serves enterprise customers with specific needs. The solution addresses market requirements and business objectives. `.trim(), ); const result = await handleValidateDiataxisContent({ contentPath: businessDir, validationType: "all", includeCodeValidation: false, }); expect(result).toBeDefined(); expect( result.confidence.breakdown.businessContextAlignment, ).toBeGreaterThanOrEqual(0); }); it("should handle deprecated patterns in technical accuracy checks", async () => { const deprecatedDir = path.join(testTempDir, "deprecated-patterns"); await fs.mkdir(deprecatedDir, { recursive: true }); await fs.writeFile( path.join(deprecatedDir, "deprecated.md"), ` # Deprecated Patterns \`\`\`bash npm install -g some-package \`\`\` \`\`\`javascript var oldVariable = "test"; function() { console.log("old style"); } \`\`\` Visit http://example.com for more info. `.trim(), ); const result = await handleValidateDiataxisContent({ contentPath: deprecatedDir, validationType: "accuracy", includeCodeValidation: false, }); const deprecatedIssues = result.issues.filter((issue) => issue.description.includes("Potentially outdated pattern"), ); expect(deprecatedIssues.length).toBeGreaterThan(0); }); it("should handle async code without error handling", async () => { const asyncDir = path.join(testTempDir, "async-code"); await fs.mkdir(asyncDir, { recursive: true }); await fs.writeFile( path.join(asyncDir, "async.md"), ` # Async Code Examples \`\`\`javascript async function fetchData() { const response = await fetch('/api/data'); return response.json(); } \`\`\` \`\`\`typescript const getData = async (): Promise<any> => { const result = await someAsyncOperation(); return result; }; \`\`\` `.trim(), ); const result = await handleValidateDiataxisContent({ contentPath: asyncDir, validationType: "accuracy", includeCodeValidation: false, }); const asyncIssues = result.issues.filter((issue) => issue.description.includes("Async code without error handling"), ); expect(asyncIssues.length).toBeGreaterThan(0); }); it("should handle version compatibility checks with project context", async () => { const versionDir = path.join(testTempDir, "version-compat"); await fs.mkdir(versionDir, { recursive: true }); // Create .documcp directory with analysis const docucmpDir = path.join(versionDir, ".documcp", "analyses"); await fs.mkdir(docucmpDir, { recursive: true }); await fs.writeFile( path.join(docucmpDir, "version-analysis.json"), JSON.stringify({ metadata: { projectName: "version-test", primaryLanguage: "TypeScript", }, technologies: { framework: "React" }, dependencies: { packages: ["react@18.2.0", "typescript@4.9.0"] }, }), ); await fs.writeFile( path.join(versionDir, "versions.md"), ` # Version Information This project uses React @18.2.0 and TypeScript @4.9.0. Also compatible with Node.js @16.14.0. `.trim(), ); const result = await handleValidateDiataxisContent({ contentPath: versionDir, analysisId: "version-analysis", validationType: "accuracy", includeCodeValidation: false, }); const versionUncertainties = result.uncertainties.filter( (u) => u.area === "version-compatibility", ); expect(versionUncertainties.length).toBeGreaterThan(0); }); it("should handle dangerous bash commands", async () => { const bashDir = path.join(testTempDir, "dangerous-bash"); await fs.mkdir(bashDir, { recursive: true }); await fs.writeFile( path.join(bashDir, "dangerous.md"), ` # Dangerous Commands \`\`\`bash rm -rf / sudo rm -rf /tmp/important chmod 777 /etc/passwd command > /dev/null 2>&1 \`\`\` `.trim(), ); const result = await handleValidateDiataxisContent({ contentPath: bashDir, validationType: "accuracy", includeCodeValidation: false, }); const dangerousIssues = result.issues.filter((issue) => issue.description.includes("Potentially dangerous command"), ); expect(dangerousIssues.length).toBeGreaterThan(0); }); it("should handle mixed path separators in commands", async () => { const pathDir = path.join(testTempDir, "mixed-paths"); await fs.mkdir(pathDir, { recursive: true }); await fs.writeFile( path.join(pathDir, "paths.md"), ` # Mixed Path Examples \`\`\`bash cp /unix/path\\windows\\mixed /destination/path \`\`\` `.trim(), ); const result = await handleValidateDiataxisContent({ contentPath: pathDir, validationType: "accuracy", includeCodeValidation: false, }); const pathIssues = result.issues.filter((issue) => issue.description.includes("Mixed path separators"), ); expect(pathIssues.length).toBeGreaterThan(0); }); it("should handle external links in accuracy validation", async () => { const linksDir = path.join(testTempDir, "external-links"); await fs.mkdir(linksDir, { recursive: true }); await fs.writeFile( path.join(linksDir, "external.md"), ` # External Links [GitHub](https://github.com) [Documentation](https://docs.example.com) `.trim(), ); const result = await handleValidateDiataxisContent({ contentPath: linksDir, validationType: "accuracy", includeCodeValidation: false, }); const linkUncertainties = result.uncertainties.filter( (u) => u.area === "external-links", ); expect(linkUncertainties.length).toBeGreaterThan(0); }); it("should handle Diataxis compliance rules for different sections", async () => { const complianceDir = path.join(testTempDir, "diataxis-compliance"); await fs.mkdir(complianceDir, { recursive: true }); // Create directories for each Diataxis section await fs.mkdir(path.join(complianceDir, "tutorials"), { recursive: true, }); await fs.mkdir(path.join(complianceDir, "how-to"), { recursive: true }); await fs.mkdir(path.join(complianceDir, "reference"), { recursive: true, }); await fs.mkdir(path.join(complianceDir, "explanation"), { recursive: true, }); // Tutorial without prerequisites await fs.writeFile( path.join(complianceDir, "tutorials", "bad-tutorial.md"), ` # Bad Tutorial This tutorial doesn't have prerequisites or clear steps. `.trim(), ); // How-to without task focus await fs.writeFile( path.join(complianceDir, "how-to", "bad-howto.md"), ` # Bad Guide Short guide. `.trim(), ); // Reference without structure await fs.writeFile( path.join(complianceDir, "reference", "bad-reference.md"), ` Bad reference without headings or tables. `.trim(), ); // Explanation without "why" await fs.writeFile( path.join(complianceDir, "explanation", "bad-explanation.md"), ` # Bad Explanation Short explanation. `.trim(), ); const result = await handleValidateDiataxisContent({ contentPath: complianceDir, validationType: "compliance", includeCodeValidation: false, }); const complianceIssues = result.issues.filter( (issue) => issue.category === "compliance", ); expect(complianceIssues.length).toBeGreaterThan(4); // Should find issues in each section }); it("should handle TypeScript code validation with compilation errors", async () => { const tsDir = path.join(testTempDir, "typescript-validation"); await fs.mkdir(tsDir, { recursive: true }); await fs.writeFile( path.join(tsDir, "typescript.md"), ` # TypeScript Examples \`\`\`typescript // This has type errors let x: string = 123; function badFunction(param: number): string { return param; // Type error } \`\`\` `.trim(), ); const result = await handleValidateDiataxisContent({ contentPath: tsDir, validationType: "all", includeCodeValidation: true, }); expect(result.codeValidation).toBeDefined(); expect(result.codeValidation!.overallSuccess).toBe(false); }); it("should handle bash code validation with complex chaining", async () => { const bashComplexDir = path.join(testTempDir, "bash-complex"); await fs.mkdir(bashComplexDir, { recursive: true }); await fs.writeFile( path.join(bashComplexDir, "complex-bash.md"), ` # Complex Bash \`\`\`bash # Complex command chaining command1 && command2 || command3 rm $VARIABLE \`\`\` `.trim(), ); const result = await handleValidateDiataxisContent({ contentPath: bashComplexDir, validationType: "all", includeCodeValidation: true, }); expect(result.codeValidation).toBeDefined(); const bashIssues = result.codeValidation!.exampleResults.flatMap( (ex) => ex.issues, ); expect(bashIssues.length).toBeGreaterThan(0); }); it("should handle file limit reached scenario", async () => { const largeDir = path.join(testTempDir, "large-directory"); await fs.mkdir(largeDir, { recursive: true }); // Create many markdown files to test file limit for (let i = 0; i < 10; i++) { await fs.writeFile( path.join(largeDir, `file${i}.md`), `# File ${i}\nContent for file ${i}.`, ); } const result = await handleValidateDiataxisContent({ contentPath: largeDir, validationType: "all", includeCodeValidation: false, }); expect(result).toBeDefined(); expect( result.confidence.breakdown.architecturalAssumptions, ).toBeGreaterThan(60); }); it("should handle symlink detection in file scanning", async () => { const symlinkDir = path.join(testTempDir, "symlink-test"); await fs.mkdir(symlinkDir, { recursive: true }); // Create a regular file await fs.writeFile(path.join(symlinkDir, "regular.md"), "# Regular File"); // Create a subdirectory await fs.mkdir(path.join(symlinkDir, "subdir"), { recursive: true }); await fs.writeFile( path.join(symlinkDir, "subdir", "nested.md"), "# Nested File", ); const result = await handleValidateDiataxisContent({ contentPath: symlinkDir, validationType: "all", includeCodeValidation: false, }); expect(result).toBeDefined(); expect( result.confidence.breakdown.architecturalAssumptions, ).toBeGreaterThanOrEqual(60); }); it("should handle timeout scenario", async () => { const timeoutDir = path.join(testTempDir, "timeout-scenario"); await fs.mkdir(timeoutDir, { recursive: true }); await fs.writeFile(path.join(timeoutDir, "test.md"), "# Test"); // Mock a timeout by creating a very short timeout const originalTimeout = 120000; const result = await handleValidateDiataxisContent({ contentPath: timeoutDir, validationType: "all", includeCodeValidation: false, }); expect(result).toBeDefined(); }); it("should handle general content validation with external links", async () => { const generalDir = path.join(testTempDir, "general-external"); await fs.mkdir(generalDir, { recursive: true }); await fs.writeFile( path.join(generalDir, "external.md"), ` # External Links Test [GitHub](https://github.com) [Local](./local.md) `.trim(), ); await fs.writeFile(path.join(generalDir, "local.md"), "# Local File"); const result = await validateGeneralContent({ contentPath: generalDir, validationType: "all", includeCodeValidation: true, followExternalLinks: false, }); expect(result.linksChecked).toBeGreaterThan(0); expect(result.success).toBe(true); }); it("should handle general content validation with code validation", async () => { const codeDir = path.join(testTempDir, "general-code"); await fs.mkdir(codeDir, { recursive: true }); await fs.writeFile( path.join(codeDir, "code.md"), ` # Code Test \`\`\`javascript console.log("test") \`\`\` \`\`\`js console.log("another test"); \`\`\` `.trim(), ); const result = await validateGeneralContent({ contentPath: codeDir, validationType: "code", includeCodeValidation: true, }); expect(result.codeBlocksValidated).toBeGreaterThan(0); expect(result.codeErrors.length).toBeGreaterThan(0); // Missing semicolon }); it("should handle validation with no code blocks", async () => { const noCodeDir = path.join(testTempDir, "no-code"); await fs.mkdir(noCodeDir, { recursive: true }); await fs.writeFile( path.join(noCodeDir, "text.md"), ` # Text Only This is just text with no code blocks. `.trim(), ); const result = await validateGeneralContent({ contentPath: noCodeDir, validationType: "all", includeCodeValidation: true, }); expect(result.codeBlocksValidated).toBe(0); expect(result.success).toBe(true); }); }); });

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/tosin2013/documcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server