@oalacea/demon 1.0.0
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/CHANGELOG.md +38 -0
- package/LICENSE +23 -0
- package/README.md +103 -0
- package/agents/deps-analyzer.js +366 -0
- package/agents/detector.js +570 -0
- package/agents/fix-engine.js +305 -0
- package/agents/perf-analyzer.js +294 -0
- package/agents/test-generator.js +387 -0
- package/agents/test-runner.js +318 -0
- package/bin/Dockerfile +65 -0
- package/bin/cli.js +455 -0
- package/lib/config.js +237 -0
- package/lib/docker.js +207 -0
- package/lib/reporter.js +297 -0
- package/package.json +34 -0
- package/prompts/DEPS_EFFICIENCY.md +558 -0
- package/prompts/E2E.md +491 -0
- package/prompts/EXECUTE.md +782 -0
- package/prompts/INTEGRATION_API.md +484 -0
- package/prompts/INTEGRATION_DB.md +425 -0
- package/prompts/PERF_API.md +433 -0
- package/prompts/PERF_DB.md +430 -0
- package/prompts/REMEDIATION.md +482 -0
- package/prompts/UNIT.md +260 -0
- package/scripts/dev.js +106 -0
- package/templates/README.md +22 -0
- package/templates/k6/load-test.js +54 -0
- package/templates/playwright/e2e.spec.ts +61 -0
- package/templates/vitest/api.test.ts +51 -0
- package/templates/vitest/component.test.ts +27 -0
- package/templates/vitest/hook.test.ts +36 -0
package/lib/reporter.js
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Demon - Reporter
|
|
3
|
+
*
|
|
4
|
+
* Generates test reports and summaries.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const fs = require('fs');
|
|
8
|
+
const path = require('path');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Generate test report
|
|
12
|
+
*/
|
|
13
|
+
function generateReport(results, options = {}) {
|
|
14
|
+
const report = {
|
|
15
|
+
summary: generateSummary(results),
|
|
16
|
+
details: results,
|
|
17
|
+
recommendations: generateRecommendations(results),
|
|
18
|
+
timestamp: new Date().toISOString(),
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
if (options.output) {
|
|
22
|
+
const outputPath = path.resolve(options.output);
|
|
23
|
+
fs.writeFileSync(outputPath, JSON.stringify(report, null, 2));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return report;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Generate summary
|
|
31
|
+
*/
|
|
32
|
+
function generateSummary(results) {
|
|
33
|
+
const summary = {
|
|
34
|
+
totalTests: 0,
|
|
35
|
+
passing: 0,
|
|
36
|
+
failing: 0,
|
|
37
|
+
skipped: 0,
|
|
38
|
+
coverage: results.coverage || null,
|
|
39
|
+
performance: results.performance || null,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// Count unit tests
|
|
43
|
+
if (results.unit) {
|
|
44
|
+
summary.totalTests += results.unit.total || 0;
|
|
45
|
+
summary.passing += results.unit.passing || 0;
|
|
46
|
+
summary.failing += results.unit.failing || 0;
|
|
47
|
+
summary.skipped += results.unit.skipped || 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Count integration tests
|
|
51
|
+
if (results.integration) {
|
|
52
|
+
summary.totalTests += results.integration.total || 0;
|
|
53
|
+
summary.passing += results.integration.passing || 0;
|
|
54
|
+
summary.failing += results.integration.failing || 0;
|
|
55
|
+
summary.skipped += results.integration.skipped || 0;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Count E2E tests
|
|
59
|
+
if (results.e2e) {
|
|
60
|
+
summary.totalTests += results.e2e.total || 0;
|
|
61
|
+
summary.passing += results.e2e.passing || 0;
|
|
62
|
+
summary.failing += results.e2e.failing || 0;
|
|
63
|
+
summary.skipped += results.e2e.skipped || 0;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
summary.passRate = summary.totalTests > 0
|
|
67
|
+
? ((summary.passing / summary.totalTests) * 100).toFixed(2) + '%'
|
|
68
|
+
: 'N/A';
|
|
69
|
+
|
|
70
|
+
return summary;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Generate recommendations
|
|
75
|
+
*/
|
|
76
|
+
function generateRecommendations(results) {
|
|
77
|
+
const recommendations = [];
|
|
78
|
+
|
|
79
|
+
// Coverage recommendations
|
|
80
|
+
if (results.coverage) {
|
|
81
|
+
const coverage = results.coverage;
|
|
82
|
+
if (coverage.lines && coverage.lines < 80) {
|
|
83
|
+
recommendations.push({
|
|
84
|
+
type: 'coverage',
|
|
85
|
+
priority: 'high',
|
|
86
|
+
message: `Line coverage is ${coverage.lines}%. Aim for at least 80%.`,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
if (coverage.branches && coverage.branches < 80) {
|
|
90
|
+
recommendations.push({
|
|
91
|
+
type: 'coverage',
|
|
92
|
+
priority: 'medium',
|
|
93
|
+
message: `Branch coverage is ${coverage.branches}%. Add tests for conditional logic.`,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Performance recommendations
|
|
99
|
+
if (results.performance) {
|
|
100
|
+
const perf = results.performance;
|
|
101
|
+
if (perf.p95 && perf.p95 > 200) {
|
|
102
|
+
recommendations.push({
|
|
103
|
+
type: 'performance',
|
|
104
|
+
priority: 'high',
|
|
105
|
+
message: `API p95 is ${perf.p95}ms. Target is <200ms. Consider optimization.`,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
if (perf.failureRate && perf.failureRate > 1) {
|
|
109
|
+
recommendations.push({
|
|
110
|
+
type: 'performance',
|
|
111
|
+
priority: 'high',
|
|
112
|
+
message: `Failure rate is ${perf.failureRate}%. Check for infrastructure issues.`,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Test failure recommendations
|
|
118
|
+
if (results.unit && results.unit.failing > 0) {
|
|
119
|
+
recommendations.push({
|
|
120
|
+
type: 'tests',
|
|
121
|
+
priority: 'high',
|
|
122
|
+
message: `${results.unit.failing} unit test(s) failing. Review and fix.`,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (results.e2e && results.e2e.failing > 0) {
|
|
127
|
+
recommendations.push({
|
|
128
|
+
type: 'tests',
|
|
129
|
+
priority: 'medium',
|
|
130
|
+
message: `${results.e2e.failing} E2E test(s) failing. Check for flaky tests.`,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return recommendations;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Format report as markdown
|
|
139
|
+
*/
|
|
140
|
+
function formatMarkdown(report) {
|
|
141
|
+
const lines = [];
|
|
142
|
+
|
|
143
|
+
lines.push('# Demon Test Report');
|
|
144
|
+
lines.push('');
|
|
145
|
+
lines.push(`**Generated:** ${new Date(report.timestamp).toLocaleString()}`);
|
|
146
|
+
lines.push('');
|
|
147
|
+
|
|
148
|
+
// Summary
|
|
149
|
+
lines.push('## Summary');
|
|
150
|
+
lines.push('');
|
|
151
|
+
lines.push('| Metric | Value |');
|
|
152
|
+
lines.push('|--------|-------|');
|
|
153
|
+
lines.push(`| Total Tests | ${report.summary.totalTests} |`);
|
|
154
|
+
lines.push(`| Passing | ${report.summary.passing} |`);
|
|
155
|
+
lines.push(`| Failing | ${report.summary.failing} |`);
|
|
156
|
+
lines.push(`| Skipped | ${report.summary.skipped} |`);
|
|
157
|
+
lines.push(`| Pass Rate | ${report.summary.passRate} |`);
|
|
158
|
+
|
|
159
|
+
if (report.summary.coverage) {
|
|
160
|
+
lines.push(`| Coverage | ${report.summary.coverage.lines || 'N/A'}% (lines) |`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (report.summary.performance) {
|
|
164
|
+
const perf = report.summary.performance;
|
|
165
|
+
lines.push(`| API p95 | ${perf.p95 || 'N/A'}ms |`);
|
|
166
|
+
lines.push(`| API p99 | ${perf.p99 || 'N/A'}ms |`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
lines.push('');
|
|
170
|
+
|
|
171
|
+
// Recommendations
|
|
172
|
+
if (report.recommendations.length > 0) {
|
|
173
|
+
lines.push('## Recommendations');
|
|
174
|
+
lines.push('');
|
|
175
|
+
|
|
176
|
+
for (const rec of report.recommendations) {
|
|
177
|
+
const icon = rec.priority === 'high' ? '🔴' : rec.priority === 'medium' ? '🟡' : '🟢';
|
|
178
|
+
lines.push(`### ${icon} ${rec.type}`);
|
|
179
|
+
lines.push('');
|
|
180
|
+
lines.push(rec.message);
|
|
181
|
+
lines.push('');
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Details by layer
|
|
186
|
+
lines.push('## Details by Layer');
|
|
187
|
+
lines.push('');
|
|
188
|
+
|
|
189
|
+
for (const [layer, data] of Object.entries(report.details)) {
|
|
190
|
+
if (!data || typeof data !== 'object') continue;
|
|
191
|
+
|
|
192
|
+
lines.push(`### ${layer.charAt(0).toUpperCase() + layer.slice(1)}`);
|
|
193
|
+
lines.push('');
|
|
194
|
+
lines.push(`| Tests | Pass | Fail | Skip |`);
|
|
195
|
+
lines.push(`|-------|------|------|------|`);
|
|
196
|
+
lines.push(
|
|
197
|
+
`| ${data.total || 0} | ${data.passing || 0} | ${data.failing || 0} | ${data.skipped || 0} |`
|
|
198
|
+
);
|
|
199
|
+
lines.push('');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return lines.join('\n');
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Format report as JSON
|
|
207
|
+
*/
|
|
208
|
+
function formatJSON(report) {
|
|
209
|
+
return JSON.stringify(report, null, 2);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Format report as console output
|
|
214
|
+
*/
|
|
215
|
+
function formatConsole(report) {
|
|
216
|
+
const lines = [];
|
|
217
|
+
|
|
218
|
+
lines.push('');
|
|
219
|
+
lines.push('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
220
|
+
lines.push(' Demon Test Report');
|
|
221
|
+
lines.push('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
222
|
+
lines.push('');
|
|
223
|
+
|
|
224
|
+
const summary = report.summary;
|
|
225
|
+
|
|
226
|
+
lines.push(` Total Tests: ${summary.totalTests}`);
|
|
227
|
+
lines.push(` Passing: ${summary.passing} ✓`);
|
|
228
|
+
lines.push(` Failing: ${summary.failing} ✗`);
|
|
229
|
+
lines.push(` Skipped: ${summary.skipped} ○`);
|
|
230
|
+
lines.push(` Pass Rate: ${summary.passRate}`);
|
|
231
|
+
|
|
232
|
+
if (summary.coverage) {
|
|
233
|
+
lines.push(` Coverage: ${summary.coverage.lines || 'N/A'}%`);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (summary.performance) {
|
|
237
|
+
lines.push(` API p95: ${summary.performance.p95 || 'N/A'}ms`);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
lines.push('');
|
|
241
|
+
|
|
242
|
+
if (report.recommendations.length > 0) {
|
|
243
|
+
lines.push(' Recommendations:');
|
|
244
|
+
for (const rec of report.recommendations) {
|
|
245
|
+
const icon = rec.priority === 'high' ? '✗' : rec.priority === 'medium' ? '⚠' : 'ℹ';
|
|
246
|
+
lines.push(` ${icon} ${rec.message}`);
|
|
247
|
+
}
|
|
248
|
+
lines.push('');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
lines.push('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
252
|
+
lines.push('');
|
|
253
|
+
|
|
254
|
+
return lines.join('\n');
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Save report to file
|
|
259
|
+
*/
|
|
260
|
+
function saveReport(report, format, outputPath) {
|
|
261
|
+
let content;
|
|
262
|
+
|
|
263
|
+
switch (format) {
|
|
264
|
+
case 'markdown':
|
|
265
|
+
content = formatMarkdown(report);
|
|
266
|
+
break;
|
|
267
|
+
case 'json':
|
|
268
|
+
content = formatJSON(report);
|
|
269
|
+
break;
|
|
270
|
+
case 'console':
|
|
271
|
+
content = formatConsole(report);
|
|
272
|
+
break;
|
|
273
|
+
default:
|
|
274
|
+
throw new Error(`Unknown format: ${format}`);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
fs.writeFileSync(outputPath, content);
|
|
278
|
+
return outputPath;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Print report to console
|
|
283
|
+
*/
|
|
284
|
+
function printReport(report) {
|
|
285
|
+
console.log(formatConsole(report));
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
module.exports = {
|
|
289
|
+
generateReport,
|
|
290
|
+
generateSummary,
|
|
291
|
+
generateRecommendations,
|
|
292
|
+
formatMarkdown,
|
|
293
|
+
formatJSON,
|
|
294
|
+
formatConsole,
|
|
295
|
+
saveReport,
|
|
296
|
+
printReport,
|
|
297
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@oalacea/demon",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "AI-powered automated test generation and remediation for web applications",
|
|
5
|
+
"author": "Yanis",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/Pamacea/demon"
|
|
10
|
+
},
|
|
11
|
+
"bin": {
|
|
12
|
+
"demon": "./bin/cli.js"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"test": "echo \"Run: npx @oalacea/demon\" && exit 0"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"testing",
|
|
19
|
+
"automation",
|
|
20
|
+
"ai",
|
|
21
|
+
"e2e",
|
|
22
|
+
"unit-tests",
|
|
23
|
+
"integration-tests",
|
|
24
|
+
"performance",
|
|
25
|
+
"vitest",
|
|
26
|
+
"playwright",
|
|
27
|
+
"k6"
|
|
28
|
+
],
|
|
29
|
+
"dependencies": {},
|
|
30
|
+
"devDependencies": {},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=18.0.0"
|
|
33
|
+
}
|
|
34
|
+
}
|