@avi770/testteam 1.2.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 +54 -0
- package/LICENSE +21 -0
- package/README.md +167 -0
- package/agents/01-analyst.ts +100 -0
- package/agents/02-seed-architect.ts +59 -0
- package/agents/03-test-generator.ts +191 -0
- package/agents/04-unit-runner.ts +160 -0
- package/agents/05-browser-crawler.ts +790 -0
- package/agents/06-api-exerciser.ts +311 -0
- package/agents/07-security-scout.ts +188 -0
- package/agents/08-a11y-guardian.ts +212 -0
- package/agents/09-healer.ts +228 -0
- package/agents/10-reporter.ts +266 -0
- package/agents/11-fixer.ts +253 -0
- package/agents/12-ux-inspector.ts +444 -0
- package/agents/13-performance-profiler.ts +271 -0
- package/agents/14-data-integrity-auditor.ts +417 -0
- package/agents/15-regression-sentinel.ts +307 -0
- package/agents/16-chaos-agent.ts +228 -0
- package/agents/17-documentation-validator.ts +266 -0
- package/agents/18-integration-watchdog.ts +178 -0
- package/agents/19-tenant-isolation-auditor.ts +199 -0
- package/agents/20-workflow-completion-tester.ts +203 -0
- package/agents/21-state-session-tester.ts +262 -0
- package/agents/22-email-notification-verifier.ts +244 -0
- package/agents/23-migration-tester.ts +80 -0
- package/agents/__tests__/01-analyst.test.ts +188 -0
- package/agents/__tests__/02-seed-architect.test.ts +152 -0
- package/agents/__tests__/03-test-generator-full.test.ts +321 -0
- package/agents/__tests__/03-test-generator.test.ts +318 -0
- package/agents/__tests__/04-unit-runner.test.ts +320 -0
- package/agents/__tests__/05-browser-crawler-beta.test.ts +492 -0
- package/agents/__tests__/05-browser-crawler-release.test.ts +412 -0
- package/agents/__tests__/05-browser-crawler-uat.test.ts +578 -0
- package/agents/__tests__/05-browser-crawler.test.ts +518 -0
- package/agents/__tests__/06-api-exerciser.test.ts +619 -0
- package/agents/__tests__/07-security-scout.test.ts +382 -0
- package/agents/__tests__/08-a11y-guardian.test.ts +530 -0
- package/agents/__tests__/09-healer.test.ts +384 -0
- package/agents/__tests__/10-reporter.test.ts +366 -0
- package/agents/__tests__/11-fixer.test.ts +406 -0
- package/agents/__tests__/12-ux-inspector-extended.test.ts +465 -0
- package/agents/__tests__/12-ux-inspector.test.ts +443 -0
- package/agents/__tests__/13-performance-profiler.test.ts +411 -0
- package/agents/__tests__/14-data-integrity-auditor-extended.test.ts +573 -0
- package/agents/__tests__/14-data-integrity-auditor.test.ts +407 -0
- package/agents/__tests__/15-regression-sentinel.test.ts +657 -0
- package/agents/__tests__/16-chaos-agent.test.ts +427 -0
- package/agents/__tests__/17-documentation-validator.test.ts +402 -0
- package/agents/__tests__/18-integration-watchdog.test.ts +263 -0
- package/agents/__tests__/19-tenant-isolation-auditor.test.ts +400 -0
- package/agents/__tests__/20-workflow-completion-tester.test.ts +586 -0
- package/agents/__tests__/21-state-session-tester.test.ts +374 -0
- package/agents/__tests__/22-email-notification-verifier.test.ts +441 -0
- package/agents/__tests__/23-migration-tester.test.ts +145 -0
- package/agents/__tests__/base-agent.test.ts +188 -0
- package/agents/__tests__/registry.test.ts +218 -0
- package/agents/base-agent.ts +77 -0
- package/agents/registry.ts +136 -0
- package/baselines/api-schemas/.gitkeep +0 -0
- package/baselines/performance/.gitkeep +0 -0
- package/baselines/screenshots/.gitkeep +0 -0
- package/bin/testteam.js +10 -0
- package/core/__tests__/ci-output.test.ts +430 -0
- package/core/__tests__/cli.test.ts +387 -0
- package/core/__tests__/config.test.ts +78 -0
- package/core/__tests__/cost-tracker.test.ts +158 -0
- package/core/__tests__/evidence.test.ts +265 -0
- package/core/__tests__/fix-loop.test.ts +210 -0
- package/core/__tests__/health-check.test.ts +44 -0
- package/core/__tests__/init.test.ts +609 -0
- package/core/__tests__/integration.test.ts +204 -0
- package/core/__tests__/license-gen.test.ts +227 -0
- package/core/__tests__/license.test.ts +326 -0
- package/core/__tests__/multi-browser.test.ts +278 -0
- package/core/__tests__/orchestrator.test.ts +519 -0
- package/core/__tests__/phase-gate.test.ts +43 -0
- package/core/__tests__/report-html.test.ts +398 -0
- package/core/__tests__/report-upload.test.ts +325 -0
- package/core/__tests__/run-counter.test.ts +234 -0
- package/core/ci-output.ts +240 -0
- package/core/cli.ts +232 -0
- package/core/config.ts +178 -0
- package/core/cost-tracker.ts +59 -0
- package/core/evidence.ts +132 -0
- package/core/fix-loop.ts +85 -0
- package/core/health-check.ts +54 -0
- package/core/init.ts +546 -0
- package/core/license-gen.ts +212 -0
- package/core/license.ts +211 -0
- package/core/messages.ts +67 -0
- package/core/multi-browser.ts +136 -0
- package/core/orchestrator.ts +354 -0
- package/core/phase-gate.ts +55 -0
- package/core/report-html.ts +657 -0
- package/core/report-upload.ts +188 -0
- package/core/run-counter.ts +175 -0
- package/core/types.ts +57 -0
- package/dist/agents/01-analyst.d.ts +11 -0
- package/dist/agents/01-analyst.d.ts.map +1 -0
- package/dist/agents/01-analyst.js +75 -0
- package/dist/agents/01-analyst.js.map +1 -0
- package/dist/agents/02-seed-architect.d.ts +11 -0
- package/dist/agents/02-seed-architect.d.ts.map +1 -0
- package/dist/agents/02-seed-architect.js +51 -0
- package/dist/agents/02-seed-architect.js.map +1 -0
- package/dist/agents/03-test-generator.d.ts +9 -0
- package/dist/agents/03-test-generator.d.ts.map +1 -0
- package/dist/agents/03-test-generator.js +167 -0
- package/dist/agents/03-test-generator.js.map +1 -0
- package/dist/agents/04-unit-runner.d.ts +9 -0
- package/dist/agents/04-unit-runner.d.ts.map +1 -0
- package/dist/agents/04-unit-runner.js +113 -0
- package/dist/agents/04-unit-runner.js.map +1 -0
- package/dist/agents/05-browser-crawler.d.ts +30 -0
- package/dist/agents/05-browser-crawler.d.ts.map +1 -0
- package/dist/agents/05-browser-crawler.js +685 -0
- package/dist/agents/05-browser-crawler.js.map +1 -0
- package/dist/agents/06-api-exerciser.d.ts +23 -0
- package/dist/agents/06-api-exerciser.d.ts.map +1 -0
- package/dist/agents/06-api-exerciser.js +253 -0
- package/dist/agents/06-api-exerciser.js.map +1 -0
- package/dist/agents/07-security-scout.d.ts +11 -0
- package/dist/agents/07-security-scout.d.ts.map +1 -0
- package/dist/agents/07-security-scout.js +142 -0
- package/dist/agents/07-security-scout.js.map +1 -0
- package/dist/agents/08-a11y-guardian.d.ts +13 -0
- package/dist/agents/08-a11y-guardian.d.ts.map +1 -0
- package/dist/agents/08-a11y-guardian.js +176 -0
- package/dist/agents/08-a11y-guardian.js.map +1 -0
- package/dist/agents/09-healer.d.ts +33 -0
- package/dist/agents/09-healer.d.ts.map +1 -0
- package/dist/agents/09-healer.js +167 -0
- package/dist/agents/09-healer.js.map +1 -0
- package/dist/agents/10-reporter.d.ts +26 -0
- package/dist/agents/10-reporter.d.ts.map +1 -0
- package/dist/agents/10-reporter.js +215 -0
- package/dist/agents/10-reporter.js.map +1 -0
- package/dist/agents/11-fixer.d.ts +26 -0
- package/dist/agents/11-fixer.d.ts.map +1 -0
- package/dist/agents/11-fixer.js +195 -0
- package/dist/agents/11-fixer.js.map +1 -0
- package/dist/agents/12-ux-inspector.d.ts +15 -0
- package/dist/agents/12-ux-inspector.d.ts.map +1 -0
- package/dist/agents/12-ux-inspector.js +364 -0
- package/dist/agents/12-ux-inspector.js.map +1 -0
- package/dist/agents/13-performance-profiler.d.ts +13 -0
- package/dist/agents/13-performance-profiler.d.ts.map +1 -0
- package/dist/agents/13-performance-profiler.js +216 -0
- package/dist/agents/13-performance-profiler.js.map +1 -0
- package/dist/agents/14-data-integrity-auditor.d.ts +12 -0
- package/dist/agents/14-data-integrity-auditor.d.ts.map +1 -0
- package/dist/agents/14-data-integrity-auditor.js +356 -0
- package/dist/agents/14-data-integrity-auditor.js.map +1 -0
- package/dist/agents/15-regression-sentinel.d.ts +25 -0
- package/dist/agents/15-regression-sentinel.d.ts.map +1 -0
- package/dist/agents/15-regression-sentinel.js +251 -0
- package/dist/agents/15-regression-sentinel.js.map +1 -0
- package/dist/agents/16-chaos-agent.d.ts +9 -0
- package/dist/agents/16-chaos-agent.d.ts.map +1 -0
- package/dist/agents/16-chaos-agent.js +207 -0
- package/dist/agents/16-chaos-agent.js.map +1 -0
- package/dist/agents/17-documentation-validator.d.ts +31 -0
- package/dist/agents/17-documentation-validator.d.ts.map +1 -0
- package/dist/agents/17-documentation-validator.js +246 -0
- package/dist/agents/17-documentation-validator.js.map +1 -0
- package/dist/agents/18-integration-watchdog.d.ts +10 -0
- package/dist/agents/18-integration-watchdog.d.ts.map +1 -0
- package/dist/agents/18-integration-watchdog.js +138 -0
- package/dist/agents/18-integration-watchdog.js.map +1 -0
- package/dist/agents/19-tenant-isolation-auditor.d.ts +9 -0
- package/dist/agents/19-tenant-isolation-auditor.d.ts.map +1 -0
- package/dist/agents/19-tenant-isolation-auditor.js +166 -0
- package/dist/agents/19-tenant-isolation-auditor.js.map +1 -0
- package/dist/agents/20-workflow-completion-tester.d.ts +12 -0
- package/dist/agents/20-workflow-completion-tester.d.ts.map +1 -0
- package/dist/agents/20-workflow-completion-tester.js +159 -0
- package/dist/agents/20-workflow-completion-tester.js.map +1 -0
- package/dist/agents/21-state-session-tester.d.ts +10 -0
- package/dist/agents/21-state-session-tester.d.ts.map +1 -0
- package/dist/agents/21-state-session-tester.js +233 -0
- package/dist/agents/21-state-session-tester.js.map +1 -0
- package/dist/agents/22-email-notification-verifier.d.ts +11 -0
- package/dist/agents/22-email-notification-verifier.d.ts.map +1 -0
- package/dist/agents/22-email-notification-verifier.js +199 -0
- package/dist/agents/22-email-notification-verifier.js.map +1 -0
- package/dist/agents/23-migration-tester.d.ts +10 -0
- package/dist/agents/23-migration-tester.d.ts.map +1 -0
- package/dist/agents/23-migration-tester.js +74 -0
- package/dist/agents/23-migration-tester.js.map +1 -0
- package/dist/agents/base-agent.d.ts +19 -0
- package/dist/agents/base-agent.d.ts.map +1 -0
- package/dist/agents/base-agent.js +67 -0
- package/dist/agents/base-agent.js.map +1 -0
- package/dist/agents/registry.d.ts +29 -0
- package/dist/agents/registry.d.ts.map +1 -0
- package/dist/agents/registry.js +117 -0
- package/dist/agents/registry.js.map +1 -0
- package/dist/core/ci-output.d.ts +35 -0
- package/dist/core/ci-output.d.ts.map +1 -0
- package/dist/core/ci-output.js +193 -0
- package/dist/core/ci-output.js.map +1 -0
- package/dist/core/cli.d.ts +11 -0
- package/dist/core/cli.d.ts.map +1 -0
- package/dist/core/cli.js +197 -0
- package/dist/core/cli.js.map +1 -0
- package/dist/core/config.d.ts +111 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +42 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/cost-tracker.d.ts +22 -0
- package/dist/core/cost-tracker.d.ts.map +1 -0
- package/dist/core/cost-tracker.js +41 -0
- package/dist/core/cost-tracker.js.map +1 -0
- package/dist/core/evidence.d.ts +28 -0
- package/dist/core/evidence.d.ts.map +1 -0
- package/dist/core/evidence.js +95 -0
- package/dist/core/evidence.js.map +1 -0
- package/dist/core/fix-loop.d.ts +29 -0
- package/dist/core/fix-loop.d.ts.map +1 -0
- package/dist/core/fix-loop.js +70 -0
- package/dist/core/fix-loop.js.map +1 -0
- package/dist/core/health-check.d.ts +21 -0
- package/dist/core/health-check.d.ts.map +1 -0
- package/dist/core/health-check.js +26 -0
- package/dist/core/health-check.js.map +1 -0
- package/dist/core/init.d.ts +2 -0
- package/dist/core/init.d.ts.map +1 -0
- package/dist/core/init.js +435 -0
- package/dist/core/init.js.map +1 -0
- package/dist/core/license-gen.d.ts +12 -0
- package/dist/core/license-gen.d.ts.map +1 -0
- package/dist/core/license-gen.js +169 -0
- package/dist/core/license-gen.js.map +1 -0
- package/dist/core/license.d.ts +33 -0
- package/dist/core/license.d.ts.map +1 -0
- package/dist/core/license.js +170 -0
- package/dist/core/license.js.map +1 -0
- package/dist/core/messages.d.ts +10 -0
- package/dist/core/messages.d.ts.map +1 -0
- package/dist/core/messages.js +47 -0
- package/dist/core/messages.js.map +1 -0
- package/dist/core/multi-browser.d.ts +36 -0
- package/dist/core/multi-browser.d.ts.map +1 -0
- package/dist/core/multi-browser.js +88 -0
- package/dist/core/multi-browser.js.map +1 -0
- package/dist/core/orchestrator.d.ts +48 -0
- package/dist/core/orchestrator.d.ts.map +1 -0
- package/dist/core/orchestrator.js +291 -0
- package/dist/core/orchestrator.js.map +1 -0
- package/dist/core/phase-gate.d.ts +4 -0
- package/dist/core/phase-gate.d.ts.map +1 -0
- package/dist/core/phase-gate.js +39 -0
- package/dist/core/phase-gate.js.map +1 -0
- package/dist/core/report-html.d.ts +9 -0
- package/dist/core/report-html.d.ts.map +1 -0
- package/dist/core/report-html.js +617 -0
- package/dist/core/report-html.js.map +1 -0
- package/dist/core/report-upload.d.ts +16 -0
- package/dist/core/report-upload.d.ts.map +1 -0
- package/dist/core/report-upload.js +124 -0
- package/dist/core/report-upload.js.map +1 -0
- package/dist/core/run-counter.d.ts +40 -0
- package/dist/core/run-counter.d.ts.map +1 -0
- package/dist/core/run-counter.js +120 -0
- package/dist/core/run-counter.js.map +1 -0
- package/dist/core/types.d.ts +53 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/helpers/api-client.d.ts +30 -0
- package/dist/helpers/api-client.d.ts.map +1 -0
- package/dist/helpers/api-client.js +77 -0
- package/dist/helpers/api-client.js.map +1 -0
- package/dist/helpers/element-discovery.d.ts +18 -0
- package/dist/helpers/element-discovery.d.ts.map +1 -0
- package/dist/helpers/element-discovery.js +82 -0
- package/dist/helpers/element-discovery.js.map +1 -0
- package/dist/helpers/env-resolver.d.ts +29 -0
- package/dist/helpers/env-resolver.d.ts.map +1 -0
- package/dist/helpers/env-resolver.js +51 -0
- package/dist/helpers/env-resolver.js.map +1 -0
- package/dist/helpers/form-filler.d.ts +13 -0
- package/dist/helpers/form-filler.d.ts.map +1 -0
- package/dist/helpers/form-filler.js +98 -0
- package/dist/helpers/form-filler.js.map +1 -0
- package/dist/helpers/modal-handler.d.ts +16 -0
- package/dist/helpers/modal-handler.d.ts.map +1 -0
- package/dist/helpers/modal-handler.js +95 -0
- package/dist/helpers/modal-handler.js.map +1 -0
- package/dist/helpers/navigation.d.ts +37 -0
- package/dist/helpers/navigation.d.ts.map +1 -0
- package/dist/helpers/navigation.js +83 -0
- package/dist/helpers/navigation.js.map +1 -0
- package/dist/helpers/quality-gate.d.ts +17 -0
- package/dist/helpers/quality-gate.d.ts.map +1 -0
- package/dist/helpers/quality-gate.js +144 -0
- package/dist/helpers/quality-gate.js.map +1 -0
- package/dist/helpers/screenshot.d.ts +24 -0
- package/dist/helpers/screenshot.d.ts.map +1 -0
- package/dist/helpers/screenshot.js +76 -0
- package/dist/helpers/screenshot.js.map +1 -0
- package/dist/helpers/seed-validator.d.ts +15 -0
- package/dist/helpers/seed-validator.d.ts.map +1 -0
- package/dist/helpers/seed-validator.js +53 -0
- package/dist/helpers/seed-validator.js.map +1 -0
- package/helpers/__tests__/api-client.test.ts +199 -0
- package/helpers/__tests__/element-discovery.test.ts +202 -0
- package/helpers/__tests__/form-filler-extended.test.ts +212 -0
- package/helpers/__tests__/form-filler.test.ts +99 -0
- package/helpers/__tests__/modal-handler.test.ts +152 -0
- package/helpers/__tests__/navigation.test.ts +214 -0
- package/helpers/__tests__/quality-gate.test.ts +117 -0
- package/helpers/__tests__/screenshot.test.ts +139 -0
- package/helpers/__tests__/seed-validator.test.ts +114 -0
- package/helpers/api-client.ts +111 -0
- package/helpers/element-discovery.ts +105 -0
- package/helpers/env-resolver.ts +69 -0
- package/helpers/form-filler.ts +126 -0
- package/helpers/modal-handler.ts +108 -0
- package/helpers/navigation.ts +100 -0
- package/helpers/quality-gate.ts +180 -0
- package/helpers/screenshot.ts +111 -0
- package/helpers/seed-validator.ts +70 -0
- package/package.json +88 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { readFile, copyFile, mkdir, stat } from 'node:fs/promises';
|
|
2
|
+
import { basename, join, resolve } from 'node:path';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Dynamically load an optional npm package at runtime.
|
|
7
|
+
* Uses createRequire to avoid TypeScript static module resolution errors.
|
|
8
|
+
*/
|
|
9
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
|
+
async function optionalRequire(packageName: string): Promise<Record<string, any>> {
|
|
11
|
+
const require = createRequire(import.meta.url);
|
|
12
|
+
return require(packageName) as Record<string, any>;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface UploadConfig {
|
|
16
|
+
provider: 's3' | 'azure-blob' | 'local';
|
|
17
|
+
bucket?: string;
|
|
18
|
+
region?: string;
|
|
19
|
+
prefix?: string;
|
|
20
|
+
connectionString?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface UploadResult {
|
|
24
|
+
url: string;
|
|
25
|
+
provider: string;
|
|
26
|
+
size: number;
|
|
27
|
+
timestamp: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function generateReportFilename(originalPath: string): string {
|
|
31
|
+
const base = basename(originalPath, '.html');
|
|
32
|
+
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
|
33
|
+
return `${base}-${ts}.html`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
async function uploadLocal(
|
|
37
|
+
htmlPath: string,
|
|
38
|
+
config: UploadConfig,
|
|
39
|
+
): Promise<UploadResult> {
|
|
40
|
+
const prefix = config.prefix ?? 'testteam-reports/';
|
|
41
|
+
const reportsDir = resolve(prefix);
|
|
42
|
+
await mkdir(reportsDir, { recursive: true });
|
|
43
|
+
|
|
44
|
+
const filename = generateReportFilename(htmlPath);
|
|
45
|
+
const destPath = join(reportsDir, filename);
|
|
46
|
+
await copyFile(htmlPath, destPath);
|
|
47
|
+
|
|
48
|
+
const fileStat = await stat(destPath);
|
|
49
|
+
const absoluteDest = resolve(destPath);
|
|
50
|
+
|
|
51
|
+
return {
|
|
52
|
+
url: `file://${absoluteDest.replace(/\\/g, '/')}`,
|
|
53
|
+
provider: 'local',
|
|
54
|
+
size: fileStat.size,
|
|
55
|
+
timestamp: Date.now(),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function uploadS3(
|
|
60
|
+
htmlPath: string,
|
|
61
|
+
config: UploadConfig,
|
|
62
|
+
): Promise<UploadResult> {
|
|
63
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
64
|
+
let sdk: Record<string, any>;
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
sdk = await optionalRequire('@aws-sdk/client-s3');
|
|
68
|
+
} catch {
|
|
69
|
+
throw new Error(
|
|
70
|
+
'Install @aws-sdk/client-s3 to use S3 uploads: npm install @aws-sdk/client-s3',
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (!config.bucket) {
|
|
75
|
+
throw new Error('S3 upload requires a bucket name in config.bucket');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const prefix = config.prefix ?? 'testteam-reports/';
|
|
79
|
+
const filename = generateReportFilename(htmlPath);
|
|
80
|
+
const key = `${prefix}${filename}`;
|
|
81
|
+
|
|
82
|
+
const body = await readFile(htmlPath);
|
|
83
|
+
|
|
84
|
+
const client = new sdk.S3Client({
|
|
85
|
+
region: config.region ?? 'us-east-1',
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
await client.send(
|
|
89
|
+
new sdk.PutObjectCommand({
|
|
90
|
+
Bucket: config.bucket,
|
|
91
|
+
Key: key,
|
|
92
|
+
Body: body,
|
|
93
|
+
ContentType: 'text/html',
|
|
94
|
+
}),
|
|
95
|
+
);
|
|
96
|
+
|
|
97
|
+
const region = config.region ?? 'us-east-1';
|
|
98
|
+
const url = `https://${config.bucket}.s3.${region}.amazonaws.com/${key}`;
|
|
99
|
+
|
|
100
|
+
return {
|
|
101
|
+
url,
|
|
102
|
+
provider: 's3',
|
|
103
|
+
size: body.byteLength,
|
|
104
|
+
timestamp: Date.now(),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async function uploadAzureBlob(
|
|
109
|
+
htmlPath: string,
|
|
110
|
+
config: UploadConfig,
|
|
111
|
+
): Promise<UploadResult> {
|
|
112
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
113
|
+
let sdk: Record<string, any>;
|
|
114
|
+
|
|
115
|
+
try {
|
|
116
|
+
sdk = await optionalRequire('@azure/storage-blob');
|
|
117
|
+
} catch {
|
|
118
|
+
throw new Error(
|
|
119
|
+
'Install @azure/storage-blob to use Azure Blob uploads: npm install @azure/storage-blob',
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (!config.connectionString) {
|
|
124
|
+
throw new Error(
|
|
125
|
+
'Azure Blob upload requires a connectionString in config.connectionString',
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (!config.bucket) {
|
|
130
|
+
throw new Error(
|
|
131
|
+
'Azure Blob upload requires a container name in config.bucket',
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const prefix = config.prefix ?? 'testteam-reports/';
|
|
136
|
+
const filename = generateReportFilename(htmlPath);
|
|
137
|
+
const blobName = `${prefix}${filename}`;
|
|
138
|
+
|
|
139
|
+
const body = await readFile(htmlPath);
|
|
140
|
+
|
|
141
|
+
const blobServiceClient = sdk.BlobServiceClient.fromConnectionString(
|
|
142
|
+
config.connectionString,
|
|
143
|
+
);
|
|
144
|
+
const containerClient = blobServiceClient.getContainerClient(config.bucket);
|
|
145
|
+
await containerClient.createIfNotExists({ access: 'blob' });
|
|
146
|
+
|
|
147
|
+
const blockBlobClient = containerClient.getBlockBlobClient(blobName);
|
|
148
|
+
await blockBlobClient.upload(body, body.byteLength, {
|
|
149
|
+
blobHTTPHeaders: { blobContentType: 'text/html' },
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
url: blockBlobClient.url,
|
|
154
|
+
provider: 'azure-blob',
|
|
155
|
+
size: body.byteLength,
|
|
156
|
+
timestamp: Date.now(),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export async function uploadReport(
|
|
161
|
+
htmlPath: string,
|
|
162
|
+
config: UploadConfig,
|
|
163
|
+
): Promise<UploadResult> {
|
|
164
|
+
const absolutePath = resolve(htmlPath);
|
|
165
|
+
|
|
166
|
+
// Verify file exists before attempting upload
|
|
167
|
+
await stat(absolutePath);
|
|
168
|
+
|
|
169
|
+
switch (config.provider) {
|
|
170
|
+
case 'local':
|
|
171
|
+
return uploadLocal(absolutePath, config);
|
|
172
|
+
case 's3':
|
|
173
|
+
return uploadS3(absolutePath, config);
|
|
174
|
+
case 'azure-blob':
|
|
175
|
+
return uploadAzureBlob(absolutePath, config);
|
|
176
|
+
default: {
|
|
177
|
+
const exhaustive: never = config.provider;
|
|
178
|
+
throw new Error(`Unknown upload provider: ${String(exhaustive)}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export function getDefaultUploadConfig(): UploadConfig {
|
|
184
|
+
return {
|
|
185
|
+
provider: 'local',
|
|
186
|
+
prefix: 'testteam-reports/',
|
|
187
|
+
};
|
|
188
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
|
|
4
|
+
// ── Types ────────────────────────────────────────────────────────────
|
|
5
|
+
|
|
6
|
+
export interface RunRecord {
|
|
7
|
+
timestamp: number;
|
|
8
|
+
phase: string;
|
|
9
|
+
configName: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface RunCounterState {
|
|
13
|
+
periodStart: number;
|
|
14
|
+
periodEnd: number;
|
|
15
|
+
runs: RunRecord[];
|
|
16
|
+
totalRuns: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface RunLimitCheck {
|
|
20
|
+
allowed: boolean;
|
|
21
|
+
used: number;
|
|
22
|
+
remaining: number;
|
|
23
|
+
warning: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const RUN_COUNTER_FILENAME = '.testteam-runs.json';
|
|
27
|
+
|
|
28
|
+
// ── Period helpers ───────────────────────────────────────────────────
|
|
29
|
+
|
|
30
|
+
/** Return Unix timestamp (ms) for midnight UTC on the 1st of the given date's month. */
|
|
31
|
+
function periodStartFor(date: Date): number {
|
|
32
|
+
return Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Return Unix timestamp (ms) for 23:59:59.999 UTC on the last day of the given date's month. */
|
|
36
|
+
function periodEndFor(date: Date): number {
|
|
37
|
+
// Month + 1, day 0 gives last day of current month
|
|
38
|
+
const lastDay = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth() + 1, 0));
|
|
39
|
+
return Date.UTC(
|
|
40
|
+
lastDay.getUTCFullYear(),
|
|
41
|
+
lastDay.getUTCMonth(),
|
|
42
|
+
lastDay.getUTCDate(),
|
|
43
|
+
23, 59, 59, 999,
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function createFreshState(now: Date): RunCounterState {
|
|
48
|
+
return {
|
|
49
|
+
periodStart: periodStartFor(now),
|
|
50
|
+
periodEnd: periodEndFor(now),
|
|
51
|
+
runs: [],
|
|
52
|
+
totalRuns: 0,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ── Type guard ──────────────────────────────────────────────────────
|
|
57
|
+
|
|
58
|
+
function isRunCounterState(value: unknown): value is RunCounterState {
|
|
59
|
+
if (typeof value !== 'object' || value === null) return false;
|
|
60
|
+
const obj = value as Record<string, unknown>;
|
|
61
|
+
|
|
62
|
+
if (typeof obj['periodStart'] !== 'number') return false;
|
|
63
|
+
if (typeof obj['periodEnd'] !== 'number') return false;
|
|
64
|
+
if (typeof obj['totalRuns'] !== 'number') return false;
|
|
65
|
+
if (!Array.isArray(obj['runs'])) return false;
|
|
66
|
+
|
|
67
|
+
return (obj['runs'] as unknown[]).every((r) => {
|
|
68
|
+
if (typeof r !== 'object' || r === null) return false;
|
|
69
|
+
const rec = r as Record<string, unknown>;
|
|
70
|
+
return (
|
|
71
|
+
typeof rec['timestamp'] === 'number' &&
|
|
72
|
+
typeof rec['phase'] === 'string' &&
|
|
73
|
+
typeof rec['configName'] === 'string'
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// ── Public API ──────────────────────────────────────────────────────
|
|
79
|
+
|
|
80
|
+
/** Returns the absolute path to the run-counter file inside `configDir`. */
|
|
81
|
+
export function getRunCounterPath(configDir: string): string {
|
|
82
|
+
return path.join(configDir, RUN_COUNTER_FILENAME);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Load the run counter state from disk.
|
|
87
|
+
* If the file does not exist or the current billing period has expired,
|
|
88
|
+
* a fresh state for the current month is returned (and persisted).
|
|
89
|
+
*/
|
|
90
|
+
export function loadRunCounter(configDir: string): RunCounterState {
|
|
91
|
+
const filePath = getRunCounterPath(configDir);
|
|
92
|
+
const now = new Date();
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
if (!fs.existsSync(filePath)) {
|
|
96
|
+
const fresh = createFreshState(now);
|
|
97
|
+
saveRunCounter(configDir, fresh);
|
|
98
|
+
return fresh;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const raw: unknown = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
|
102
|
+
|
|
103
|
+
if (!isRunCounterState(raw)) {
|
|
104
|
+
const fresh = createFreshState(now);
|
|
105
|
+
saveRunCounter(configDir, fresh);
|
|
106
|
+
return fresh;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Auto-reset if past the billing period
|
|
110
|
+
if (now.getTime() > raw.periodEnd) {
|
|
111
|
+
const fresh = createFreshState(now);
|
|
112
|
+
saveRunCounter(configDir, fresh);
|
|
113
|
+
return fresh;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return raw;
|
|
117
|
+
} catch {
|
|
118
|
+
const fresh = createFreshState(now);
|
|
119
|
+
saveRunCounter(configDir, fresh);
|
|
120
|
+
return fresh;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Record a pipeline run in the current billing period. */
|
|
125
|
+
export function recordRun(
|
|
126
|
+
configDir: string,
|
|
127
|
+
phase: string,
|
|
128
|
+
configName: string,
|
|
129
|
+
): void {
|
|
130
|
+
const state = loadRunCounter(configDir);
|
|
131
|
+
|
|
132
|
+
const record: RunRecord = {
|
|
133
|
+
timestamp: Date.now(),
|
|
134
|
+
phase,
|
|
135
|
+
configName,
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
state.runs.push(record);
|
|
139
|
+
state.totalRuns = state.runs.length;
|
|
140
|
+
|
|
141
|
+
saveRunCounter(configDir, state);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Check whether a new run is allowed under the given monthly limit.
|
|
146
|
+
* `warning` is `true` when usage has reached 80 % or more of the limit.
|
|
147
|
+
*/
|
|
148
|
+
export function checkRunLimit(
|
|
149
|
+
configDir: string,
|
|
150
|
+
maxRuns: number,
|
|
151
|
+
): RunLimitCheck {
|
|
152
|
+
const state = loadRunCounter(configDir);
|
|
153
|
+
const used = state.totalRuns;
|
|
154
|
+
const remaining = Math.max(0, maxRuns - used);
|
|
155
|
+
const allowed = used < maxRuns;
|
|
156
|
+
const warning = used >= maxRuns * 0.8;
|
|
157
|
+
|
|
158
|
+
return { allowed, used, remaining, warning };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Manually reset the run counter (admin / support use). */
|
|
162
|
+
export function resetRunCounter(configDir: string): void {
|
|
163
|
+
const fresh = createFreshState(new Date());
|
|
164
|
+
saveRunCounter(configDir, fresh);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// ── Internal helpers ────────────────────────────────────────────────
|
|
168
|
+
|
|
169
|
+
function saveRunCounter(configDir: string, state: RunCounterState): void {
|
|
170
|
+
const filePath = getRunCounterPath(configDir);
|
|
171
|
+
fs.writeFileSync(filePath, JSON.stringify(state, null, 2), 'utf8');
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Exported for testing only
|
|
175
|
+
export { periodStartFor as _periodStartFor, periodEndFor as _periodEndFor };
|
package/core/types.ts
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
export type Phase = 'alpha' | 'beta' | 'uat' | 'release';
|
|
2
|
+
export type AgentStatus = 'pending' | 'running' | 'passed' | 'failed' | 'skipped';
|
|
3
|
+
export type FindingType = 'test-bug' | 'code-bug-cosmetic' | 'code-bug-logic' | 'code-bug-security' | 'infra-issue';
|
|
4
|
+
export type FindingSeverity = 'critical' | 'high' | 'medium' | 'low' | 'info';
|
|
5
|
+
|
|
6
|
+
export interface AgentResult {
|
|
7
|
+
agentId: number;
|
|
8
|
+
agentName: string;
|
|
9
|
+
phase: Phase;
|
|
10
|
+
status: AgentStatus;
|
|
11
|
+
startedAt: string;
|
|
12
|
+
completedAt: string;
|
|
13
|
+
durationMs: number;
|
|
14
|
+
findings: Finding[];
|
|
15
|
+
evidence: EvidenceItem[];
|
|
16
|
+
summary: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface Finding {
|
|
20
|
+
id: string;
|
|
21
|
+
type: FindingType;
|
|
22
|
+
severity: FindingSeverity;
|
|
23
|
+
agentId: number;
|
|
24
|
+
module: string;
|
|
25
|
+
description: string;
|
|
26
|
+
file?: string;
|
|
27
|
+
line?: number;
|
|
28
|
+
screenshot?: string;
|
|
29
|
+
consoleError?: string;
|
|
30
|
+
stackTrace?: string;
|
|
31
|
+
suggestedFix?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface EvidenceItem {
|
|
35
|
+
type: 'screenshot' | 'log' | 'report' | 'video' | 'snapshot';
|
|
36
|
+
path: string;
|
|
37
|
+
description: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface PhaseGateResult {
|
|
41
|
+
phase: Phase;
|
|
42
|
+
passed: boolean;
|
|
43
|
+
blockingResults: AgentResult[];
|
|
44
|
+
advisoryResults: AgentResult[];
|
|
45
|
+
failedBlockingAgents: number[];
|
|
46
|
+
timestamp: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface RunState {
|
|
50
|
+
runId: string;
|
|
51
|
+
startedAt: string;
|
|
52
|
+
currentPhase: Phase;
|
|
53
|
+
phaseAttempt: number;
|
|
54
|
+
agentResults: Map<number, AgentResult>;
|
|
55
|
+
gateResults: PhaseGateResult[];
|
|
56
|
+
totalCost: number;
|
|
57
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BaseAgent } from './base-agent';
|
|
2
|
+
import type { Finding } from '../core/types';
|
|
3
|
+
export declare class AnalystAgent extends BaseAgent {
|
|
4
|
+
readonly agentId = 1;
|
|
5
|
+
readonly agentName = "Analyst";
|
|
6
|
+
protected preFlight(): Promise<void>;
|
|
7
|
+
/** Auto-detect the default branch: try 'main', fall back to 'master', then 'HEAD'. */
|
|
8
|
+
private detectDefaultBranch;
|
|
9
|
+
protected execute(): Promise<Finding[]>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=01-analyst.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"01-analyst.d.ts","sourceRoot":"","sources":["../../agents/01-analyst.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAiB7C,qBAAa,YAAa,SAAQ,SAAS;IACzC,QAAQ,CAAC,OAAO,KAAK;IACrB,QAAQ,CAAC,SAAS,aAAa;cAEf,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;IAI1C,sFAAsF;YACxE,mBAAmB;cAYjB,OAAO,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;CAwD9C"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { promisify } from 'node:util';
|
|
3
|
+
import { writeFile } from 'node:fs/promises';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { BaseAgent } from './base-agent';
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
/** Shared-dependency file patterns whose changes elevate all modules to critical risk */
|
|
8
|
+
const SHARED_DEPENDENCY_PATTERNS = ['AuthContext', 'App.tsx', 'router'];
|
|
9
|
+
export class AnalystAgent extends BaseAgent {
|
|
10
|
+
agentId = 1;
|
|
11
|
+
agentName = 'Analyst';
|
|
12
|
+
async preFlight() {
|
|
13
|
+
await execFileAsync('git', ['--version'], { shell: true });
|
|
14
|
+
}
|
|
15
|
+
/** Auto-detect the default branch: try 'main', fall back to 'master', then 'HEAD'. */
|
|
16
|
+
async detectDefaultBranch() {
|
|
17
|
+
for (const candidate of ['main', 'master']) {
|
|
18
|
+
try {
|
|
19
|
+
await execFileAsync('git', ['rev-parse', '--verify', candidate], { shell: true });
|
|
20
|
+
return candidate;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// branch does not exist, try next
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return 'HEAD';
|
|
27
|
+
}
|
|
28
|
+
async execute() {
|
|
29
|
+
const defaultBranch = await this.detectDefaultBranch();
|
|
30
|
+
const { stdout } = await execFileAsync('git', ['diff', `${defaultBranch}...HEAD`, '--name-only'], { shell: true });
|
|
31
|
+
const changedFiles = stdout.split('\n').map(f => f.trim()).filter(Boolean);
|
|
32
|
+
this.addEvidence({
|
|
33
|
+
type: 'log',
|
|
34
|
+
path: join(this.runDir, 'changed-files.txt'),
|
|
35
|
+
description: `${changedFiles.length} files changed since ${defaultBranch}`,
|
|
36
|
+
});
|
|
37
|
+
const hasSharedDependencyChange = changedFiles.some(file => SHARED_DEPENDENCY_PATTERNS.some(pattern => file.includes(pattern)));
|
|
38
|
+
const moduleMap = new Map();
|
|
39
|
+
for (const module of this.config.modules) {
|
|
40
|
+
moduleMap.set(module.id, []);
|
|
41
|
+
}
|
|
42
|
+
for (const file of changedFiles) {
|
|
43
|
+
for (const module of this.config.modules) {
|
|
44
|
+
const routeKey = module.route.replace(/^\//, '');
|
|
45
|
+
if (routeKey && file.includes(routeKey)) {
|
|
46
|
+
moduleMap.get(module.id)?.push(file);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const testPlanModules = this.config.modules.map(module => {
|
|
51
|
+
const moduleChangedFiles = moduleMap.get(module.id) ?? [];
|
|
52
|
+
let risk;
|
|
53
|
+
if (hasSharedDependencyChange) {
|
|
54
|
+
risk = 'critical';
|
|
55
|
+
}
|
|
56
|
+
else if (moduleChangedFiles.length > 0) {
|
|
57
|
+
risk = 'high';
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
risk = 'low';
|
|
61
|
+
}
|
|
62
|
+
return { id: module.id, risk, changedFiles: moduleChangedFiles };
|
|
63
|
+
});
|
|
64
|
+
const testPlan = { modules: testPlanModules };
|
|
65
|
+
const testPlanPath = join(this.runDir, 'test-plan.json');
|
|
66
|
+
await writeFile(testPlanPath, JSON.stringify(testPlan, null, 2), 'utf-8');
|
|
67
|
+
this.addEvidence({
|
|
68
|
+
type: 'report',
|
|
69
|
+
path: testPlanPath,
|
|
70
|
+
description: 'Generated test plan with module risk scores',
|
|
71
|
+
});
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=01-analyst.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"01-analyst.js","sourceRoot":"","sources":["../../agents/01-analyst.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { BaseAgent } from './base-agent';
|
|
2
|
+
import type { Finding } from '../core/types';
|
|
3
|
+
export declare class SeedArchitectAgent extends BaseAgent {
|
|
4
|
+
readonly agentId = 2;
|
|
5
|
+
readonly agentName = "Seed Architect";
|
|
6
|
+
private get projectRoot();
|
|
7
|
+
private get seedFilePath();
|
|
8
|
+
protected preFlight(): Promise<void>;
|
|
9
|
+
protected execute(): Promise<Finding[]>;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=02-seed-architect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"02-seed-architect.d.ts","sourceRoot":"","sources":["../../agents/02-seed-architect.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAE7C,qBAAa,kBAAmB,SAAQ,SAAS;IAC/C,QAAQ,CAAC,OAAO,KAAK;IACrB,QAAQ,CAAC,SAAS,oBAAoB;IAEtC,OAAO,KAAK,WAAW,GAKtB;IAED,OAAO,KAAK,YAAY,GAEvB;cAEe,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;cAI1B,OAAO,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;CAiC9C"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { BaseAgent } from './base-agent';
|
|
4
|
+
import { validateSeedCompleteness } from '../helpers/seed-validator';
|
|
5
|
+
export class SeedArchitectAgent extends BaseAgent {
|
|
6
|
+
agentId = 2;
|
|
7
|
+
agentName = 'Seed Architect';
|
|
8
|
+
get projectRoot() {
|
|
9
|
+
const envConfig = this.config.environments['alpha'] ?? Object.values(this.config.environments)[0];
|
|
10
|
+
// Use projectRoot from environment config if available, otherwise fall back to cwd
|
|
11
|
+
const root = envConfig?.['projectRoot'];
|
|
12
|
+
return typeof root === 'string' ? root : process.cwd();
|
|
13
|
+
}
|
|
14
|
+
get seedFilePath() {
|
|
15
|
+
return join(this.projectRoot, 'prisma', 'seed.ts');
|
|
16
|
+
}
|
|
17
|
+
async preFlight() {
|
|
18
|
+
// Seed file is optional — missing file produces a warning finding instead of throwing
|
|
19
|
+
}
|
|
20
|
+
async execute() {
|
|
21
|
+
if (!existsSync(this.seedFilePath)) {
|
|
22
|
+
return [{
|
|
23
|
+
id: `${this.agentId}-seed-file-missing`,
|
|
24
|
+
type: 'infra-issue',
|
|
25
|
+
severity: 'medium',
|
|
26
|
+
agentId: this.agentId,
|
|
27
|
+
module: 'seed',
|
|
28
|
+
description: `Seed file not found at ${this.seedFilePath}. Seed validation skipped.`,
|
|
29
|
+
suggestedFix: `Create a seed file at prisma/seed.ts or set projectRoot in your environment config.`,
|
|
30
|
+
}];
|
|
31
|
+
}
|
|
32
|
+
const moduleIds = this.config.modules.map(m => m.id);
|
|
33
|
+
const result = await validateSeedCompleteness(this.seedFilePath, moduleIds);
|
|
34
|
+
this.addEvidence({
|
|
35
|
+
type: 'report',
|
|
36
|
+
path: this.seedFilePath,
|
|
37
|
+
description: `Seed validation: ${result.covered.length} covered, ${result.missing.length} missing`,
|
|
38
|
+
});
|
|
39
|
+
return result.missing.map((moduleId, index) => ({
|
|
40
|
+
id: `${this.agentId}-seed-missing-${index}`,
|
|
41
|
+
type: 'code-bug-logic',
|
|
42
|
+
severity: 'medium',
|
|
43
|
+
agentId: this.agentId,
|
|
44
|
+
module: moduleId,
|
|
45
|
+
description: `Module "${moduleId}" has no seed data in prisma/seed.ts`,
|
|
46
|
+
file: this.seedFilePath,
|
|
47
|
+
suggestedFix: `Add a data-creation block for module "${moduleId}" in prisma/seed.ts`,
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=02-seed-architect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"02-seed-architect.js","sourceRoot":"","sources":["../../agents/02-seed-architect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAGrE,MAAM,OAAO,kBAAmB,SAAQ,SAAS;IACtC,OAAO,GAAG,CAAC,CAAC;IACZ,SAAS,GAAG,gBAAgB,CAAC;IAEtC,IAAY,WAAW;QACrB,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,OAAO,CAAC,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;QAClG,mFAAmF;QACnF,MAAM,IAAI,GAAI,SAA4D,EAAE,CAAC,aAAa,CAAC,CAAC;QAC5F,OAAO,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;IACzD,CAAC;IAED,IAAY,YAAY;QACtB,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC;IACrD,CAAC;IAES,KAAK,CAAC,SAAS;QACvB,sFAAsF;IACxF,CAAC;IAES,KAAK,CAAC,OAAO;QACrB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;YACnC,OAAO,CAAC;oBACN,EAAE,EAAE,GAAG,IAAI,CAAC,OAAO,oBAAoB;oBACvC,IAAI,EAAE,aAAsB;oBAC5B,QAAQ,EAAE,QAAiB;oBAC3B,OAAO,EAAE,IAAI,CAAC,OAAO;oBACrB,MAAM,EAAE,MAAM;oBACd,WAAW,EAAE,0BAA0B,IAAI,CAAC,YAAY,4BAA4B;oBACpF,YAAY,EAAE,qFAAqF;iBACpG,CAAC,CAAC;QACL,CAAC;QAED,MAAM,SAAS,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACrD,MAAM,MAAM,GAAG,MAAM,wBAAwB,CAAC,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;QAE5E,IAAI,CAAC,WAAW,CAAC;YACf,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,IAAI,CAAC,YAAY;YACvB,WAAW,EAAE,oBAAoB,MAAM,CAAC,OAAO,CAAC,MAAM,aAAa,MAAM,CAAC,OAAO,CAAC,MAAM,UAAU;SACnG,CAAC,CAAC;QAEH,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC;YAC9C,EAAE,EAAE,GAAG,IAAI,CAAC,OAAO,iBAAiB,KAAK,EAAE;YAC3C,IAAI,EAAE,gBAAyB;YAC/B,QAAQ,EAAE,QAAiB;YAC3B,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,MAAM,EAAE,QAAQ;YAChB,WAAW,EAAE,WAAW,QAAQ,sCAAsC;YACtE,IAAI,EAAE,IAAI,CAAC,YAAY;YACvB,YAAY,EAAE,yCAAyC,QAAQ,qBAAqB;SACrF,CAAC,CAAC,CAAC;IACN,CAAC;CACF"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Finding } from '../core/types';
|
|
2
|
+
import { BaseAgent } from './base-agent';
|
|
3
|
+
export declare class TestGeneratorAgent extends BaseAgent {
|
|
4
|
+
readonly agentId = 3;
|
|
5
|
+
readonly agentName = "Test Generator";
|
|
6
|
+
protected preFlight(): Promise<void>;
|
|
7
|
+
protected execute(): Promise<Finding[]>;
|
|
8
|
+
}
|
|
9
|
+
//# sourceMappingURL=03-test-generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"03-test-generator.d.ts","sourceRoot":"","sources":["../../agents/03-test-generator.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAqDzC,qBAAa,kBAAmB,SAAQ,SAAS;IAC/C,QAAQ,CAAC,OAAO,KAAK;IACrB,QAAQ,CAAC,SAAS,oBAAoB;cAEtB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC;cAqB1B,OAAO,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;CA6G9C"}
|