Skip to main content
Glama

mcp-github-project-manager

#!/usr/bin/env node /** * Test Validation Script for MCP GitHub Project Manager * * This script validates that all GitHub Project v2 MCP server functionality * is properly tested and working correctly. */ import { execSync } from 'child_process'; import { existsSync } from 'fs'; import chalk from 'chalk'; const REQUIRED_ENV_VARS = { github: ['GITHUB_TOKEN', 'GITHUB_OWNER', 'GITHUB_REPO'], ai: ['ANTHROPIC_API_KEY', 'OPENAI_API_KEY', 'GOOGLE_API_KEY'] }; class TestValidator { constructor() { this.results = { unit: { passed: 0, failed: 0, skipped: 0 }, e2e: { passed: 0, failed: 0, skipped: 0 }, integration: { passed: 0, failed: 0, skipped: 0 } }; this.errors = []; } log(message, type = 'info') { const colors = { info: chalk.blue, success: chalk.green, warning: chalk.yellow, error: chalk.red }; console.log(colors[type](`[${type.toUpperCase()}] ${message}`)); } checkEnvironment() { this.log('Checking environment setup...', 'info'); const missing = { github: REQUIRED_ENV_VARS.github.filter(env => !process.env[env]), ai: REQUIRED_ENV_VARS.ai.filter(env => !process.env[env]) }; if (missing.github.length > 0) { this.log(`Missing GitHub credentials: ${missing.github.join(', ')}`, 'warning'); this.log('E2E tests will be skipped for GitHub functionality', 'warning'); } else { this.log('GitHub credentials found ✓', 'success'); } if (missing.ai.length === REQUIRED_ENV_VARS.ai.length) { this.log('No AI provider credentials found', 'warning'); this.log('AI-related E2E tests will be skipped', 'warning'); } else { const available = REQUIRED_ENV_VARS.ai.filter(env => process.env[env]); this.log(`AI providers available: ${available.join(', ')} ✓`, 'success'); } return { hasGitHub: missing.github.length === 0, hasAI: missing.ai.length < REQUIRED_ENV_VARS.ai.length }; } async runUnitTests() { this.log('Running unit tests...', 'info'); try { const output = execSync('pnpm test --testPathPattern="unit" --passWithNoTests --verbose', { encoding: 'utf8', stdio: 'pipe' }); // Parse Jest output for test results const lines = output.split('\n'); const summaryLine = lines.find(line => line.includes('Test Suites:')); if (summaryLine) { const passedMatch = summaryLine.match(/(\d+) passed/); const failedMatch = summaryLine.match(/(\d+) failed/); this.results.unit.passed = passedMatch ? parseInt(passedMatch[1]) : 0; this.results.unit.failed = failedMatch ? parseInt(failedMatch[1]) : 0; } this.log(`Unit tests completed: ${this.results.unit.passed} passed, ${this.results.unit.failed} failed`, 'success'); return true; } catch (error) { this.log(`Unit tests failed: ${error.message}`, 'error'); this.errors.push(`Unit tests: ${error.message}`); return false; } } async runE2ETests(hasCredentials) { this.log('Running E2E tests...', 'info'); if (!hasCredentials.hasGitHub && !hasCredentials.hasAI) { this.log('Skipping E2E tests - no credentials available', 'warning'); this.results.e2e.skipped = 1; return true; } try { const output = execSync('pnpm test --testPathPattern="e2e" --passWithNoTests --verbose --testTimeout=30000', { encoding: 'utf8', stdio: 'pipe' }); // Parse results const lines = output.split('\n'); const summaryLine = lines.find(line => line.includes('Test Suites:')); if (summaryLine) { const passedMatch = summaryLine.match(/(\d+) passed/); const failedMatch = summaryLine.match(/(\d+) failed/); const skippedMatch = summaryLine.match(/(\d+) skipped/); this.results.e2e.passed = passedMatch ? parseInt(passedMatch[1]) : 0; this.results.e2e.failed = failedMatch ? parseInt(failedMatch[1]) : 0; this.results.e2e.skipped = skippedMatch ? parseInt(skippedMatch[1]) : 0; } this.log(`E2E tests completed: ${this.results.e2e.passed} passed, ${this.results.e2e.failed} failed, ${this.results.e2e.skipped} skipped`, 'success'); return true; } catch (error) { // E2E tests might fail due to missing credentials, which is expected if (error.message.includes('missing credentials') || error.message.includes('Skipping')) { this.log('E2E tests skipped due to missing credentials (expected)', 'warning'); this.results.e2e.skipped = 1; return true; } this.log(`E2E tests failed: ${error.message}`, 'error'); this.errors.push(`E2E tests: ${error.message}`); return false; } } validateGitHubProjectV2Functionality() { this.log('Validating GitHub Project v2 functionality coverage...', 'info'); const requiredTests = [ 'src/__tests__/unit/infrastructure/github/repositories/GitHubProjectRepository.test.ts', 'src/__tests__/unit/infrastructure/github/repositories/GitHubMilestoneRepository.test.ts', 'src/__tests__/unit/infrastructure/github/repositories/GitHubIssueRepository.test.ts', 'src/__tests__/unit/services/ProjectManagementService.test.ts', 'src/__tests__/e2e/tools/github-project-tools.e2e.ts', 'src/__tests__/e2e/tools/ai-task-tools.e2e.ts' ]; const missing = requiredTests.filter(test => !existsSync(test)); if (missing.length > 0) { this.log(`Missing required test files: ${missing.join(', ')}`, 'error'); this.errors.push(`Missing test files: ${missing.join(', ')}`); return false; } this.log('All required test files present ✓', 'success'); return true; } generateReport() { this.log('\n' + '='.repeat(60), 'info'); this.log('TEST VALIDATION REPORT', 'info'); this.log('='.repeat(60), 'info'); // Unit Tests this.log(`\nUnit Tests:`, 'info'); this.log(` ✓ Passed: ${this.results.unit.passed}`, 'success'); if (this.results.unit.failed > 0) { this.log(` ✗ Failed: ${this.results.unit.failed}`, 'error'); } // E2E Tests this.log(`\nE2E Tests:`, 'info'); this.log(` ✓ Passed: ${this.results.e2e.passed}`, 'success'); if (this.results.e2e.failed > 0) { this.log(` ✗ Failed: ${this.results.e2e.failed}`, 'error'); } if (this.results.e2e.skipped > 0) { this.log(` ⏭ Skipped: ${this.results.e2e.skipped}`, 'warning'); } // Overall Status const totalPassed = this.results.unit.passed + this.results.e2e.passed; const totalFailed = this.results.unit.failed + this.results.e2e.failed; const totalSkipped = this.results.unit.skipped + this.results.e2e.skipped; this.log(`\nOverall Results:`, 'info'); this.log(` Total Passed: ${totalPassed}`, 'success'); if (totalFailed > 0) { this.log(` Total Failed: ${totalFailed}`, 'error'); } if (totalSkipped > 0) { this.log(` Total Skipped: ${totalSkipped}`, 'warning'); } // Errors if (this.errors.length > 0) { this.log(`\nErrors:`, 'error'); this.errors.forEach(error => this.log(` - ${error}`, 'error')); } // Final Status const success = totalFailed === 0 && this.errors.length === 0; this.log(`\nValidation Status: ${success ? 'PASSED' : 'FAILED'}`, success ? 'success' : 'error'); return success; } async run() { this.log('Starting MCP GitHub Project Manager test validation...', 'info'); const credentials = this.checkEnvironment(); const functionalityValid = this.validateGitHubProjectV2Functionality(); const unitTestsPass = await this.runUnitTests(); const e2eTestsPass = await this.runE2ETests(credentials); const success = this.generateReport(); process.exit(success ? 0 : 1); } } // Run validation if this script is executed directly if (import.meta.url === `file://${process.argv[1]}`) { const validator = new TestValidator(); validator.run().catch(error => { console.error(chalk.red('Validation failed:'), error); process.exit(1); }); } export default TestValidator;

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/kunwarVivek/mcp-github-project-manager'

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