Skip to main content
Glama

documcp

by tosin2013
validate-content.test.ts42 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