stigmergy 1.2.13 โ 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -3
- package/STIGMERGY.md +3 -0
- package/config/builtin-skills.json +43 -0
- package/config/enhanced-cli-config.json +438 -0
- package/docs/CLI_TOOLS_AGENT_SKILL_ANALYSIS.md +463 -0
- package/docs/DESIGN_CLI_HELP_ANALYZER_REFACTOR.md +726 -0
- package/docs/ENHANCED_CLI_AGENT_SKILL_CONFIG.md +285 -0
- package/docs/IMPLEMENTATION_CHECKLIST_CLI_HELP_ANALYZER_REFACTOR.md +1268 -0
- package/docs/INSTALLER_ARCHITECTURE.md +257 -0
- package/docs/LESSONS_LEARNED.md +252 -0
- package/docs/SPECS_CLI_HELP_ANALYZER_REFACTOR.md +287 -0
- package/docs/SUDO_PROBLEM_AND_SOLUTION.md +529 -0
- package/docs/correct-skillsio-implementation.md +368 -0
- package/docs/development_guidelines.md +276 -0
- package/docs/independent-resume-implementation.md +198 -0
- package/docs/resumesession-final-implementation.md +195 -0
- package/docs/resumesession-usage.md +87 -0
- package/package.json +146 -136
- package/scripts/analyze-router.js +168 -0
- package/scripts/run-comprehensive-tests.js +230 -0
- package/scripts/run-quick-tests.js +90 -0
- package/scripts/test-runner.js +344 -0
- package/skills/resumesession/INDEPENDENT_SKILL.md +403 -0
- package/skills/resumesession/README.md +381 -0
- package/skills/resumesession/SKILL.md +211 -0
- package/skills/resumesession/__init__.py +33 -0
- package/skills/resumesession/implementations/simple-resume.js +13 -0
- package/skills/resumesession/independent-resume.js +750 -0
- package/skills/resumesession/package.json +1 -0
- package/skills/resumesession/skill.json +1 -0
- package/src/adapters/claude/install_claude_integration.js +9 -1
- package/src/adapters/codebuddy/install_codebuddy_integration.js +3 -1
- package/src/adapters/codex/install_codex_integration.js +15 -5
- package/src/adapters/gemini/install_gemini_integration.js +3 -1
- package/src/adapters/qwen/install_qwen_integration.js +3 -1
- package/src/cli/commands/autoinstall.js +65 -0
- package/src/cli/commands/errors.js +190 -0
- package/src/cli/commands/independent-resume.js +395 -0
- package/src/cli/commands/install.js +179 -0
- package/src/cli/commands/permissions.js +108 -0
- package/src/cli/commands/project.js +485 -0
- package/src/cli/commands/scan.js +97 -0
- package/src/cli/commands/simple-resume.js +377 -0
- package/src/cli/commands/skills.js +158 -0
- package/src/cli/commands/status.js +113 -0
- package/src/cli/commands/stigmergy-resume.js +775 -0
- package/src/cli/commands/system.js +301 -0
- package/src/cli/commands/universal-resume.js +394 -0
- package/src/cli/router-beta.js +471 -0
- package/src/cli/utils/environment.js +75 -0
- package/src/cli/utils/formatters.js +47 -0
- package/src/cli/utils/skills_cache.js +92 -0
- package/src/core/cache_cleaner.js +1 -0
- package/src/core/cli_adapters.js +345 -0
- package/src/core/cli_help_analyzer.js +1236 -680
- package/src/core/cli_path_detector.js +702 -709
- package/src/core/cli_tools.js +515 -160
- package/src/core/coordination/nodejs/CLIIntegrationManager.js +18 -0
- package/src/core/coordination/nodejs/HookDeploymentManager.js +242 -412
- package/src/core/coordination/nodejs/HookDeploymentManager.refactored.js +323 -0
- package/src/core/coordination/nodejs/generators/CLIAdapterGenerator.js +363 -0
- package/src/core/coordination/nodejs/generators/ResumeSessionGenerator.js +932 -0
- package/src/core/coordination/nodejs/generators/SkillsIntegrationGenerator.js +1395 -0
- package/src/core/coordination/nodejs/generators/index.js +12 -0
- package/src/core/enhanced_cli_installer.js +1208 -608
- package/src/core/enhanced_cli_parameter_handler.js +402 -0
- package/src/core/execution_mode_detector.js +222 -0
- package/src/core/installer.js +151 -106
- package/src/core/local_skill_scanner.js +732 -0
- package/src/core/multilingual/language-pattern-manager.js +1 -1
- package/src/core/skills/BuiltinSkillsDeployer.js +188 -0
- package/src/core/skills/StigmergySkillManager.js +123 -16
- package/src/core/skills/embedded-openskills/SkillParser.js +7 -3
- package/src/core/smart_router.js +550 -261
- package/src/index.js +10 -4
- package/src/utils.js +66 -7
- package/test/cli-integration.test.js +304 -0
- package/test/direct_smart_router_test.js +88 -0
- package/test/enhanced-cli-agent-skill-test.js +485 -0
- package/test/simple_test.js +82 -0
- package/test/smart_router_test_runner.js +123 -0
- package/test/smart_routing_edge_cases.test.js +284 -0
- package/test/smart_routing_simple_verification.js +139 -0
- package/test/smart_routing_verification.test.js +346 -0
- package/test/specific-cli-agent-skill-analysis.js +385 -0
- package/test/unit/smart_router.test.js +295 -0
- package/test/very_simple_test.js +54 -0
- package/src/cli/router.js +0 -1783
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Comprehensive Test Runner
|
|
5
|
+
* Runs all tests and generates detailed coverage report
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { execSync } = require('child_process');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
console.log('๐ฏ COMPREHENSIVE TEST SUITE');
|
|
13
|
+
console.log('='.repeat(70));
|
|
14
|
+
|
|
15
|
+
// ANSI color codes
|
|
16
|
+
const colors = {
|
|
17
|
+
reset: '\x1b[0m',
|
|
18
|
+
green: '\x1b[32m',
|
|
19
|
+
red: '\x1b[31m',
|
|
20
|
+
yellow: '\x1b[33m',
|
|
21
|
+
blue: '\x1b[34m',
|
|
22
|
+
cyan: '\x1b[36m'
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function log(message, color = 'reset') {
|
|
26
|
+
console.log(`${colors[color]}${message}${colors.reset}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function runCommand(command, description) {
|
|
30
|
+
log(`\n๐ฆ ${description}...`, 'cyan');
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const output = execSync(command, {
|
|
34
|
+
stdio: 'inherit',
|
|
35
|
+
cwd: process.cwd()
|
|
36
|
+
});
|
|
37
|
+
log(`โ
${description} completed`, 'green');
|
|
38
|
+
return true;
|
|
39
|
+
} catch (error) {
|
|
40
|
+
log(`โ ${description} failed`, 'red');
|
|
41
|
+
log(`Error: ${error.message}`, 'red');
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function ensureDirectories() {
|
|
47
|
+
const dirs = [
|
|
48
|
+
'tests/comprehensive/cli',
|
|
49
|
+
'tests/comprehensive/cli/commands',
|
|
50
|
+
'tests/comprehensive/core',
|
|
51
|
+
'tests/comprehensive/coordination',
|
|
52
|
+
'tests/comprehensive/adapters',
|
|
53
|
+
'tests/comprehensive/utils',
|
|
54
|
+
'coverage'
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
dirs.forEach(dir => {
|
|
58
|
+
const fullPath = path.join(process.cwd(), dir);
|
|
59
|
+
if (!fs.existsSync(fullPath)) {
|
|
60
|
+
fs.mkdirSync(fullPath, { recursive: true });
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function countTestFiles() {
|
|
66
|
+
const testDirs = [
|
|
67
|
+
'tests/unit',
|
|
68
|
+
'tests/integration',
|
|
69
|
+
'tests/e2e',
|
|
70
|
+
'tests/comprehensive',
|
|
71
|
+
'tests/regression'
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
let totalTests = 0;
|
|
75
|
+
testDirs.forEach(dir => {
|
|
76
|
+
const testPath = path.join(process.cwd(), dir);
|
|
77
|
+
if (fs.existsSync(testPath)) {
|
|
78
|
+
const files = fs.readdirSync(testPath)
|
|
79
|
+
.filter(f => f.endsWith('.test.js'));
|
|
80
|
+
totalTests += files.length;
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
return totalTests;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function countSourceFiles() {
|
|
88
|
+
const srcPath = path.join(process.cwd(), 'src');
|
|
89
|
+
|
|
90
|
+
function countDir(dir) {
|
|
91
|
+
let count = 0;
|
|
92
|
+
const items = fs.readdirSync(dir, { withFileTypes: true });
|
|
93
|
+
|
|
94
|
+
items.forEach(item => {
|
|
95
|
+
if (item.isDirectory()) {
|
|
96
|
+
count += countDir(path.join(dir, item.name));
|
|
97
|
+
} else if (item.name.endsWith('.js')) {
|
|
98
|
+
count++;
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
return count;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return countDir(srcPath);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function printSummary() {
|
|
109
|
+
log('\n' + '='.repeat(70), 'cyan');
|
|
110
|
+
log('๐ TEST SUITE SUMMARY', 'cyan');
|
|
111
|
+
log('='.repeat(70), 'cyan');
|
|
112
|
+
|
|
113
|
+
const sourceFiles = countSourceFiles();
|
|
114
|
+
const testFiles = countTestFiles();
|
|
115
|
+
const coverage = ((testFiles / sourceFiles) * 100).toFixed(1);
|
|
116
|
+
|
|
117
|
+
log(`\n๐ Source Files: ${sourceFiles}`);
|
|
118
|
+
log(`๐งช Test Files: ${testFiles}`);
|
|
119
|
+
log(`๐ Test Coverage: ${coverage}%`);
|
|
120
|
+
|
|
121
|
+
log('\n' + '='.repeat(70), 'cyan');
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function readCoverageReport() {
|
|
125
|
+
const coveragePath = path.join(process.cwd(), 'coverage', 'coverage-summary.json');
|
|
126
|
+
|
|
127
|
+
if (!fs.existsSync(coveragePath)) {
|
|
128
|
+
log('\nโ ๏ธ Coverage report not found', 'yellow');
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const coverage = JSON.parse(fs.readFileSync(coveragePath, 'utf8'));
|
|
133
|
+
|
|
134
|
+
log('\n' + '='.repeat(70), 'cyan');
|
|
135
|
+
log('๐ DETAILED COVERAGE REPORT', 'cyan');
|
|
136
|
+
log('='.repeat(70), 'cyan');
|
|
137
|
+
|
|
138
|
+
const total = coverage.total;
|
|
139
|
+
log(`\n๐ Overall Coverage:`, 'cyan');
|
|
140
|
+
log(` Statements: ${total.statements.pct}% (${total.statements.covered}/${total.statements.total})`);
|
|
141
|
+
log(` Branches: ${total.branches.pct}% (${total.branches.covered}/${total.branches.total})`);
|
|
142
|
+
log(` Functions: ${total.functions.pct}% (${total.functions.covered}/${total.functions.total})`);
|
|
143
|
+
log(` Lines: ${total.lines.pct}% (${total.lines.covered}/${total.lines.total})`);
|
|
144
|
+
|
|
145
|
+
// Find files with low coverage
|
|
146
|
+
log('\n๐ Files with Low Coverage (< 80%):', 'yellow');
|
|
147
|
+
let lowCoverageFound = false;
|
|
148
|
+
|
|
149
|
+
Object.entries(coverage).forEach(([file, data]) => {
|
|
150
|
+
if (file !== 'total' && data.statements.pct < 80) {
|
|
151
|
+
lowCoverageFound = true;
|
|
152
|
+
const relativePath = file.replace(process.cwd(), '');
|
|
153
|
+
log(` ${relativePath}: ${data.statements.pct}%`, 'red');
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
if (!lowCoverageFound) {
|
|
158
|
+
log(' โ
All files have good coverage!', 'green');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
log('\n' + '='.repeat(70), 'cyan');
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
async function main() {
|
|
165
|
+
const startTime = Date.now();
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
// Ensure test directories exist
|
|
169
|
+
ensureDirectories();
|
|
170
|
+
|
|
171
|
+
// Print initial summary
|
|
172
|
+
log('\n๐ Test Environment:', 'blue');
|
|
173
|
+
log(` Node.js: ${process.version}`);
|
|
174
|
+
log(` Platform: ${process.platform}`);
|
|
175
|
+
log(` CWD: ${process.cwd()}`);
|
|
176
|
+
|
|
177
|
+
// Run pre-test cleanup
|
|
178
|
+
log('\n๐งน Cleaning test environment...', 'blue');
|
|
179
|
+
try {
|
|
180
|
+
execSync('npm run clean 2>/dev/null || true', { stdio: 'ignore' });
|
|
181
|
+
} catch (e) {
|
|
182
|
+
// Ignore cleanup errors
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Run tests by category
|
|
186
|
+
const results = {
|
|
187
|
+
unit: runCommand('npm run test:unit -- --silent', 'Unit Tests'),
|
|
188
|
+
integration: runCommand('npm run test:integration -- --silent', 'Integration Tests'),
|
|
189
|
+
comprehensive: runCommand('jest tests/comprehensive --coverage --silent', 'Comprehensive Tests')
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
// Generate coverage report
|
|
193
|
+
if (results.comprehensive) {
|
|
194
|
+
readCoverageReport();
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Calculate duration
|
|
198
|
+
const duration = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
199
|
+
|
|
200
|
+
// Print final summary
|
|
201
|
+
printSummary();
|
|
202
|
+
|
|
203
|
+
log(`\nโฑ๏ธ Total Duration: ${duration}s`, 'blue');
|
|
204
|
+
|
|
205
|
+
// Determine success
|
|
206
|
+
const allPassed = Object.values(results).every(r => r === true);
|
|
207
|
+
|
|
208
|
+
if (allPassed) {
|
|
209
|
+
log('\nโ
ALL TESTS PASSED!', 'green');
|
|
210
|
+
log('\n๐ Great job! Your test suite is comprehensive.', 'green');
|
|
211
|
+
process.exit(0);
|
|
212
|
+
} else {
|
|
213
|
+
log('\nโ SOME TESTS FAILED', 'red');
|
|
214
|
+
log('\n๐ก Run with --verbose to see detailed error messages', 'yellow');
|
|
215
|
+
process.exit(1);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
} catch (error) {
|
|
219
|
+
log(`\n๐ฅ Fatal Error: ${error.message}`, 'red');
|
|
220
|
+
console.error(error);
|
|
221
|
+
process.exit(1);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Run if executed directly
|
|
226
|
+
if (require.main === module) {
|
|
227
|
+
main();
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
module.exports = { runCommand, readCoverageReport, printSummary };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Quick Test Runner
|
|
5
|
+
* Runs only the comprehensive tests without legacy tests
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { execSync } = require('child_process');
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
console.log('๐ฏ QUICK COMPREHENSIVE TEST SUITE');
|
|
13
|
+
console.log('='.repeat(70));
|
|
14
|
+
|
|
15
|
+
// ANSI colors
|
|
16
|
+
const colors = {
|
|
17
|
+
reset: '\x1b[0m',
|
|
18
|
+
green: '\x1b[32m',
|
|
19
|
+
red: '\x1b[31m',
|
|
20
|
+
yellow: '\x1b[33m',
|
|
21
|
+
blue: '\x1b[34m',
|
|
22
|
+
cyan: '\x1b[36m'
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
function log(message, color = 'reset') {
|
|
26
|
+
console.log(`${colors[color]}${message}${colors.reset}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function runCommand(command, description) {
|
|
30
|
+
log(`\n๐ฆ ${description}...`, 'cyan');
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const output = execSync(command, {
|
|
34
|
+
stdio: 'inherit',
|
|
35
|
+
cwd: process.cwd()
|
|
36
|
+
});
|
|
37
|
+
log(`โ
${description} completed`, 'green');
|
|
38
|
+
return true;
|
|
39
|
+
} catch (error) {
|
|
40
|
+
log(`โ ${description} failed`, 'red');
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function main() {
|
|
46
|
+
const startTime = Date.now();
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
log('\n๐ Running comprehensive tests only...', 'blue');
|
|
50
|
+
|
|
51
|
+
// Ensure directories exist
|
|
52
|
+
const dirs = ['tests/comprehensive/cli/commands', 'tests/comprehensive/core', 'tests/comprehensive/coordination'];
|
|
53
|
+
dirs.forEach(dir => {
|
|
54
|
+
const fullPath = path.join(process.cwd(), dir);
|
|
55
|
+
if (!fs.existsSync(fullPath)) {
|
|
56
|
+
fs.mkdirSync(fullPath, { recursive: true });
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// Run comprehensive tests only
|
|
61
|
+
const result = runCommand(
|
|
62
|
+
'jest tests/comprehensive --coverage',
|
|
63
|
+
'Comprehensive Tests'
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// Calculate duration
|
|
67
|
+
const duration = ((Date.now() - startTime) / 1000).toFixed(2);
|
|
68
|
+
|
|
69
|
+
log(`\nโฑ๏ธ Duration: ${duration}s`, 'blue');
|
|
70
|
+
|
|
71
|
+
if (result) {
|
|
72
|
+
log('\nโ
ALL TESTS PASSED!', 'green');
|
|
73
|
+
process.exit(0);
|
|
74
|
+
} else {
|
|
75
|
+
log('\nโ SOME TESTS FAILED', 'red');
|
|
76
|
+
log('\n๐ก Check the output above for details', 'yellow');
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
} catch (error) {
|
|
81
|
+
log(`\n๐ฅ Fatal Error: ${error.message}`, 'red');
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (require.main === module) {
|
|
87
|
+
main();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
module.exports = { runCommand };
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Enhanced Test Runner for HookDeploymentManager Test Suite
|
|
5
|
+
* Provides comprehensive testing with reporting and analysis
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { execSync } = require('child_process');
|
|
9
|
+
const fs = require('fs-extra');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
const chalk = require('chalk');
|
|
12
|
+
|
|
13
|
+
class TestRunner {
|
|
14
|
+
constructor() {
|
|
15
|
+
this.testResults = {
|
|
16
|
+
unit: { passed: 0, failed: 0, total: 0, duration: 0 },
|
|
17
|
+
integration: { passed: 0, failed: 0, total: 0, duration: 0 },
|
|
18
|
+
regression: { passed: 0, failed: 0, total: 0, duration: 0 },
|
|
19
|
+
performance: { passed: 0, failed: 0, total: 0, duration: 0 }
|
|
20
|
+
};
|
|
21
|
+
this.startTime = Date.now();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
log(message, type = 'info') {
|
|
25
|
+
const colors = {
|
|
26
|
+
info: chalk.blue,
|
|
27
|
+
success: chalk.green,
|
|
28
|
+
warning: chalk.yellow,
|
|
29
|
+
error: chalk.red
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
console.log(colors[type](`[${new Date().toLocaleTimeString()}] ${message}`));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
async runCommand(command, description) {
|
|
36
|
+
this.log(`Running: ${description}...`);
|
|
37
|
+
const startTime = Date.now();
|
|
38
|
+
|
|
39
|
+
try {
|
|
40
|
+
const output = execSync(command, {
|
|
41
|
+
encoding: 'utf8',
|
|
42
|
+
stdio: 'pipe'
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const duration = Date.now() - startTime;
|
|
46
|
+
this.log(`${description} completed successfully (${duration}ms)`, 'success');
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
success: true,
|
|
50
|
+
output,
|
|
51
|
+
duration
|
|
52
|
+
};
|
|
53
|
+
} catch (error) {
|
|
54
|
+
const duration = Date.now() - startTime;
|
|
55
|
+
this.log(`${description} failed after ${duration}ms`, 'error');
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
success: false,
|
|
59
|
+
error: error.message,
|
|
60
|
+
output: error.stdout,
|
|
61
|
+
duration
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
parseJestOutput(output) {
|
|
67
|
+
const lines = output.split('\n');
|
|
68
|
+
const results = {
|
|
69
|
+
passed: 0,
|
|
70
|
+
failed: 0,
|
|
71
|
+
total: 0,
|
|
72
|
+
suites: 0
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
lines.forEach(line => {
|
|
76
|
+
const match = line.match(/Tests:\s+(\d+)\s+passed,\s+(\d+)\s+failed/);
|
|
77
|
+
if (match) {
|
|
78
|
+
results.passed = parseInt(match[1]);
|
|
79
|
+
results.failed = parseInt(match[2]);
|
|
80
|
+
results.total = results.passed + results.failed;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const suiteMatch = line.match(/Test Suites:\s+(\d+)\s+passed,\s+(\d+)\s+failed/);
|
|
84
|
+
if (suiteMatch) {
|
|
85
|
+
results.suites = parseInt(suiteMatch[1]) + parseInt(suiteMatch[2]);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
return results;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async runUnitTests() {
|
|
93
|
+
this.log('\n๐งช Running Unit Tests', 'info');
|
|
94
|
+
|
|
95
|
+
const result = await this.runCommand(
|
|
96
|
+
'npm run test:unit -- --verbose --json',
|
|
97
|
+
'Unit Tests'
|
|
98
|
+
);
|
|
99
|
+
|
|
100
|
+
if (result.success) {
|
|
101
|
+
const jestOutput = result.output;
|
|
102
|
+
try {
|
|
103
|
+
const jsonMatch = jestOutput.match(/\{[\s\S]*\}/);
|
|
104
|
+
if (jsonMatch) {
|
|
105
|
+
const jestResults = JSON.parse(jsonMatch[0]);
|
|
106
|
+
this.testResults.unit.passed = jestResults.numPassedTests || 0;
|
|
107
|
+
this.testResults.unit.failed = jestResults.numFailedTests || 0;
|
|
108
|
+
this.testResults.unit.total = jestResults.numTotalTests || 0;
|
|
109
|
+
this.testResults.unit.duration = jestResults.testResults?.reduce(
|
|
110
|
+
(total, test) => total + (test.duration || 0), 0
|
|
111
|
+
) || 0;
|
|
112
|
+
} else {
|
|
113
|
+
const parsed = this.parseJestOutput(result.output);
|
|
114
|
+
Object.assign(this.testResults.unit, parsed);
|
|
115
|
+
}
|
|
116
|
+
} catch (parseError) {
|
|
117
|
+
this.log('Failed to parse Jest output, using fallback parsing', 'warning');
|
|
118
|
+
const parsed = this.parseJestOutput(result.output);
|
|
119
|
+
Object.assign(this.testResults.unit, parsed);
|
|
120
|
+
}
|
|
121
|
+
} else {
|
|
122
|
+
this.testResults.unit.failed = 1;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return result.success;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async runIntegrationTests() {
|
|
129
|
+
this.log('\n๐ Running Integration Tests', 'info');
|
|
130
|
+
|
|
131
|
+
const result = await this.runCommand(
|
|
132
|
+
'npm run test:integration -- --verbose',
|
|
133
|
+
'Integration Tests'
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
if (result.success) {
|
|
137
|
+
const parsed = this.parseJestOutput(result.output);
|
|
138
|
+
Object.assign(this.testResults.integration, parsed);
|
|
139
|
+
this.testResults.integration.duration = result.duration;
|
|
140
|
+
} else {
|
|
141
|
+
this.testResults.integration.failed = 1;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return result.success;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async runRegressionTests() {
|
|
148
|
+
this.log('\n๐ Running Regression Tests', 'info');
|
|
149
|
+
|
|
150
|
+
const result = await this.runCommand(
|
|
151
|
+
'npm run test:regression -- --verbose',
|
|
152
|
+
'Regression Tests'
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
if (result.success) {
|
|
156
|
+
const parsed = this.parseJestOutput(result.output);
|
|
157
|
+
Object.assign(this.testResults.regression, parsed);
|
|
158
|
+
this.testResults.regression.duration = result.duration;
|
|
159
|
+
} else {
|
|
160
|
+
this.testResults.regression.failed = 1;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return result.success;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
async runPerformanceTests() {
|
|
167
|
+
this.log('\nโก Running Performance Tests', 'info');
|
|
168
|
+
|
|
169
|
+
const result = await this.runCommand(
|
|
170
|
+
'npm run test:performance -- --verbose',
|
|
171
|
+
'Performance Tests'
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
if (result.success) {
|
|
175
|
+
const parsed = this.parseJestOutput(result.output);
|
|
176
|
+
Object.assign(this.testResults.performance, parsed);
|
|
177
|
+
this.testResults.performance.duration = result.duration;
|
|
178
|
+
} else {
|
|
179
|
+
this.testResults.performance.failed = 1;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return result.success;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
generateReport() {
|
|
186
|
+
const totalDuration = Date.now() - this.startTime;
|
|
187
|
+
const allTests = Object.values(this.testResults);
|
|
188
|
+
const totalPassed = allTests.reduce((sum, cat) => sum + cat.passed, 0);
|
|
189
|
+
const totalFailed = allTests.reduce((sum, cat) => sum + cat.failed, 0);
|
|
190
|
+
const totalTests = totalPassed + totalFailed;
|
|
191
|
+
|
|
192
|
+
console.log('\n' + '='.repeat(80));
|
|
193
|
+
console.log(chalk.bold.blue('๐ TEST SUITE REPORT'));
|
|
194
|
+
console.log('='.repeat(80));
|
|
195
|
+
|
|
196
|
+
// Summary
|
|
197
|
+
console.log(chalk.bold('\n๐ SUMMARY:'));
|
|
198
|
+
console.log(`Total Duration: ${(totalDuration / 1000).toFixed(2)}s`);
|
|
199
|
+
console.log(`Total Tests: ${totalTests}`);
|
|
200
|
+
console.log(`Passed: ${chalk.green(totalPassed)}`);
|
|
201
|
+
console.log(`Failed: ${chalk.red(totalFailed)}`);
|
|
202
|
+
console.log(`Success Rate: ${((totalPassed / totalTests) * 100).toFixed(1)}%`);
|
|
203
|
+
|
|
204
|
+
// Category breakdown
|
|
205
|
+
console.log(chalk.bold('\n๐ TEST CATEGORIES:'));
|
|
206
|
+
|
|
207
|
+
const categories = [
|
|
208
|
+
{ name: 'Unit Tests', key: 'unit', icon: '๐งช' },
|
|
209
|
+
{ name: 'Integration Tests', key: 'integration', icon: '๐' },
|
|
210
|
+
{ name: 'Regression Tests', key: 'regression', icon: '๐' },
|
|
211
|
+
{ name: 'Performance Tests', key: 'performance', icon: 'โก' }
|
|
212
|
+
];
|
|
213
|
+
|
|
214
|
+
categories.forEach(category => {
|
|
215
|
+
const results = this.testResults[category.key];
|
|
216
|
+
const success = results.failed === 0;
|
|
217
|
+
const status = success ? 'โ
' : 'โ';
|
|
218
|
+
const color = success ? chalk.green : chalk.red;
|
|
219
|
+
|
|
220
|
+
console.log(`\n${category.icon} ${category.name}: ${status}`);
|
|
221
|
+
console.log(` Tests: ${results.total}`);
|
|
222
|
+
console.log(` Passed: ${color(results.passed)}`);
|
|
223
|
+
console.log(` Failed: ${results.failed > 0 ? chalk.red(results.failed) : results.failed}`);
|
|
224
|
+
console.log(` Duration: ${(results.duration / 1000).toFixed(2)}s`);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// Coverage information if available
|
|
228
|
+
const coveragePath = path.join(process.cwd(), 'coverage', 'coverage-summary.json');
|
|
229
|
+
if (fs.existsSync(coveragePath)) {
|
|
230
|
+
try {
|
|
231
|
+
const coverage = fs.readJsonSync(coveragePath);
|
|
232
|
+
console.log(chalk.bold('\n๐ COVERAGE SUMMARY:'));
|
|
233
|
+
console.log(`Lines: ${coverage.total.lines.pct}%`);
|
|
234
|
+
console.log(`Functions: ${coverage.total.functions.pct}%`);
|
|
235
|
+
console.log(`Branches: ${coverage.total.branches.pct}%`);
|
|
236
|
+
console.log(`Statements: ${coverage.total.statements.pct}%`);
|
|
237
|
+
} catch (error) {
|
|
238
|
+
this.log('Could not read coverage information', 'warning');
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
console.log('\n' + '='.repeat(80));
|
|
243
|
+
|
|
244
|
+
// Exit with appropriate code
|
|
245
|
+
const exitCode = totalFailed > 0 ? 1 : 0;
|
|
246
|
+
|
|
247
|
+
if (exitCode === 0) {
|
|
248
|
+
console.log(chalk.green.bold('\n๐ ALL TESTS PASSED!'));
|
|
249
|
+
} else {
|
|
250
|
+
console.log(chalk.red.bold(`\nโ ${totalFailed} TEST(S) FAILED!`));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return exitCode;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async runTestSuite() {
|
|
257
|
+
console.log(chalk.bold.blue('๐ Starting HookDeploymentManager Test Suite'));
|
|
258
|
+
console.log(`Node.js version: ${process.version}`);
|
|
259
|
+
console.log(`Platform: ${process.platform}`);
|
|
260
|
+
console.log(`Working directory: ${process.cwd()}`);
|
|
261
|
+
|
|
262
|
+
try {
|
|
263
|
+
// Clean previous results
|
|
264
|
+
await fs.remove('coverage');
|
|
265
|
+
await fs.remove('test-results');
|
|
266
|
+
|
|
267
|
+
const unitSuccess = await this.runUnitTests();
|
|
268
|
+
const integrationSuccess = await this.runIntegrationTests();
|
|
269
|
+
const regressionSuccess = await this.runRegressionTests();
|
|
270
|
+
|
|
271
|
+
// Run performance tests only if others pass (to save time)
|
|
272
|
+
let performanceSuccess = true;
|
|
273
|
+
if (unitSuccess && integrationSuccess && regressionSuccess) {
|
|
274
|
+
performanceSuccess = await this.runPerformanceTests();
|
|
275
|
+
} else {
|
|
276
|
+
this.log('Skipping performance tests due to failures in other categories', 'warning');
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// Generate report and exit
|
|
280
|
+
const exitCode = this.generateReport();
|
|
281
|
+
process.exit(exitCode);
|
|
282
|
+
|
|
283
|
+
} catch (error) {
|
|
284
|
+
this.log(`Test runner error: ${error.message}`, 'error');
|
|
285
|
+
console.error(error.stack);
|
|
286
|
+
process.exit(1);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// CLI interface
|
|
292
|
+
if (require.main === module) {
|
|
293
|
+
const runner = new TestRunner();
|
|
294
|
+
|
|
295
|
+
// Parse command line arguments
|
|
296
|
+
const args = process.argv.slice(2);
|
|
297
|
+
|
|
298
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
299
|
+
console.log(`
|
|
300
|
+
HookDeploymentManager Test Runner
|
|
301
|
+
|
|
302
|
+
Usage: node scripts/test-runner.js [options]
|
|
303
|
+
|
|
304
|
+
Options:
|
|
305
|
+
--help, -h Show this help message
|
|
306
|
+
--unit-only Run only unit tests
|
|
307
|
+
--integration-only Run only integration tests
|
|
308
|
+
--regression-only Run only regression tests
|
|
309
|
+
--performance-only Run only performance tests
|
|
310
|
+
--no-performance Skip performance tests
|
|
311
|
+
--verbose Enable verbose output
|
|
312
|
+
|
|
313
|
+
Examples:
|
|
314
|
+
node scripts/test-runner.js # Run all tests
|
|
315
|
+
node scripts/test-runner.js --unit-only # Run only unit tests
|
|
316
|
+
node scripts/test-runner.js --no-performance # Skip performance tests
|
|
317
|
+
`);
|
|
318
|
+
process.exit(0);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// Handle specific test category requests
|
|
322
|
+
if (args.includes('--unit-only')) {
|
|
323
|
+
runner.runUnitTests().then(success => {
|
|
324
|
+
process.exit(success ? 0 : 1);
|
|
325
|
+
});
|
|
326
|
+
} else if (args.includes('--integration-only')) {
|
|
327
|
+
runner.runIntegrationTests().then(success => {
|
|
328
|
+
process.exit(success ? 0 : 1);
|
|
329
|
+
});
|
|
330
|
+
} else if (args.includes('--regression-only')) {
|
|
331
|
+
runner.runRegressionTests().then(success => {
|
|
332
|
+
process.exit(success ? 0 : 1);
|
|
333
|
+
});
|
|
334
|
+
} else if (args.includes('--performance-only')) {
|
|
335
|
+
runner.runPerformanceTests().then(success => {
|
|
336
|
+
process.exit(success ? 0 : 1);
|
|
337
|
+
});
|
|
338
|
+
} else {
|
|
339
|
+
// Run all tests
|
|
340
|
+
runner.runTestSuite();
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
module.exports = TestRunner;
|