@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.
Files changed (325) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/LICENSE +21 -0
  3. package/README.md +167 -0
  4. package/agents/01-analyst.ts +100 -0
  5. package/agents/02-seed-architect.ts +59 -0
  6. package/agents/03-test-generator.ts +191 -0
  7. package/agents/04-unit-runner.ts +160 -0
  8. package/agents/05-browser-crawler.ts +790 -0
  9. package/agents/06-api-exerciser.ts +311 -0
  10. package/agents/07-security-scout.ts +188 -0
  11. package/agents/08-a11y-guardian.ts +212 -0
  12. package/agents/09-healer.ts +228 -0
  13. package/agents/10-reporter.ts +266 -0
  14. package/agents/11-fixer.ts +253 -0
  15. package/agents/12-ux-inspector.ts +444 -0
  16. package/agents/13-performance-profiler.ts +271 -0
  17. package/agents/14-data-integrity-auditor.ts +417 -0
  18. package/agents/15-regression-sentinel.ts +307 -0
  19. package/agents/16-chaos-agent.ts +228 -0
  20. package/agents/17-documentation-validator.ts +266 -0
  21. package/agents/18-integration-watchdog.ts +178 -0
  22. package/agents/19-tenant-isolation-auditor.ts +199 -0
  23. package/agents/20-workflow-completion-tester.ts +203 -0
  24. package/agents/21-state-session-tester.ts +262 -0
  25. package/agents/22-email-notification-verifier.ts +244 -0
  26. package/agents/23-migration-tester.ts +80 -0
  27. package/agents/__tests__/01-analyst.test.ts +188 -0
  28. package/agents/__tests__/02-seed-architect.test.ts +152 -0
  29. package/agents/__tests__/03-test-generator-full.test.ts +321 -0
  30. package/agents/__tests__/03-test-generator.test.ts +318 -0
  31. package/agents/__tests__/04-unit-runner.test.ts +320 -0
  32. package/agents/__tests__/05-browser-crawler-beta.test.ts +492 -0
  33. package/agents/__tests__/05-browser-crawler-release.test.ts +412 -0
  34. package/agents/__tests__/05-browser-crawler-uat.test.ts +578 -0
  35. package/agents/__tests__/05-browser-crawler.test.ts +518 -0
  36. package/agents/__tests__/06-api-exerciser.test.ts +619 -0
  37. package/agents/__tests__/07-security-scout.test.ts +382 -0
  38. package/agents/__tests__/08-a11y-guardian.test.ts +530 -0
  39. package/agents/__tests__/09-healer.test.ts +384 -0
  40. package/agents/__tests__/10-reporter.test.ts +366 -0
  41. package/agents/__tests__/11-fixer.test.ts +406 -0
  42. package/agents/__tests__/12-ux-inspector-extended.test.ts +465 -0
  43. package/agents/__tests__/12-ux-inspector.test.ts +443 -0
  44. package/agents/__tests__/13-performance-profiler.test.ts +411 -0
  45. package/agents/__tests__/14-data-integrity-auditor-extended.test.ts +573 -0
  46. package/agents/__tests__/14-data-integrity-auditor.test.ts +407 -0
  47. package/agents/__tests__/15-regression-sentinel.test.ts +657 -0
  48. package/agents/__tests__/16-chaos-agent.test.ts +427 -0
  49. package/agents/__tests__/17-documentation-validator.test.ts +402 -0
  50. package/agents/__tests__/18-integration-watchdog.test.ts +263 -0
  51. package/agents/__tests__/19-tenant-isolation-auditor.test.ts +400 -0
  52. package/agents/__tests__/20-workflow-completion-tester.test.ts +586 -0
  53. package/agents/__tests__/21-state-session-tester.test.ts +374 -0
  54. package/agents/__tests__/22-email-notification-verifier.test.ts +441 -0
  55. package/agents/__tests__/23-migration-tester.test.ts +145 -0
  56. package/agents/__tests__/base-agent.test.ts +188 -0
  57. package/agents/__tests__/registry.test.ts +218 -0
  58. package/agents/base-agent.ts +77 -0
  59. package/agents/registry.ts +136 -0
  60. package/baselines/api-schemas/.gitkeep +0 -0
  61. package/baselines/performance/.gitkeep +0 -0
  62. package/baselines/screenshots/.gitkeep +0 -0
  63. package/bin/testteam.js +10 -0
  64. package/core/__tests__/ci-output.test.ts +430 -0
  65. package/core/__tests__/cli.test.ts +387 -0
  66. package/core/__tests__/config.test.ts +78 -0
  67. package/core/__tests__/cost-tracker.test.ts +158 -0
  68. package/core/__tests__/evidence.test.ts +265 -0
  69. package/core/__tests__/fix-loop.test.ts +210 -0
  70. package/core/__tests__/health-check.test.ts +44 -0
  71. package/core/__tests__/init.test.ts +609 -0
  72. package/core/__tests__/integration.test.ts +204 -0
  73. package/core/__tests__/license-gen.test.ts +227 -0
  74. package/core/__tests__/license.test.ts +326 -0
  75. package/core/__tests__/multi-browser.test.ts +278 -0
  76. package/core/__tests__/orchestrator.test.ts +519 -0
  77. package/core/__tests__/phase-gate.test.ts +43 -0
  78. package/core/__tests__/report-html.test.ts +398 -0
  79. package/core/__tests__/report-upload.test.ts +325 -0
  80. package/core/__tests__/run-counter.test.ts +234 -0
  81. package/core/ci-output.ts +240 -0
  82. package/core/cli.ts +232 -0
  83. package/core/config.ts +178 -0
  84. package/core/cost-tracker.ts +59 -0
  85. package/core/evidence.ts +132 -0
  86. package/core/fix-loop.ts +85 -0
  87. package/core/health-check.ts +54 -0
  88. package/core/init.ts +546 -0
  89. package/core/license-gen.ts +212 -0
  90. package/core/license.ts +211 -0
  91. package/core/messages.ts +67 -0
  92. package/core/multi-browser.ts +136 -0
  93. package/core/orchestrator.ts +354 -0
  94. package/core/phase-gate.ts +55 -0
  95. package/core/report-html.ts +657 -0
  96. package/core/report-upload.ts +188 -0
  97. package/core/run-counter.ts +175 -0
  98. package/core/types.ts +57 -0
  99. package/dist/agents/01-analyst.d.ts +11 -0
  100. package/dist/agents/01-analyst.d.ts.map +1 -0
  101. package/dist/agents/01-analyst.js +75 -0
  102. package/dist/agents/01-analyst.js.map +1 -0
  103. package/dist/agents/02-seed-architect.d.ts +11 -0
  104. package/dist/agents/02-seed-architect.d.ts.map +1 -0
  105. package/dist/agents/02-seed-architect.js +51 -0
  106. package/dist/agents/02-seed-architect.js.map +1 -0
  107. package/dist/agents/03-test-generator.d.ts +9 -0
  108. package/dist/agents/03-test-generator.d.ts.map +1 -0
  109. package/dist/agents/03-test-generator.js +167 -0
  110. package/dist/agents/03-test-generator.js.map +1 -0
  111. package/dist/agents/04-unit-runner.d.ts +9 -0
  112. package/dist/agents/04-unit-runner.d.ts.map +1 -0
  113. package/dist/agents/04-unit-runner.js +113 -0
  114. package/dist/agents/04-unit-runner.js.map +1 -0
  115. package/dist/agents/05-browser-crawler.d.ts +30 -0
  116. package/dist/agents/05-browser-crawler.d.ts.map +1 -0
  117. package/dist/agents/05-browser-crawler.js +685 -0
  118. package/dist/agents/05-browser-crawler.js.map +1 -0
  119. package/dist/agents/06-api-exerciser.d.ts +23 -0
  120. package/dist/agents/06-api-exerciser.d.ts.map +1 -0
  121. package/dist/agents/06-api-exerciser.js +253 -0
  122. package/dist/agents/06-api-exerciser.js.map +1 -0
  123. package/dist/agents/07-security-scout.d.ts +11 -0
  124. package/dist/agents/07-security-scout.d.ts.map +1 -0
  125. package/dist/agents/07-security-scout.js +142 -0
  126. package/dist/agents/07-security-scout.js.map +1 -0
  127. package/dist/agents/08-a11y-guardian.d.ts +13 -0
  128. package/dist/agents/08-a11y-guardian.d.ts.map +1 -0
  129. package/dist/agents/08-a11y-guardian.js +176 -0
  130. package/dist/agents/08-a11y-guardian.js.map +1 -0
  131. package/dist/agents/09-healer.d.ts +33 -0
  132. package/dist/agents/09-healer.d.ts.map +1 -0
  133. package/dist/agents/09-healer.js +167 -0
  134. package/dist/agents/09-healer.js.map +1 -0
  135. package/dist/agents/10-reporter.d.ts +26 -0
  136. package/dist/agents/10-reporter.d.ts.map +1 -0
  137. package/dist/agents/10-reporter.js +215 -0
  138. package/dist/agents/10-reporter.js.map +1 -0
  139. package/dist/agents/11-fixer.d.ts +26 -0
  140. package/dist/agents/11-fixer.d.ts.map +1 -0
  141. package/dist/agents/11-fixer.js +195 -0
  142. package/dist/agents/11-fixer.js.map +1 -0
  143. package/dist/agents/12-ux-inspector.d.ts +15 -0
  144. package/dist/agents/12-ux-inspector.d.ts.map +1 -0
  145. package/dist/agents/12-ux-inspector.js +364 -0
  146. package/dist/agents/12-ux-inspector.js.map +1 -0
  147. package/dist/agents/13-performance-profiler.d.ts +13 -0
  148. package/dist/agents/13-performance-profiler.d.ts.map +1 -0
  149. package/dist/agents/13-performance-profiler.js +216 -0
  150. package/dist/agents/13-performance-profiler.js.map +1 -0
  151. package/dist/agents/14-data-integrity-auditor.d.ts +12 -0
  152. package/dist/agents/14-data-integrity-auditor.d.ts.map +1 -0
  153. package/dist/agents/14-data-integrity-auditor.js +356 -0
  154. package/dist/agents/14-data-integrity-auditor.js.map +1 -0
  155. package/dist/agents/15-regression-sentinel.d.ts +25 -0
  156. package/dist/agents/15-regression-sentinel.d.ts.map +1 -0
  157. package/dist/agents/15-regression-sentinel.js +251 -0
  158. package/dist/agents/15-regression-sentinel.js.map +1 -0
  159. package/dist/agents/16-chaos-agent.d.ts +9 -0
  160. package/dist/agents/16-chaos-agent.d.ts.map +1 -0
  161. package/dist/agents/16-chaos-agent.js +207 -0
  162. package/dist/agents/16-chaos-agent.js.map +1 -0
  163. package/dist/agents/17-documentation-validator.d.ts +31 -0
  164. package/dist/agents/17-documentation-validator.d.ts.map +1 -0
  165. package/dist/agents/17-documentation-validator.js +246 -0
  166. package/dist/agents/17-documentation-validator.js.map +1 -0
  167. package/dist/agents/18-integration-watchdog.d.ts +10 -0
  168. package/dist/agents/18-integration-watchdog.d.ts.map +1 -0
  169. package/dist/agents/18-integration-watchdog.js +138 -0
  170. package/dist/agents/18-integration-watchdog.js.map +1 -0
  171. package/dist/agents/19-tenant-isolation-auditor.d.ts +9 -0
  172. package/dist/agents/19-tenant-isolation-auditor.d.ts.map +1 -0
  173. package/dist/agents/19-tenant-isolation-auditor.js +166 -0
  174. package/dist/agents/19-tenant-isolation-auditor.js.map +1 -0
  175. package/dist/agents/20-workflow-completion-tester.d.ts +12 -0
  176. package/dist/agents/20-workflow-completion-tester.d.ts.map +1 -0
  177. package/dist/agents/20-workflow-completion-tester.js +159 -0
  178. package/dist/agents/20-workflow-completion-tester.js.map +1 -0
  179. package/dist/agents/21-state-session-tester.d.ts +10 -0
  180. package/dist/agents/21-state-session-tester.d.ts.map +1 -0
  181. package/dist/agents/21-state-session-tester.js +233 -0
  182. package/dist/agents/21-state-session-tester.js.map +1 -0
  183. package/dist/agents/22-email-notification-verifier.d.ts +11 -0
  184. package/dist/agents/22-email-notification-verifier.d.ts.map +1 -0
  185. package/dist/agents/22-email-notification-verifier.js +199 -0
  186. package/dist/agents/22-email-notification-verifier.js.map +1 -0
  187. package/dist/agents/23-migration-tester.d.ts +10 -0
  188. package/dist/agents/23-migration-tester.d.ts.map +1 -0
  189. package/dist/agents/23-migration-tester.js +74 -0
  190. package/dist/agents/23-migration-tester.js.map +1 -0
  191. package/dist/agents/base-agent.d.ts +19 -0
  192. package/dist/agents/base-agent.d.ts.map +1 -0
  193. package/dist/agents/base-agent.js +67 -0
  194. package/dist/agents/base-agent.js.map +1 -0
  195. package/dist/agents/registry.d.ts +29 -0
  196. package/dist/agents/registry.d.ts.map +1 -0
  197. package/dist/agents/registry.js +117 -0
  198. package/dist/agents/registry.js.map +1 -0
  199. package/dist/core/ci-output.d.ts +35 -0
  200. package/dist/core/ci-output.d.ts.map +1 -0
  201. package/dist/core/ci-output.js +193 -0
  202. package/dist/core/ci-output.js.map +1 -0
  203. package/dist/core/cli.d.ts +11 -0
  204. package/dist/core/cli.d.ts.map +1 -0
  205. package/dist/core/cli.js +197 -0
  206. package/dist/core/cli.js.map +1 -0
  207. package/dist/core/config.d.ts +111 -0
  208. package/dist/core/config.d.ts.map +1 -0
  209. package/dist/core/config.js +42 -0
  210. package/dist/core/config.js.map +1 -0
  211. package/dist/core/cost-tracker.d.ts +22 -0
  212. package/dist/core/cost-tracker.d.ts.map +1 -0
  213. package/dist/core/cost-tracker.js +41 -0
  214. package/dist/core/cost-tracker.js.map +1 -0
  215. package/dist/core/evidence.d.ts +28 -0
  216. package/dist/core/evidence.d.ts.map +1 -0
  217. package/dist/core/evidence.js +95 -0
  218. package/dist/core/evidence.js.map +1 -0
  219. package/dist/core/fix-loop.d.ts +29 -0
  220. package/dist/core/fix-loop.d.ts.map +1 -0
  221. package/dist/core/fix-loop.js +70 -0
  222. package/dist/core/fix-loop.js.map +1 -0
  223. package/dist/core/health-check.d.ts +21 -0
  224. package/dist/core/health-check.d.ts.map +1 -0
  225. package/dist/core/health-check.js +26 -0
  226. package/dist/core/health-check.js.map +1 -0
  227. package/dist/core/init.d.ts +2 -0
  228. package/dist/core/init.d.ts.map +1 -0
  229. package/dist/core/init.js +435 -0
  230. package/dist/core/init.js.map +1 -0
  231. package/dist/core/license-gen.d.ts +12 -0
  232. package/dist/core/license-gen.d.ts.map +1 -0
  233. package/dist/core/license-gen.js +169 -0
  234. package/dist/core/license-gen.js.map +1 -0
  235. package/dist/core/license.d.ts +33 -0
  236. package/dist/core/license.d.ts.map +1 -0
  237. package/dist/core/license.js +170 -0
  238. package/dist/core/license.js.map +1 -0
  239. package/dist/core/messages.d.ts +10 -0
  240. package/dist/core/messages.d.ts.map +1 -0
  241. package/dist/core/messages.js +47 -0
  242. package/dist/core/messages.js.map +1 -0
  243. package/dist/core/multi-browser.d.ts +36 -0
  244. package/dist/core/multi-browser.d.ts.map +1 -0
  245. package/dist/core/multi-browser.js +88 -0
  246. package/dist/core/multi-browser.js.map +1 -0
  247. package/dist/core/orchestrator.d.ts +48 -0
  248. package/dist/core/orchestrator.d.ts.map +1 -0
  249. package/dist/core/orchestrator.js +291 -0
  250. package/dist/core/orchestrator.js.map +1 -0
  251. package/dist/core/phase-gate.d.ts +4 -0
  252. package/dist/core/phase-gate.d.ts.map +1 -0
  253. package/dist/core/phase-gate.js +39 -0
  254. package/dist/core/phase-gate.js.map +1 -0
  255. package/dist/core/report-html.d.ts +9 -0
  256. package/dist/core/report-html.d.ts.map +1 -0
  257. package/dist/core/report-html.js +617 -0
  258. package/dist/core/report-html.js.map +1 -0
  259. package/dist/core/report-upload.d.ts +16 -0
  260. package/dist/core/report-upload.d.ts.map +1 -0
  261. package/dist/core/report-upload.js +124 -0
  262. package/dist/core/report-upload.js.map +1 -0
  263. package/dist/core/run-counter.d.ts +40 -0
  264. package/dist/core/run-counter.d.ts.map +1 -0
  265. package/dist/core/run-counter.js +120 -0
  266. package/dist/core/run-counter.js.map +1 -0
  267. package/dist/core/types.d.ts +53 -0
  268. package/dist/core/types.d.ts.map +1 -0
  269. package/dist/core/types.js +2 -0
  270. package/dist/core/types.js.map +1 -0
  271. package/dist/helpers/api-client.d.ts +30 -0
  272. package/dist/helpers/api-client.d.ts.map +1 -0
  273. package/dist/helpers/api-client.js +77 -0
  274. package/dist/helpers/api-client.js.map +1 -0
  275. package/dist/helpers/element-discovery.d.ts +18 -0
  276. package/dist/helpers/element-discovery.d.ts.map +1 -0
  277. package/dist/helpers/element-discovery.js +82 -0
  278. package/dist/helpers/element-discovery.js.map +1 -0
  279. package/dist/helpers/env-resolver.d.ts +29 -0
  280. package/dist/helpers/env-resolver.d.ts.map +1 -0
  281. package/dist/helpers/env-resolver.js +51 -0
  282. package/dist/helpers/env-resolver.js.map +1 -0
  283. package/dist/helpers/form-filler.d.ts +13 -0
  284. package/dist/helpers/form-filler.d.ts.map +1 -0
  285. package/dist/helpers/form-filler.js +98 -0
  286. package/dist/helpers/form-filler.js.map +1 -0
  287. package/dist/helpers/modal-handler.d.ts +16 -0
  288. package/dist/helpers/modal-handler.d.ts.map +1 -0
  289. package/dist/helpers/modal-handler.js +95 -0
  290. package/dist/helpers/modal-handler.js.map +1 -0
  291. package/dist/helpers/navigation.d.ts +37 -0
  292. package/dist/helpers/navigation.d.ts.map +1 -0
  293. package/dist/helpers/navigation.js +83 -0
  294. package/dist/helpers/navigation.js.map +1 -0
  295. package/dist/helpers/quality-gate.d.ts +17 -0
  296. package/dist/helpers/quality-gate.d.ts.map +1 -0
  297. package/dist/helpers/quality-gate.js +144 -0
  298. package/dist/helpers/quality-gate.js.map +1 -0
  299. package/dist/helpers/screenshot.d.ts +24 -0
  300. package/dist/helpers/screenshot.d.ts.map +1 -0
  301. package/dist/helpers/screenshot.js +76 -0
  302. package/dist/helpers/screenshot.js.map +1 -0
  303. package/dist/helpers/seed-validator.d.ts +15 -0
  304. package/dist/helpers/seed-validator.d.ts.map +1 -0
  305. package/dist/helpers/seed-validator.js +53 -0
  306. package/dist/helpers/seed-validator.js.map +1 -0
  307. package/helpers/__tests__/api-client.test.ts +199 -0
  308. package/helpers/__tests__/element-discovery.test.ts +202 -0
  309. package/helpers/__tests__/form-filler-extended.test.ts +212 -0
  310. package/helpers/__tests__/form-filler.test.ts +99 -0
  311. package/helpers/__tests__/modal-handler.test.ts +152 -0
  312. package/helpers/__tests__/navigation.test.ts +214 -0
  313. package/helpers/__tests__/quality-gate.test.ts +117 -0
  314. package/helpers/__tests__/screenshot.test.ts +139 -0
  315. package/helpers/__tests__/seed-validator.test.ts +114 -0
  316. package/helpers/api-client.ts +111 -0
  317. package/helpers/element-discovery.ts +105 -0
  318. package/helpers/env-resolver.ts +69 -0
  319. package/helpers/form-filler.ts +126 -0
  320. package/helpers/modal-handler.ts +108 -0
  321. package/helpers/navigation.ts +100 -0
  322. package/helpers/quality-gate.ts +180 -0
  323. package/helpers/screenshot.ts +111 -0
  324. package/helpers/seed-validator.ts +70 -0
  325. package/package.json +88 -0
