Files
lcbp3/.agents/tests/workflow-validation.test.js
T

236 lines
8.6 KiB
JavaScript

/**
* workflow-validation.test.js - Integration tests for workflows
* Part of LCBP3-DMS Phase 3 enhancements
*/
const fs = require('fs');
const path = require('path');
// Test configuration
const BASE_DIR = path.resolve(__dirname, '..');
const WORKFLOWS_DIR = path.join(BASE_DIR, '.windsurf', 'workflows');
const AGENTS_DIR = path.join(BASE_DIR, '.agents');
// Test utilities
class WorkflowTestSuite {
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;
}
}
testWorkflowFile(filePath, expectedName) {
if (!fs.existsSync(filePath)) {
this.assert(false, `Workflow file exists: ${expectedName}`);
return false;
}
try {
const content = fs.readFileSync(filePath, 'utf8');
// Basic structure checks
this.assert(content.length > 0, `${expectedName} has content`);
this.assert(content.includes('#'), `${expectedName} has markdown headers`);
// Check for workflow-specific patterns
if (expectedName.includes('speckit-')) {
this.assert(content.includes('speckit-'), `${expectedName} contains speckit reference`);
}
// Check for proper markdown formatting
const lines = content.split('\n');
const nonEmptyLines = lines.filter(line => line.trim().length > 0);
this.assert(nonEmptyLines.length >= 5, `${expectedName} has sufficient content`);
return true;
} catch (error) {
this.assert(false, `${expectedName} - error reading file: ${error.message}`);
return false;
}
}
validateWorkflowDependency(workflowName, workflowContent) {
// Check if workflow references existing skills
const skillReferences = workflowContent.match(/@speckit-\w+/g) || [];
const skillsDir = path.join(AGENTS_DIR, 'skills');
for (const skillRef of skillReferences) {
const skillName = skillRef.replace('@', '');
const skillPath = path.join(skillsDir, skillName);
if (!fs.existsSync(skillPath)) {
this.assert(false, `${workflowName} references non-existent skill: ${skillRef}`);
return false;
}
}
return true;
}
}
// Expected workflows mapping
const expectedWorkflows = {
'00-speckit.all.md': 'Full pipeline workflow',
'01-speckit.constitution.md': 'Constitution workflow',
'02-speckit.specify.md': 'Specification workflow',
'03-speckit.clarify.md': 'Clarification workflow',
'04-speckit.plan.md': 'Planning workflow',
'05-speckit.tasks.md': 'Task breakdown workflow',
'06-speckit.analyze.md': 'Analysis workflow',
'07-speckit.implement.md': 'Implementation workflow',
'08-speckit.checker.md': 'Static analysis workflow',
'09-speckit.tester.md': 'Testing workflow',
'10-speckit.reviewer.md': 'Code review workflow',
'11-speckit.validate.md': 'Validation workflow',
'speckit.prepare.md': 'Preparation workflow',
'schema-change.md': 'Schema change workflow',
'create-backend-module.md': 'Backend module creation',
'create-frontend-page.md': 'Frontend page creation',
'deploy.md': 'Deployment workflow',
'review.md': 'Code review workflow',
'util-speckit.checklist.md': 'Checklist utility',
'util-speckit.diff.md': 'Diff utility',
'util-speckit.migrate.md': 'Migration utility',
'util-speckit.quizme.md': 'Quiz utility',
'util-speckit.status.md': 'Status utility',
'util-speckit.taskstoissues.md': 'Task to issues utility'
};
// Test suite implementation
const workflowTestSuite = new WorkflowTestSuite();
function runWorkflowTests() {
workflowTestSuite.log('=== Workflow Validation Test Suite ===', 'info');
workflowTestSuite.log(`Workflows directory: ${WORKFLOWS_DIR}`, 'info');
workflowTestSuite.log(`Started: ${new Date().toISOString()}`, 'info');
workflowTestSuite.log('');
// Test 1: Workflows directory exists
workflowTestSuite.log('Test 1: Directory Structure', 'info');
workflowTestSuite.assert(fs.existsSync(WORKFLOWS_DIR), 'Workflows directory exists');
workflowTestSuite.log('');
// Test 2: Expected workflow files exist
workflowTestSuite.log('Test 2: Expected Workflow Files', 'info');
let foundWorkflows = 0;
for (const [filename, description] of Object.entries(expectedWorkflows)) {
const filePath = path.join(WORKFLOWS_DIR, filename);
workflowTestSuite.testWorkflowFile(filePath, description);
if (fs.existsSync(filePath)) {
foundWorkflows++;
}
}
workflowTestSuite.assert(foundWorkflows >= 20, `Found at least 20 workflows (found ${foundWorkflows})`);
workflowTestSuite.log('');
// Test 3: Workflow content validation
workflowTestSuite.log('Test 3: Content Validation', 'info');
for (const [filename, description] of Object.entries(expectedWorkflows)) {
const filePath = path.join(WORKFLOWS_DIR, filename);
if (fs.existsSync(filePath)) {
try {
const content = fs.readFileSync(filePath, 'utf8');
// Check for proper workflow structure
workflowTestSuite.assert(content.includes('#'), `${filename} has markdown headers`);
workflowTestSuite.assert(content.length > 100, `${filename} has substantial content`);
// Validate skill dependencies
workflowTestSuite.validateWorkflowDependency(filename, content);
} catch (error) {
workflowTestSuite.assert(false, `${filename} - content validation error: ${error.message}`);
}
}
}
workflowTestSuite.log('');
// Test 4: Workflow naming consistency
workflowTestSuite.log('Test 4: Naming Consistency', 'info');
const actualFiles = fs.readdirSync(WORKFLOWS_DIR).filter(file => file.endsWith('.md'));
for (const actualFile of actualFiles) {
if (!expectedWorkflows[actualFile]) {
workflowTestSuite.log(` UNEXPECTED: ${actualFile} not in expected list`, 'warn');
}
}
for (const expectedFile of Object.keys(expectedWorkflows)) {
if (!actualFiles.includes(expectedFile)) {
workflowTestSuite.assert(false, `Missing expected workflow: ${expectedFile}`);
}
}
workflowTestSuite.log('');
// Test 5: Cross-reference validation
workflowTestSuite.log('Test 5: Cross-Reference Validation', 'info');
// Check if README.md references workflows correctly
const readmePath = path.join(AGENTS_DIR, 'README.md');
if (fs.existsSync(readmePath)) {
const readmeContent = fs.readFileSync(readmePath, 'utf8');
workflowTestSuite.assert(
readmeContent.includes('.windsurf/workflows'),
'README.md references correct workflows path'
);
}
workflowTestSuite.log('');
// Results Summary
workflowTestSuite.log('=== Workflow Test Results Summary ===', 'info');
workflowTestSuite.log(`Passed: ${workflowTestSuite.results.passed}`, 'pass');
workflowTestSuite.log(`Failed: ${workflowTestSuite.results.failed}`, workflowTestSuite.results.failed > 0 ? 'fail' : 'pass');
if (workflowTestSuite.results.errors.length > 0) {
workflowTestSuite.log('Errors:', 'fail');
workflowTestSuite.results.errors.forEach(error => {
workflowTestSuite.log(` - ${error}`, 'fail');
});
}
workflowTestSuite.log(`Completed: ${new Date().toISOString()}`, 'info');
return workflowTestSuite.results.failed === 0;
}
// Export for use in other modules
module.exports = { WorkflowTestSuite, runWorkflowTests };
// Run tests if called directly
if (require.main === module) {
const success = runWorkflowTests();
process.exit(success ? 0 : 1);
}