all-tools.test.ts•19.5 kB
// Comprehensive tests for all MCP tools to achieve 80% coverage
import { promises as fs } from 'fs';
import path from 'path';
import os from 'os';
import tmp from 'tmp';
import { analyzeRepository } from '../../src/tools/analyze-repository';
import { recommendSSG } from '../../src/tools/recommend-ssg';
import { generateConfig } from '../../src/tools/generate-config';
import { setupStructure } from '../../src/tools/setup-structure';
import { deployPages } from '../../src/tools/deploy-pages';
import { verifyDeployment } from '../../src/tools/verify-deployment';
import { evaluateReadmeHealth } from '../../src/tools/evaluate-readme-health';
import { readmeBestPractices } from '../../src/tools/readme-best-practices';
describe('All MCP Tools Coverage Tests', () => {
let tempDir: string;
beforeAll(async () => {
tempDir = path.join(os.tmpdir(), 'documcp-coverage-tests');
await fs.mkdir(tempDir, { recursive: true });
});
afterAll(async () => {
try {
await fs.rm(tempDir, { recursive: true, force: true });
} catch (error) {
// Ignore cleanup errors
}
});
describe('analyze_repository', () => {
it('should analyze JavaScript project', async () => {
const jsRepo = await createJSRepo();
const result = await analyzeRepository({ path: jsRepo, depth: 'standard' });
expect(result.content).toBeDefined();
const analysisData = JSON.parse(
result.content.find((c) => c.text.includes('"ecosystem"'))!.text,
);
expect(analysisData.dependencies.ecosystem).toBe('javascript');
});
it('should analyze Python project', async () => {
const pyRepo = await createPythonRepo();
const result = await analyzeRepository({ path: pyRepo, depth: 'standard' });
expect(result.content).toBeDefined();
const analysisData = JSON.parse(
result.content.find((c) => c.text.includes('"ecosystem"'))!.text,
);
expect(analysisData.dependencies.ecosystem).toBe('python');
});
it('should handle different depths', async () => {
const repo = await createJSRepo();
const quickResult = await analyzeRepository({ path: repo, depth: 'quick' });
const deepResult = await analyzeRepository({ path: repo, depth: 'deep' });
expect(quickResult.content).toBeDefined();
expect(deepResult.content).toBeDefined();
});
it('should detect CI/CD workflows', async () => {
const ciRepo = await createRepoWithCI();
const result = await analyzeRepository({ path: ciRepo, depth: 'standard' });
const analysisData = JSON.parse(result.content.find((c) => c.text.includes('"hasCI"'))!.text);
expect(analysisData.structure.hasCI).toBe(true);
});
it('should handle repository without dependencies', async () => {
const emptyRepo = await createEmptyRepo();
const result = await analyzeRepository({ path: emptyRepo, depth: 'standard' });
const analysisData = JSON.parse(
result.content.find((c) => c.text.includes('"ecosystem"'))!.text,
);
expect(analysisData.dependencies.ecosystem).toBe('unknown');
});
});
describe('recommend_ssg', () => {
it('should provide recommendation with confidence', async () => {
const result = await recommendSSG({ analysisId: 'test-123' });
expect(result.content).toBeDefined();
const recData = JSON.parse(result.content.find((c) => c.text.includes('"confidence"'))!.text);
expect(recData.confidence).toBeGreaterThan(0);
expect(recData.confidence).toBeLessThanOrEqual(1);
});
it('should handle preferences', async () => {
const result = await recommendSSG({
analysisId: 'test-456',
preferences: {
priority: 'simplicity',
ecosystem: 'javascript',
},
});
expect(result.content).toBeDefined();
const recData = JSON.parse(
result.content.find((c) => c.text.includes('"recommended"'))!.text,
);
expect(['jekyll', 'hugo', 'docusaurus', 'mkdocs', 'eleventy']).toContain(recData.recommended);
});
it('should provide alternatives', async () => {
const result = await recommendSSG({ analysisId: 'test-789' });
const recData = JSON.parse(
result.content.find((c) => c.text.includes('"alternatives"'))!.text,
);
expect(Array.isArray(recData.alternatives)).toBe(true);
expect(recData.alternatives.length).toBeGreaterThan(0);
});
});
describe('generate_config', () => {
it('should generate Docusaurus config', async () => {
const outputDir = await createTempDir('docusaurus-config');
const result = await generateConfig({
ssg: 'docusaurus',
projectName: 'Test Docusaurus',
outputPath: outputDir,
});
expect(result.content).toBeDefined();
expect(await fileExists(path.join(outputDir, 'docusaurus.config.js'))).toBe(true);
expect(await fileExists(path.join(outputDir, 'package.json'))).toBe(true);
});
it('should generate MkDocs config', async () => {
const outputDir = await createTempDir('mkdocs-config');
const result = await generateConfig({
ssg: 'mkdocs',
projectName: 'Test MkDocs',
outputPath: outputDir,
});
expect(result.content).toBeDefined();
expect(await fileExists(path.join(outputDir, 'mkdocs.yml'))).toBe(true);
expect(await fileExists(path.join(outputDir, 'requirements.txt'))).toBe(true);
});
it('should generate Hugo config', async () => {
const outputDir = await createTempDir('hugo-config');
const result = await generateConfig({
ssg: 'hugo',
projectName: 'Test Hugo',
outputPath: outputDir,
});
expect(result.content).toBeDefined();
expect(await fileExists(path.join(outputDir, 'hugo.toml'))).toBe(true);
});
it('should generate Jekyll config', async () => {
const outputDir = await createTempDir('jekyll-config');
const result = await generateConfig({
ssg: 'jekyll',
projectName: 'Test Jekyll',
outputPath: outputDir,
});
expect(result.content).toBeDefined();
expect(await fileExists(path.join(outputDir, '_config.yml'))).toBe(true);
expect(await fileExists(path.join(outputDir, 'Gemfile'))).toBe(true);
});
it('should generate Eleventy config', async () => {
const outputDir = await createTempDir('eleventy-config');
const result = await generateConfig({
ssg: 'eleventy',
projectName: 'Test Eleventy',
outputPath: outputDir,
});
expect(result.content).toBeDefined();
expect(await fileExists(path.join(outputDir, '.eleventy.js'))).toBe(true);
expect(await fileExists(path.join(outputDir, 'package.json'))).toBe(true);
});
});
describe('setup_structure', () => {
it('should create Diataxis structure', async () => {
const docsDir = await createTempDir('diataxis-structure');
const result = await setupStructure({
path: docsDir,
ssg: 'docusaurus',
includeExamples: true,
});
expect(result.content).toBeDefined();
const categories = ['tutorials', 'how-to', 'reference', 'explanation'];
for (const category of categories) {
expect(await fileExists(path.join(docsDir, category, 'index.md'))).toBe(true);
}
expect(await fileExists(path.join(docsDir, 'index.md'))).toBe(true);
});
it('should create structure without examples', async () => {
const docsDir = await createTempDir('no-examples');
const result = await setupStructure({
path: docsDir,
ssg: 'mkdocs',
includeExamples: false,
});
expect(result.content).toBeDefined();
const tutorialsFiles = await fs.readdir(path.join(docsDir, 'tutorials'));
expect(tutorialsFiles).toEqual(['index.md']); // Only index, no examples
});
it('should handle different SSG formats', async () => {
const docusaurusDir = await createTempDir('docusaurus-format');
await setupStructure({
path: docusaurusDir,
ssg: 'docusaurus',
includeExamples: true,
});
const content = await fs.readFile(path.join(docusaurusDir, 'tutorials', 'index.md'), 'utf-8');
expect(content).toContain('id: tutorials-index');
const jekyllDir = await createTempDir('jekyll-format');
await setupStructure({
path: jekyllDir,
ssg: 'jekyll',
includeExamples: true,
});
const jekyllContent = await fs.readFile(
path.join(jekyllDir, 'tutorials', 'index.md'),
'utf-8',
);
expect(jekyllContent).toContain('title:');
});
});
describe('deploy_pages', () => {
it('should create Docusaurus deployment workflow', async () => {
const repoDir = await createTempDir('docusaurus-deploy');
const result = await deployPages({
repository: repoDir,
ssg: 'docusaurus',
});
expect(result.content).toBeDefined();
const workflowPath = path.join(repoDir, '.github', 'workflows', 'deploy-docs.yml');
expect(await fileExists(workflowPath)).toBe(true);
const workflowContent = await fs.readFile(workflowPath, 'utf-8');
expect(workflowContent).toContain('Deploy Docusaurus');
expect(workflowContent).toContain('npm run build');
});
it('should create MkDocs deployment workflow', async () => {
const repoDir = await createTempDir('mkdocs-deploy');
const result = await deployPages({
repository: repoDir,
ssg: 'mkdocs',
});
const workflowContent = await fs.readFile(
path.join(repoDir, '.github', 'workflows', 'deploy-docs.yml'),
'utf-8',
);
expect(workflowContent).toContain('mkdocs gh-deploy');
});
it('should handle custom domain', async () => {
const repoDir = await createTempDir('custom-domain');
const result = await deployPages({
repository: repoDir,
ssg: 'docusaurus',
customDomain: 'docs.example.com',
});
expect(result.content).toBeDefined();
expect(await fileExists(path.join(repoDir, 'CNAME'))).toBe(true);
const cnameContent = await fs.readFile(path.join(repoDir, 'CNAME'), 'utf-8');
expect(cnameContent.trim()).toBe('docs.example.com');
});
it('should handle different branches', async () => {
const repoDir = await createTempDir('custom-branch');
await deployPages({
repository: repoDir,
ssg: 'hugo',
branch: 'main',
});
const workflowContent = await fs.readFile(
path.join(repoDir, '.github', 'workflows', 'deploy-docs.yml'),
'utf-8',
);
expect(workflowContent).toContain('Deploy Hugo');
});
});
describe('verify_deployment', () => {
it('should verify complete setup', async () => {
const repoDir = await createCompleteRepo();
const result = await verifyDeployment({
repository: repoDir,
});
expect(result.content).toBeDefined();
const verification = JSON.parse(result.content[0].text);
expect(verification.overallStatus).toBe('Ready for deployment');
expect(verification.summary.passed).toBeGreaterThan(0);
});
it('should identify missing components', async () => {
const emptyDir = await createTempDir('empty-verify');
const result = await verifyDeployment({
repository: emptyDir,
});
const verification = JSON.parse(result.content[0].text);
expect(verification.overallStatus).toContain('Configuration required');
expect(verification.summary.failed).toBeGreaterThan(0);
expect(
verification.checks.some((check: any) => check.message.includes('No .github/workflows')),
).toBe(true);
});
it('should handle different repository paths', async () => {
const relativeResult = await verifyDeployment({ repository: '.' });
expect(relativeResult.content).toBeDefined();
const httpResult = await verifyDeployment({ repository: 'https://github.com/user/repo' });
expect(httpResult.content).toBeDefined();
});
it('should provide recommendations', async () => {
const incompleteDir = await createTempDir('incomplete');
await fs.writeFile(path.join(incompleteDir, 'README.md'), '# Test');
const result = await verifyDeployment({
repository: incompleteDir,
});
const resultText = result.content.map((c) => c.text).join('\n');
expect(resultText).toContain('→');
expect(resultText).toContain('deploy_pages tool');
});
it('should check for different config patterns', async () => {
const configDir = await createTempDir('config-check');
await fs.writeFile(path.join(configDir, 'docusaurus.config.js'), 'module.exports = {};');
const result = await verifyDeployment({
repository: configDir,
});
const resultText = result.content.map((c) => c.text).join('\n');
expect(resultText).toContain('SSG Configuration');
expect(resultText).toContain('docusaurus.config.js');
});
});
describe('evaluate_readme_health', () => {
it('should evaluate README health with minimal input', async () => {
const readmePath = await createReadmeFile('Basic project README');
const result = await evaluateReadmeHealth({
readme_path: readmePath,
});
expect(result.content).toBeDefined();
expect(result.isError).toBe(false);
const healthData = result.content.find((c) => c.text.includes('healthReport'));
expect(healthData).toBeDefined();
});
it('should handle different project types', async () => {
const readmePath = await createReadmeFile('Enterprise tool documentation');
const result = await evaluateReadmeHealth({
readme_path: readmePath,
project_type: 'enterprise_tool',
});
expect(result.content).toBeDefined();
expect(result.isError).toBe(false);
});
it('should provide health components and scoring', async () => {
const readmePath = await createReadmeFile(`# Complete Project
## Description
Detailed description
## Installation
Installation steps
## Usage
Usage examples
## Contributing
Contributing guidelines
## License
MIT License`);
const result = await evaluateReadmeHealth({
readme_path: readmePath,
});
const dataContent = result.content.find((c) => c.text.includes('healthReport'));
const data = JSON.parse(dataContent!.text);
expect(data.healthReport.overallScore).toBeGreaterThanOrEqual(0);
expect(data.healthReport.grade).toBeDefined();
expect(data.healthReport.components).toBeDefined();
});
});
describe('readme_best_practices', () => {
it('should analyze README best practices', async () => {
const readmePath = await createReadmeFile('Basic library README');
const result = await readmeBestPractices({
readme_path: readmePath,
project_type: 'library',
});
expect(result.success).toBe(true);
expect(result.data).toBeDefined();
expect(result.data!.bestPracticesReport).toBeDefined();
});
it('should handle template generation', async () => {
const outputDir = await createTempDir('best-practices-template');
const result = await readmeBestPractices({
readme_path: path.join(outputDir, 'missing.md'),
generate_template: true,
output_directory: outputDir,
project_type: 'application',
});
expect(result.success).toBe(true);
expect(result.data).toBeDefined();
});
it('should provide checklist and recommendations', async () => {
const readmePath = await createReadmeFile(`# Library Project
## Installation
npm install my-lib
## Usage
Basic usage here
## API
API documentation
`);
const result = await readmeBestPractices({
readme_path: readmePath,
});
expect(result.success).toBe(true);
expect(result.data!.bestPracticesReport.checklist).toBeDefined();
expect(Array.isArray(result.data!.bestPracticesReport.checklist)).toBe(true);
expect(result.data!.recommendations).toBeDefined();
expect(result.data!.nextSteps).toBeDefined();
});
});
// Helper functions
async function createJSRepo(): Promise<string> {
const repoPath = path.join(tempDir, `js-repo-${Date.now()}`);
await fs.mkdir(repoPath, { recursive: true });
await fs.writeFile(
path.join(repoPath, 'package.json'),
JSON.stringify(
{
name: 'test-js-project',
dependencies: { express: '^4.0.0' },
devDependencies: { jest: '^29.0.0' },
},
null,
2,
),
);
await fs.writeFile(path.join(repoPath, 'index.js'), 'console.log("Hello");');
await fs.writeFile(path.join(repoPath, 'README.md'), '# JS Project');
return repoPath;
}
async function createPythonRepo(): Promise<string> {
const repoPath = path.join(tempDir, `py-repo-${Date.now()}`);
await fs.mkdir(repoPath, { recursive: true });
await fs.writeFile(path.join(repoPath, 'requirements.txt'), 'flask>=2.0.0');
await fs.writeFile(path.join(repoPath, 'main.py'), 'print("Hello")');
await fs.writeFile(path.join(repoPath, 'README.md'), '# Python Project');
return repoPath;
}
async function createRepoWithCI(): Promise<string> {
const repoPath = path.join(tempDir, `ci-repo-${Date.now()}`);
await fs.mkdir(path.join(repoPath, '.github', 'workflows'), { recursive: true });
await fs.writeFile(path.join(repoPath, '.github', 'workflows', 'ci.yml'), 'name: CI\non: push');
await fs.writeFile(path.join(repoPath, 'README.md'), '# CI Project');
return repoPath;
}
async function createEmptyRepo(): Promise<string> {
const repoPath = path.join(tempDir, `empty-repo-${Date.now()}`);
await fs.mkdir(repoPath, { recursive: true });
await fs.writeFile(path.join(repoPath, 'README.md'), '# Empty Project');
return repoPath;
}
async function createTempDir(suffix: string): Promise<string> {
const dirPath = path.join(tempDir, `${suffix}-${Date.now()}`);
await fs.mkdir(dirPath, { recursive: true });
return dirPath;
}
async function createCompleteRepo(): Promise<string> {
const repoPath = await createTempDir('complete-repo');
// Create workflow
await fs.mkdir(path.join(repoPath, '.github', 'workflows'), { recursive: true });
await fs.writeFile(
path.join(repoPath, '.github', 'workflows', 'deploy.yml'),
'name: Deploy\non: push',
);
// Create docs
await fs.mkdir(path.join(repoPath, 'docs'), { recursive: true });
await fs.writeFile(path.join(repoPath, 'docs', 'index.md'), '# Docs');
// Create config
await fs.writeFile(path.join(repoPath, 'docusaurus.config.js'), 'module.exports = {};');
// Create build output
await fs.mkdir(path.join(repoPath, 'build'), { recursive: true });
await fs.writeFile(path.join(repoPath, 'build', 'index.html'), '<html></html>');
return repoPath;
}
async function fileExists(filePath: string): Promise<boolean> {
try {
await fs.access(filePath);
return true;
} catch {
return false;
}
}
async function createReadmeFile(content: string): Promise<string> {
const file = tmp.fileSync({ postfix: '.md', keep: false });
await fs.writeFile(file.name, content);
return file.name;
}
});