/** * skill-integration.test.js - Integration tests for .agents skills * Part of LCBP3-DMS Phase 3 enhancements */ const fs = require('fs'); const path = require('path'); const { execSync } = require('child_process'); // Test configuration const BASE_DIR = path.resolve(__dirname, '..'); const AGENTS_DIR = path.join(BASE_DIR, '.agents'); const SKILLS_DIR = path.join(AGENTS_DIR, 'skills'); const WORKFLOWS_DIR = path.join(BASE_DIR, '.windsurf', 'workflows'); // Test utilities class SkillTestSuite { constructor() { this.results = { passed: 0, failed: 0, errors: [] }; } log(message, type = 'info') { const colors = { info: '\x1b[36m', // Cyan pass: '\x1b[32m', // Green fail: '\x1b[31m', // Red warn: '\x1b[33m', // Yellow reset: '\x1b[0m' }; const color = colors[type] || colors.info; console.log(`${color}${message}${colors.reset}`); } assert(condition, message) { if (condition) { this.log(` PASS: ${message}`, 'pass'); this.results.passed++; return true; } else { this.log(` FAIL: ${message}`, 'fail'); this.results.failed++; this.results.errors.push(message); return false; } } testDirectoryExists(dirPath, description) { const exists = fs.existsSync(dirPath); this.assert(exists, `${description} exists at ${dirPath}`); return exists; } testFileExists(filePath, description) { const exists = fs.existsSync(filePath); this.assert(exists, `${description} exists at ${filePath}`); return exists; } testFileContent(filePath, pattern, description) { if (!fs.existsSync(filePath)) { this.assert(false, `${description} - file not found: ${filePath}`); return false; } try { const content = fs.readFileSync(filePath, 'utf8'); const matches = content.match(pattern); this.assert(matches !== null, `${description} - pattern found in ${filePath}`); return matches !== null; } catch (error) { this.assert(false, `${description} - error reading file: ${error.message}`); return false; } } runScript(scriptPath, description) { try { const output = execSync(scriptPath, { encoding: 'utf8', cwd: BASE_DIR }); this.log(` SCRIPT: ${description} executed successfully`, 'pass'); return { success: true, output }; } catch (error) { this.log(` SCRIPT: ${description} failed - ${error.message}`, 'fail'); this.results.failed++; this.results.errors.push(`${description}: ${error.message}`); return { success: false, error: error.message }; } } } // Test suite implementation const testSuite = new SkillTestSuite(); function runAllTests() { testSuite.log('=== .agents Integration Test Suite ===', 'info'); testSuite.log(`Base directory: ${BASE_DIR}`, 'info'); testSuite.log(`Started: ${new Date().toISOString()}`, 'info'); testSuite.log(''); // Test 1: Directory Structure testSuite.log('Test 1: Directory Structure', 'info'); testSuite.testDirectoryExists(AGENTS_DIR, '.agents directory'); testSuite.testDirectoryExists(SKILLS_DIR, 'skills directory'); testSuite.testDirectoryExists(WORKFLOWS_DIR, 'workflows directory'); testSuite.testDirectoryExists(path.join(AGENTS_DIR, 'scripts'), 'scripts directory'); testSuite.testDirectoryExists(path.join(AGENTS_DIR, 'rules'), 'rules directory'); testSuite.log(''); // Test 2: Core Files testSuite.log('Test 2: Core Files', 'info'); testSuite.testFileExists(path.join(AGENTS_DIR, 'README.md'), 'README.md'); testSuite.testFileExists(path.join(SKILLS_DIR, 'VERSION'), 'skills VERSION file'); testSuite.testFileExists(path.join(SKILLS_DIR, 'skills.md'), 'skills.md documentation'); testSuite.log(''); // Test 3: Script Files testSuite.log('Test 3: Validation Scripts', 'info'); testSuite.testFileExists(path.join(AGENTS_DIR, 'scripts', 'bash', 'validate-versions.sh'), 'bash validate-versions.sh'); testSuite.testFileExists(path.join(AGENTS_DIR, 'scripts', 'bash', 'audit-skills.sh'), 'bash audit-skills.sh'); testSuite.testFileExists(path.join(AGENTS_DIR, 'scripts', 'bash', 'sync-workflows.sh'), 'bash sync-workflows.sh'); testSuite.testFileExists(path.join(AGENTS_DIR, 'scripts', 'powershell', 'validate-versions.ps1'), 'powershell validate-versions.ps1'); testSuite.testFileExists(path.join(AGENTS_DIR, 'scripts', 'powershell', 'audit-skills.ps1'), 'powershell audit-skills.ps1'); testSuite.log(''); // Test 4: Version Consistency testSuite.log('Test 4: Version Consistency', 'info'); testSuite.testFileContent(path.join(AGENTS_DIR, 'README.md'), /v1\.8\.6/, 'README.md version'); testSuite.testFileContent(path.join(SKILLS_DIR, 'VERSION'), /version: 1\.8\.6/, 'skills VERSION file'); testSuite.testFileContent(path.join(SKILLS_DIR, 'skills.md'), /v1\.8\.6/, 'skills.md version'); testSuite.testFileContent(path.join(AGENTS_DIR, 'rules', '00-project-context.md'), /v1\.8\.6/, 'project context version'); testSuite.log(''); // Test 5: Skills Structure testSuite.log('Test 5: Skills Structure', 'info'); const skillDirs = fs.readdirSync(SKILLS_DIR).filter(item => { const itemPath = path.join(SKILLS_DIR, item); return fs.statSync(itemPath).isDirectory() && item.startsWith('speckit-') || item === 'nestjs-best-practices' || item === 'next-best-practices'; }); testSuite.assert(skillDirs.length >= 20, `Found at least 20 skill directories (found ${skillDirs.length})`); // Test a few key skills const keySkills = ['speckit-plan', 'speckit-implement', 'speckit-specify', 'speckit-validate']; keySkills.forEach(skill => { const skillPath = path.join(SKILLS_DIR, skill); const skillMdPath = path.join(skillPath, 'SKILL.md'); testSuite.testDirectoryExists(skillPath, `${skill} directory`); testSuite.testFileExists(skillMdPath, `${skill} SKILL.md`); if (fs.existsSync(skillMdPath)) { testSuite.testFileContent(skillMdPath, /^name:/, `${skill} has name field`); testSuite.testFileContent(skillMdPath, /^description:/, `${skill} has description field`); testSuite.testFileContent(skillMdPath, /^version:/, `${skill} has version field`); testSuite.testFileContent(skillMdPath, /^## Role$/, `${skill} has Role section`); testSuite.testFileContent(skillMdPath, /^## Task$/, `${skill} has Task section`); } }); testSuite.log(''); // Test 6: Workflows Structure testSuite.log('Test 6: Workflows Structure', 'info'); const workflowFiles = fs.readdirSync(WORKFLOWS_DIR).filter(item => item.endsWith('.md')); testSuite.assert(workflowFiles.length >= 20, `Found at least 20 workflow files (found ${workflowFiles.length})`); // Test key workflows const keyWorkflows = ['00-speckit.all.md', '02-speckit.specify.md', '04-speckit.plan.md', '07-speckit.implement.md']; keyWorkflows.forEach(workflow => { const workflowPath = path.join(WORKFLOWS_DIR, workflow); testSuite.testFileExists(workflowPath, `${workflow} file`); }); testSuite.log(''); // Test 7: Rules Structure testSuite.log('Test 7: Rules Structure', 'info'); const rulesDir = path.join(AGENTS_DIR, 'rules'); const ruleFiles = fs.readdirSync(rulesDir).filter(item => item.endsWith('.md')); testSuite.assert(ruleFiles.length >= 10, `Found at least 10 rule files (found ${ruleFiles.length})`); // Test key rules const keyRules = ['00-project-context.md', '01-adr-019-uuid.md', '02-security.md']; keyRules.forEach(rule => { const rulePath = path.join(rulesDir, rule); testSuite.testFileExists(rulePath, `${rule} file`); }); testSuite.log(''); // Test 8: Script Execution (if on Unix-like system) if (process.platform !== 'win32') { testSuite.log('Test 8: Script Execution', 'info'); // Test version validation script const versionScript = path.join(AGENTS_DIR, 'scripts', 'bash', 'validate-versions.sh'); if (fs.existsSync(versionScript)) { try { // Make executable fs.chmodSync(versionScript, '755'); testSuite.runScript(versionScript, 'Version validation script'); } catch (error) { testSuite.log(` SKIP: Cannot execute version script - ${error.message}`, 'warn'); } } testSuite.log(''); } // Test 9: Documentation Quality testSuite.log('Test 9: Documentation Quality', 'info'); testSuite.testFileContent(path.join(AGENTS_DIR, 'README.md'), /## Troubleshooting/, 'README.md has troubleshooting section'); testSuite.testFileContent(path.join(SKILLS_DIR, 'skills.md'), /## Skill Dependency Matrix/, 'skills.md has dependency matrix'); testSuite.testFileContent(path.join(AGENTS_DIR, 'README.md'), /## Architecture/, 'README.md has architecture section'); testSuite.log(''); // Results Summary testSuite.log('=== Test Results Summary ===', 'info'); testSuite.log(`Passed: ${testSuite.results.passed}`, 'pass'); testSuite.log(`Failed: ${testSuite.results.failed}`, testSuite.results.failed > 0 ? 'fail' : 'pass'); if (testSuite.results.errors.length > 0) { testSuite.log('Errors:', 'fail'); testSuite.results.errors.forEach(error => { testSuite.log(` - ${error}`, 'fail'); }); } testSuite.log(`Completed: ${new Date().toISOString()}`, 'info'); return testSuite.results.failed === 0; } // Export for use in other modules module.exports = { SkillTestSuite, runAllTests }; // Run tests if called directly if (require.main === module) { const success = runAllTests(); process.exit(success ? 0 : 1); }