236 lines
8.6 KiB
JavaScript
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);
|
|
}
|