@@ -0,0 +1,321 @@
1
+ /**
2
+ * 03-test-generator-full.test.ts
3
+ *
4
+ * Tests for Task 42: Agent 3 quality gate integration.
5
+ * Covers:
6
+ * - validateCodeQuality called on generated test file content
7
+ * - `any` type annotation triggers a quality-violation finding
8
+ * - clean file passes quality gate (no violation findings)
9
+ */
10
+
11
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
12
+ import type { ValidatedConfig } from '../../core/config';
13
+ import type { Phase } from '../../core/types';
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Stub config
17
+ // ---------------------------------------------------------------------------
18
+
19
+ const stubConfig = {
20
+ schemaVersion: 1,
21
+ name: 'test',
22
+ auth: { loginUrl: 'http://localhost' },
23
+ modules: [{ id: 'mod', route: '/mod', sidebarIcon: true }],
24
+ environments: {},
25
+ portals: [],
26
+ breakpoints: [],
27
+ integrations: [],
28
+ workers: [],
29
+ workflows: [],
30
+ accuracy: { enabled: false, decimalPrecision: 2, currencySymbol: '$', timezone: 'UTC' },
31
+ tenancy: { enabled: false, isolationField: 'firmId', testFirms: [] },
32
+ costBudget: { maxPerRun: 100, maxPerAgent: 10, alertThreshold: 0.8, onExceeded: 'abort' as const },
33
+ agentClassification: { blocking: [], advisory: [] },
34
+ thresholds: {
35
+ unitCoverage: { lines: 80, functions: 80, branches: 80, statements: 80 },
36
+ lighthouse: { accessibility: 90, performance: 90, bestPractices: 90, seo: 90 },
37
+ apiResponseTime: 2000,
38
+ bundleSizeLimit: 500000,
39
+ maxConsoleErrors: 0,
40
+ },
41
+ disabledAgents: [],
42
+ disabledAgentSet: new Set<number>(),
43
+ } satisfies ValidatedConfig;
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // Mock node:fs/promises
47
+ // ---------------------------------------------------------------------------
48
+
49
+ const statMock = vi.fn();
50
+ const readdirMock = vi.fn();
51
+ const readFileMock = vi.fn();
52
+
53
+ vi.mock('node:fs/promises', async (importOriginal) => {
54
+ const actual = await importOriginal<typeof import('node:fs/promises')>();
55
+ return {
56
+ ...actual,
57
+ stat: (...args: unknown[]) => statMock(...args),
58
+ readdir: (...args: unknown[]) => readdirMock(...args),
59
+ readFile: (...args: unknown[]) => readFileMock(...args),
60
+ };
61
+ });
62
+
63
+ // ---------------------------------------------------------------------------
64
+ // Dynamic import AFTER mocks
65
+ // ---------------------------------------------------------------------------
66
+
67
+ const { TestGeneratorAgent } = await import('../03-test-generator');
68
+
69
+ function createAgent(phase: Phase = 'alpha'): InstanceType<typeof TestGeneratorAgent> {
70
+ return new TestGeneratorAgent(stubConfig, phase, '/tmp/test-run');
71
+ }
72
+
73
+ // ---------------------------------------------------------------------------
74
+ // Helpers
75
+ // ---------------------------------------------------------------------------
76
+
77
+ /** Minimal valid generated test file with no quality violations. */
78
+ const CLEAN_GENERATED_TEST = `
79
+ import { describe, it, expect } from 'vitest';
80
+
81
+ describe('SomeService', () => {
82
+ it('returns true', () => {
83
+ const result: boolean = true;
84
+ expect(result).toBe(true);
85
+ });
86
+ });
87
+ `.trim();
88
+
89
+ /** Generated test file that uses \`any\` type — should trigger a quality violation. */
90
+ const DIRTY_GENERATED_TEST_ANY = `
91
+ import { describe, it, expect } from 'vitest';
92
+
93
+ describe('SomeService', () => {
94
+ it('works with any typed value', () => {
95
+ const value: any = 'hello';
96
+ expect(value).toBeDefined();
97
+ });
98
+ });
99
+ `.trim();
100
+
101
+ // ---------------------------------------------------------------------------
102
+ // Tests
103
+ // ---------------------------------------------------------------------------
104
+
105
+ describe('TestGeneratorAgent — quality gate (Task 42)', () => {
106
+ beforeEach(() => {
107
+ statMock.mockReset();
108
+ readdirMock.mockReset();
109
+ readFileMock.mockReset();
110
+
111
+ // Default: no vitest config on disk, coverage dir throws, generated dir empty
112
+ statMock.mockRejectedValue(new Error('ENOENT'));
113
+ readdirMock.mockImplementation((dir: string) => {
114
+ if (dir === 'tests/generated') return Promise.resolve([]);
115
+ return Promise.reject(new Error('ENOENT: no such file or directory'));
116
+ });
117
+ });
118
+
119
+ // -------------------------------------------------------------------------
120
+ // Quality gate is invoked on generated test content
121
+ // -------------------------------------------------------------------------
122
+
123
+ describe('quality gate invocation on generated tests', () => {
124
+ it('calls quality gate on each file in tests/generated/', async () => {
125
+ readdirMock.mockImplementation((dir: string) => {
126
+ if (dir === 'tests/generated') return Promise.resolve(['some-service.test.ts']);
127
+ return Promise.reject(new Error('ENOENT: no such file or directory'));
128
+ });
129
+ readFileMock.mockResolvedValue(CLEAN_GENERATED_TEST);
130
+
131
+ const agent = createAgent();
132
+ const result = await agent.run();
133
+
134
+ // Quality gate passed → evidence log added, no quality-violation findings
135
+ const qualityFindings = result.findings.filter((f) => f.id.includes('quality-violation'));
136
+ expect(qualityFindings).toHaveLength(0);
137
+
138
+ const passedEvidence = result.evidence.filter(
139
+ (e) => e.type === 'log' && e.description.includes('Quality gate passed'),
140
+ );
141
+ expect(passedEvidence).toHaveLength(1);
142
+ });
143
+
144
+ it('processes multiple generated test files', async () => {
145
+ readdirMock.mockImplementation((dir: string) => {
146
+ if (dir === 'tests/generated') return Promise.resolve(['service-a.test.ts', 'service-b.test.ts']);
147
+ return Promise.reject(new Error('ENOENT: no such file or directory'));
148
+ });
149
+ readFileMock.mockResolvedValue(CLEAN_GENERATED_TEST);
150
+
151
+ const agent = createAgent();
152
+ const result = await agent.run();
153
+
154
+ const passedEvidence = result.evidence.filter(
155
+ (e) => e.type === 'log' && e.description.includes('Quality gate passed'),
156
+ );
157
+ expect(passedEvidence).toHaveLength(2);
158
+ });
159
+ });
160
+
161
+ // -------------------------------------------------------------------------
162
+ // `any` type triggers a quality violation finding
163
+ // -------------------------------------------------------------------------
164
+
165
+ describe('quality violation: any type', () => {
166
+ beforeEach(() => {
167
+ readdirMock.mockImplementation((dir: string) => {
168
+ if (dir === 'tests/generated') return Promise.resolve(['dirty-test.ts']);
169
+ return Promise.reject(new Error('ENOENT: no such file or directory'));
170
+ });
171
+ readFileMock.mockResolvedValue(DIRTY_GENERATED_TEST_ANY);
172
+ });
173
+
174
+ it('creates a quality-violation finding when generated test contains `any` type', async () => {
175
+ const agent = createAgent();
176
+ const result = await agent.run();
177
+
178
+ const qualityFindings = result.findings.filter((f) => f.id.includes('quality-violation'));
179
+ expect(qualityFindings.length).toBeGreaterThan(0);
180
+ });
181
+
182
+ it('quality violation finding has high severity (error-level rule)', async () => {
183
+ const agent = createAgent();
184
+ const result = await agent.run();
185
+
186
+ const anyViolation = result.findings.find(
187
+ (f) => f.id.includes('quality-violation') && f.description.includes('no-any'),
188
+ );
189
+ expect(anyViolation).toBeDefined();
190
+ expect(anyViolation!.severity).toBe('high');
191
+ });
192
+
193
+ it('quality violation finding mentions the rule name in the description', async () => {
194
+ const agent = createAgent();
195
+ const result = await agent.run();
196
+
197
+ const anyViolation = result.findings.find(
198
+ (f) => f.description.includes('no-any'),
199
+ );
200
+ expect(anyViolation).toBeDefined();
201
+ expect(anyViolation!.description).toContain('no-any');
202
+ });
203
+
204
+ it('quality violation finding has type code-bug-logic', async () => {
205
+ const agent = createAgent();
206
+ const result = await agent.run();
207
+
208
+ const qualityFinding = result.findings.find((f) => f.id.includes('quality-violation'));
209
+ expect(qualityFinding!.type).toBe('code-bug-logic');
210
+ });
211
+
212
+ it('quality violation finding references the generated test file', async () => {
213
+ const agent = createAgent();
214
+ const result = await agent.run();
215
+
216
+ const qualityFinding = result.findings.find((f) => f.id.includes('quality-violation'));
217
+ expect(qualityFinding!.file).toContain('dirty-test.ts');
218
+ });
219
+
220
+ it('does NOT add quality-gate-passed evidence when violations are found', async () => {
221
+ const agent = createAgent();
222
+ const result = await agent.run();
223
+
224
+ const passedEvidence = result.evidence.filter(
225
+ (e) => e.type === 'log' && e.description.includes('Quality gate passed'),
226
+ );
227
+ expect(passedEvidence).toHaveLength(0);
228
+ });
229
+ });
230
+
231
+ // -------------------------------------------------------------------------
232
+ // Clean file passes quality gate
233
+ // -------------------------------------------------------------------------
234
+
235
+ describe('clean generated file passes quality gate', () => {
236
+ beforeEach(() => {
237
+ readdirMock.mockImplementation((dir: string) => {
238
+ if (dir === 'tests/generated') return Promise.resolve(['clean-test.ts']);
239
+ return Promise.reject(new Error('ENOENT: no such file or directory'));
240
+ });
241
+ readFileMock.mockResolvedValue(CLEAN_GENERATED_TEST);
242
+ });
243
+
244
+ it('produces no quality-violation findings for a clean generated test file', async () => {
245
+ const agent = createAgent();
246
+ const result = await agent.run();
247
+
248
+ const qualityFindings = result.findings.filter((f) => f.id.includes('quality-violation'));
249
+ expect(qualityFindings).toHaveLength(0);
250
+ });
251
+
252
+ it('adds quality-gate-passed evidence for a clean generated test file', async () => {
253
+ const agent = createAgent();
254
+ const result = await agent.run();
255
+
256
+ const passedEvidence = result.evidence.find(
257
+ (e) => e.type === 'log' && e.description.includes('Quality gate passed'),
258
+ );
259
+ expect(passedEvidence).toBeDefined();
260
+ });
261
+
262
+ it('passed evidence references the file path', async () => {
263
+ const agent = createAgent();
264
+ const result = await agent.run();
265
+
266
+ const passedEvidence = result.evidence.find(
267
+ (e) => e.type === 'log' && e.description.includes('Quality gate passed'),
268
+ );
269
+ expect(passedEvidence!.description).toContain('clean-test.ts');
270
+ });
271
+ });
272
+
273
+ // -------------------------------------------------------------------------
274
+ // No generated test files — quality gate section produces no findings
275
+ // -------------------------------------------------------------------------
276
+
277
+ describe('when no generated tests exist', () => {
278
+ it('produces no quality-violation findings when tests/generated/ is empty', async () => {
279
+ // readdirMock already returns [] for tests/generated in beforeEach
280
+ const agent = createAgent();
281
+ const result = await agent.run();
282
+
283
+ const qualityFindings = result.findings.filter((f) => f.id.includes('quality-violation'));
284
+ expect(qualityFindings).toHaveLength(0);
285
+ });
286
+
287
+ it('produces no quality-gate-passed evidence when tests/generated/ is empty', async () => {
288
+ const agent = createAgent();
289
+ const result = await agent.run();
290
+
291
+ const passedEvidence = result.evidence.filter(
292
+ (e) => e.type === 'log' && e.description.includes('Quality gate passed'),
293
+ );
294
+ expect(passedEvidence).toHaveLength(0);
295
+ });
296
+ });
297
+
298
+ // -------------------------------------------------------------------------
299
+ // Only .ts/.tsx/.js files are scanned — non-test files ignored
300
+ // -------------------------------------------------------------------------
301
+
302
+ describe('file filtering in tests/generated/', () => {
303
+ it('ignores non-TypeScript/JavaScript files in tests/generated/', async () => {
304
+ readdirMock.mockImplementation((dir: string) => {
305
+ if (dir === 'tests/generated') return Promise.resolve(['readme.md', 'notes.txt', 'clean.ts']);
306
+ return Promise.reject(new Error('ENOENT: no such file or directory'));
307
+ });
308
+ // readFile only called for the .ts file
309
+ readFileMock.mockResolvedValue(CLEAN_GENERATED_TEST);
310
+
311
+ const agent = createAgent();
312
+ const result = await agent.run();
313
+
314
+ // Only clean.ts was scanned — 1 passed-evidence entry
315
+ const passedEvidence = result.evidence.filter(
316
+ (e) => e.type === 'log' && e.description.includes('Quality gate passed'),
317
+ );
318
+ expect(passedEvidence).toHaveLength(1);
319
+ });
320
+ });
321
+ });
@@ -0,0 +1,318 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import type { ValidatedConfig } from '../../core/config';
3
+ import type { Phase } from '../../core/types';
4
+
5
+ // ---------------------------------------------------------------------------
6
+ // Stub config
7
+ // ---------------------------------------------------------------------------
8
+
9
+ const stubConfig = {
10
+ schemaVersion: 1,
11
+ name: 'test',
12
+ auth: { loginUrl: 'http://localhost' },
13
+ modules: [{ id: 'mod', route: '/mod', sidebarIcon: true }],
14
+ environments: {},
15
+ portals: [],
16
+ breakpoints: [],
17
+ integrations: [],
18
+ workers: [],
19
+ workflows: [],
20
+ accuracy: { enabled: false, decimalPrecision: 2, currencySymbol: '$', timezone: 'UTC' },
21
+ tenancy: { enabled: false, isolationField: 'firmId', testFirms: [] },
22
+ costBudget: { maxPerRun: 100, maxPerAgent: 10, alertThreshold: 0.8, onExceeded: 'abort' as const },
23
+ agentClassification: { blocking: [], advisory: [] },
24
+ thresholds: {
25
+ unitCoverage: { lines: 80, functions: 80, branches: 80, statements: 80 },
26
+ lighthouse: { accessibility: 90, performance: 90, bestPractices: 90, seo: 90 },
27
+ apiResponseTime: 2000,
28
+ bundleSizeLimit: 500000,
29
+ maxConsoleErrors: 0,
30
+ },
31
+ disabledAgents: [],
32
+ disabledAgentSet: new Set<number>(),
33
+ } satisfies ValidatedConfig;
34
+
35
+ // ---------------------------------------------------------------------------
36
+ // Mock node:fs/promises
37
+ // ---------------------------------------------------------------------------
38
+
39
+ const statMock = vi.fn();
40
+ const readdirMock = vi.fn();
41
+ const readFileMock = vi.fn();
42
+
43
+ vi.mock('node:fs/promises', async (importOriginal) => {
44
+ const actual = await importOriginal<typeof import('node:fs/promises')>();
45
+ return {
46
+ ...actual,
47
+ stat: (...args: unknown[]) => statMock(...args),
48
+ readdir: (...args: unknown[]) => readdirMock(...args),
49
+ readFile: (...args: unknown[]) => readFileMock(...args),
50
+ };
51
+ });
52
+
53
+ // ---------------------------------------------------------------------------
54
+ // Coverage fixture helpers
55
+ // ---------------------------------------------------------------------------
56
+
57
+ interface FileCoverage {
58
+ lines: { pct: number };
59
+ functions: { pct: number };
60
+ branches: { pct: number };
61
+ statements: { pct: number };
62
+ }
63
+
64
+ type CoverageSummaryFixture = Record<string, FileCoverage>;
65
+
66
+ function makeCoverageSummary(
67
+ files: Array<{ path: string; lines: number; functions: number; branches: number; statements: number }>,
68
+ ): CoverageSummaryFixture {
69
+ const summary: CoverageSummaryFixture = {
70
+ total: {
71
+ lines: { pct: 100 },
72
+ functions: { pct: 100 },
73
+ branches: { pct: 100 },
74
+ statements: { pct: 100 },
75
+ },
76
+ };
77
+ for (const f of files) {
78
+ summary[f.path] = {
79
+ lines: { pct: f.lines },
80
+ functions: { pct: f.functions },
81
+ branches: { pct: f.branches },
82
+ statements: { pct: f.statements },
83
+ };
84
+ }
85
+ return summary;
86
+ }
87
+
88
+ // ---------------------------------------------------------------------------
89
+ // Dynamic import AFTER mocks
90
+ // ---------------------------------------------------------------------------
91
+
92
+ const { TestGeneratorAgent } = await import('../03-test-generator');
93
+
94
+ function createAgent(phase: Phase = 'alpha'): InstanceType<typeof TestGeneratorAgent> {
95
+ return new TestGeneratorAgent(stubConfig, phase, '/tmp/test-run');
96
+ }
97
+
98
+ // ---------------------------------------------------------------------------
99
+ // Tests
100
+ // ---------------------------------------------------------------------------
101
+
102
+ describe('TestGeneratorAgent', () => {
103
+ beforeEach(() => {
104
+ statMock.mockReset();
105
+ readdirMock.mockReset();
106
+ readFileMock.mockReset();
107
+
108
+ // Default: no vitest config exists, no generated tests dir
109
+ statMock.mockRejectedValue(new Error('ENOENT'));
110
+ // Default: tests/generated/ is empty
111
+ readdirMock.mockImplementation((dir: string) => {
112
+ if (dir === 'tests/generated') return Promise.resolve([]);
113
+ return Promise.reject(new Error('ENOENT: no such file or directory'));
114
+ });
115
+ });
116
+
117
+ it('has agentId 3 and agentName "Test Generator"', () => {
118
+ const agent = createAgent();
119
+ expect(agent.agentId).toBe(3);
120
+ expect(agent.agentName).toBe('Test Generator');
121
+ });
122
+
123
+ // -------------------------------------------------------------------------
124
+ // No coverage data
125
+ // -------------------------------------------------------------------------
126
+
127
+ describe('when no coverage JSON files exist', () => {
128
+ beforeEach(() => {
129
+ readdirMock.mockImplementation(() => {
130
+ return Promise.reject(new Error('ENOENT: no such file or directory'));
131
+ });
132
+ });
133
+
134
+ it('creates a single advisory medium-severity finding', async () => {
135
+ const agent = createAgent();
136
+ const result = await agent.run();
137
+
138
+ expect(result.findings).toHaveLength(1);
139
+ expect(result.findings[0].severity).toBe('medium');
140
+ expect(result.findings[0].type).toBe('code-bug-logic');
141
+ });
142
+
143
+ it('the finding description mentions coverage analysis not available', async () => {
144
+ const agent = createAgent();
145
+ const result = await agent.run();
146
+
147
+ expect(result.findings[0].description).toContain('Coverage analysis not available');
148
+ });
149
+
150
+ it('returns passed status (medium finding does not block)', async () => {
151
+ const agent = createAgent();
152
+ const result = await agent.run();
153
+
154
+ expect(result.status).toBe('passed');
155
+ });
156
+ });
157
+
158
+ describe('when coverage directory exists but contains no JSON files', () => {
159
+ beforeEach(() => {
160
+ readdirMock.mockResolvedValue(['lcov.info', 'index.html']);
161
+ });
162
+
163
+ it('creates an advisory finding noting coverage not available', async () => {
164
+ const agent = createAgent();
165
+ const result = await agent.run();
166
+
167
+ expect(result.findings).toHaveLength(1);
168
+ expect(result.findings[0].description).toContain('Coverage analysis not available');
169
+ });
170
+ });
171
+
172
+ // -------------------------------------------------------------------------
173
+ // Coverage data present — all above threshold
174
+ // -------------------------------------------------------------------------
175
+
176
+ describe('when coverage data shows all files above threshold', () => {
177
+ beforeEach(() => {
178
+ readdirMock.mockResolvedValue(['coverage-summary.json']);
179
+ const summary = makeCoverageSummary([
180
+ { path: 'src/utils/format.ts', lines: 95, functions: 90, branches: 85, statements: 92 },
181
+ { path: 'src/services/billing.ts', lines: 88, functions: 82, branches: 80, statements: 87 },
182
+ ]);
183
+ readFileMock.mockResolvedValue(JSON.stringify(summary));
184
+ });
185
+
186
+ it('returns no coverage-gap findings', async () => {
187
+ const agent = createAgent();
188
+ const result = await agent.run();
189
+
190
+ const coverageFindings = result.findings.filter((f) => f.id.startsWith('3-coverage-gap'));
191
+ expect(coverageFindings).toHaveLength(0);
192
+ });
193
+
194
+ it('returns passed status when all files meet thresholds', async () => {
195
+ const agent = createAgent();
196
+ const result = await agent.run();
197
+
198
+ expect(result.status).toBe('passed');
199
+ });
200
+
201
+ it('adds report evidence for the coverage file', async () => {
202
+ const agent = createAgent();
203
+ const result = await agent.run();
204
+
205
+ expect(result.evidence.some((e) => e.type === 'report')).toBe(true);
206
+ });
207
+ });
208
+
209
+ // -------------------------------------------------------------------------
210
+ // Coverage data present — files below threshold
211
+ // -------------------------------------------------------------------------
212
+
213
+ describe('when coverage data shows files below threshold', () => {
214
+ beforeEach(() => {
215
+ readdirMock.mockResolvedValue(['coverage-summary.json']);
216
+ const summary = makeCoverageSummary([
217
+ // This file has all metrics below 80%
218
+ { path: 'src/services/payroll.ts', lines: 60, functions: 55, branches: 50, statements: 58 },
219
+ // This file is above threshold
220
+ { path: 'src/utils/helpers.ts', lines: 95, functions: 90, branches: 85, statements: 92 },
221
+ ]);
222
+ readFileMock.mockResolvedValue(JSON.stringify(summary));
223
+ });
224
+
225
+ it('creates a finding for the file below threshold', async () => {
226
+ const agent = createAgent();
227
+ const result = await agent.run();
228
+
229
+ const gapFindings = result.findings.filter((f) => f.id.startsWith('3-coverage-gap'));
230
+ expect(gapFindings).toHaveLength(1);
231
+ });
232
+
233
+ it('the finding has medium severity', async () => {
234
+ const agent = createAgent();
235
+ const result = await agent.run();
236
+
237
+ const gapFinding = result.findings.find((f) => f.id.startsWith('3-coverage-gap'));
238
+ expect(gapFinding!.severity).toBe('medium');
239
+ });
240
+
241
+ it('the finding has type code-bug-logic', async () => {
242
+ const agent = createAgent();
243
+ const result = await agent.run();
244
+
245
+ const gapFinding = result.findings.find((f) => f.id.startsWith('3-coverage-gap'));
246
+ expect(gapFinding!.type).toBe('code-bug-logic');
247
+ });
248
+
249
+ it('the finding description mentions the specific thresholds not met', async () => {
250
+ const agent = createAgent();
251
+ const result = await agent.run();
252
+
253
+ const gapFinding = result.findings.find((f) => f.id.startsWith('3-coverage-gap'));
254
+ expect(gapFinding!.description).toContain('lines');
255
+ });
256
+
257
+ it('the finding file property points to the uncovered file', async () => {
258
+ const agent = createAgent();
259
+ const result = await agent.run();
260
+
261
+ const gapFinding = result.findings.find((f) => f.id.startsWith('3-coverage-gap'));
262
+ expect(gapFinding!.file).toBe('src/services/payroll.ts');
263
+ });
264
+
265
+ it('does not create a finding for the file above threshold', async () => {
266
+ const agent = createAgent();
267
+ const result = await agent.run();
268
+
269
+ const gapFindings = result.findings.filter((f) => f.id.startsWith('3-coverage-gap'));
270
+ const helpersFinding = gapFindings.find((f) => f.file === 'src/utils/helpers.ts');
271
+ expect(helpersFinding).toBeUndefined();
272
+ });
273
+ });
274
+
275
+ // -------------------------------------------------------------------------
276
+ // Multiple files below threshold
277
+ // -------------------------------------------------------------------------
278
+
279
+ describe('when multiple files are below threshold', () => {
280
+ beforeEach(() => {
281
+ readdirMock.mockResolvedValue(['coverage-summary.json']);
282
+ const summary = makeCoverageSummary([
283
+ { path: 'src/a.ts', lines: 50, functions: 50, branches: 50, statements: 50 },
284
+ { path: 'src/b.ts', lines: 40, functions: 40, branches: 40, statements: 40 },
285
+ { path: 'src/c.ts', lines: 30, functions: 30, branches: 30, statements: 30 },
286
+ ]);
287
+ readFileMock.mockResolvedValue(JSON.stringify(summary));
288
+ });
289
+
290
+ it('creates one finding per uncovered file', async () => {
291
+ const agent = createAgent();
292
+ const result = await agent.run();
293
+
294
+ const gapFindings = result.findings.filter((f) => f.id.startsWith('3-coverage-gap'));
295
+ expect(gapFindings).toHaveLength(3);
296
+ });
297
+ });
298
+
299
+ // -------------------------------------------------------------------------
300
+ // Malformed JSON
301
+ // -------------------------------------------------------------------------
302
+
303
+ describe('when coverage JSON is malformed', () => {
304
+ beforeEach(() => {
305
+ readdirMock.mockResolvedValue(['coverage-summary.json']);
306
+ readFileMock.mockResolvedValue('not valid json {{{');
307
+ });
308
+
309
+ it('returns no findings (skips malformed file gracefully)', async () => {
310
+ const agent = createAgent();
311
+ const result = await agent.run();
312
+
313
+ // Malformed file produces no findings — this is silent skip behaviour
314
+ const gapFindings = result.findings.filter((f) => f.id.startsWith('3-coverage-gap'));
315
+ expect(gapFindings).toHaveLength(0);
316
+ });
317
+ });
318
+ });