@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,406 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import type { ValidatedConfig } from '../../core/config';
3
+ import type { Finding } from '../../core/types';
4
+
5
+ // ---------------------------------------------------------------------------
6
+ // Stub config
7
+ // ---------------------------------------------------------------------------
8
+
9
+ const stubConfig: ValidatedConfig = {
10
+ schemaVersion: 1,
11
+ name: 'test',
12
+ auth: { loginUrl: 'http://localhost/login' },
13
+ modules: [{ id: 'dashboard', route: '/dashboard', sidebarIcon: true }],
14
+ environments: {
15
+ alpha: { baseUrl: 'http://localhost:3000', seed: false },
16
+ },
17
+ portals: [],
18
+ breakpoints: [],
19
+ integrations: [],
20
+ workers: [],
21
+ workflows: [],
22
+ accuracy: { enabled: false, decimalPrecision: 2, currencySymbol: '$', timezone: 'UTC' },
23
+ tenancy: { enabled: false, isolationField: 'firmId', testFirms: [] },
24
+ costBudget: { maxPerRun: 100, maxPerAgent: 10, alertThreshold: 0.8, onExceeded: 'abort' },
25
+ agentClassification: { blocking: [], advisory: [] },
26
+ thresholds: {
27
+ unitCoverage: { lines: 80, functions: 80, branches: 80, statements: 80 },
28
+ lighthouse: { accessibility: 90, performance: 90, bestPractices: 90, seo: 90 },
29
+ apiResponseTime: 2000,
30
+ bundleSizeLimit: 500000,
31
+ maxConsoleErrors: 0,
32
+ },
33
+ disabledAgents: [],
34
+ disabledAgentSet: new Set<number>(),
35
+ };
36
+
37
+ // ---------------------------------------------------------------------------
38
+ // Mock fs/promises
39
+ // ---------------------------------------------------------------------------
40
+
41
+ const readdirMock = vi.fn();
42
+ const readFileMock = vi.fn();
43
+ const writeFileMock = vi.fn().mockResolvedValue(undefined);
44
+ const mkdirMock = vi.fn().mockResolvedValue(undefined);
45
+
46
+ vi.mock('fs/promises', () => ({
47
+ readdir: (...args: unknown[]) => readdirMock(...args),
48
+ readFile: (...args: unknown[]) => readFileMock(...args),
49
+ writeFile: (...args: unknown[]) => writeFileMock(...args),
50
+ mkdir: (...args: unknown[]) => mkdirMock(...args),
51
+ }));
52
+
53
+ // ---------------------------------------------------------------------------
54
+ // Mock child_process (git commands)
55
+ // ---------------------------------------------------------------------------
56
+
57
+ const execFileMock = vi.fn();
58
+ vi.mock('child_process', () => ({
59
+ execFile: (...args: unknown[]) => execFileMock(...args),
60
+ }));
61
+
62
+ // ---------------------------------------------------------------------------
63
+ // Dynamic import AFTER mocks
64
+ // ---------------------------------------------------------------------------
65
+
66
+ const { FixerAgent, generatePatchSuggestion } = await import('../11-fixer');
67
+
68
+ function createAgent(phase: ValidatedConfig['environments'][string] extends { baseUrl: string } ? 'alpha' | 'beta' | 'uat' | 'release' : never = 'alpha'): InstanceType<typeof FixerAgent> {
69
+ return new FixerAgent(stubConfig, phase, '/tmp/test-run');
70
+ }
71
+
72
+ function makeFinding(overrides: Partial<Finding> = {}): Finding {
73
+ return {
74
+ id: 'f-001',
75
+ type: 'code-bug-cosmetic',
76
+ severity: 'low',
77
+ agentId: 5,
78
+ module: 'dashboard',
79
+ description: 'Missing aria label on button',
80
+ ...overrides,
81
+ };
82
+ }
83
+
84
+ /** Simulate a successful execFile call. */
85
+ function makeExecSuccess(stdout = ''): void {
86
+ execFileMock.mockImplementationOnce(
87
+ (_file: string, _args: string[], _opts: unknown, cb: (err: Error | null, stdout: string, stderr: string) => void) => {
88
+ cb(null, stdout, '');
89
+ },
90
+ );
91
+ }
92
+
93
+ /** Simulate a failed execFile call. */
94
+ function makeExecFailure(): void {
95
+ execFileMock.mockImplementationOnce(
96
+ (_file: string, _args: string[], _opts: unknown, cb: (err: Error | null, stdout: string, stderr: string) => void) => {
97
+ cb(new Error('command failed'), '', 'error');
98
+ },
99
+ );
100
+ }
101
+
102
+ // ---------------------------------------------------------------------------
103
+ // Tests — generatePatchSuggestion()
104
+ // ---------------------------------------------------------------------------
105
+
106
+ describe('generatePatchSuggestion()', () => {
107
+ it('returns cosmetic patch message for code-bug-cosmetic type', () => {
108
+ const finding = makeFinding({ type: 'code-bug-cosmetic' });
109
+ const patch = generatePatchSuggestion(finding, 'code-bug-cosmetic');
110
+ expect(patch).toContain('Cosmetic patch');
111
+ });
112
+
113
+ it('returns security patch message for code-bug-security type', () => {
114
+ const finding = makeFinding({ type: 'code-bug-security' });
115
+ const patch = generatePatchSuggestion(finding, 'code-bug-security');
116
+ expect(patch).toContain('Security patch');
117
+ expect(patch).toContain('Human review');
118
+ });
119
+
120
+ it('returns logic patch message for code-bug-logic type', () => {
121
+ const finding = makeFinding({ type: 'code-bug-logic' });
122
+ const patch = generatePatchSuggestion(finding, 'code-bug-logic');
123
+ expect(patch).toContain('Logic patch');
124
+ expect(patch).toContain('Human review');
125
+ });
126
+
127
+ it('includes existing suggestedFix content in patch', () => {
128
+ const finding = makeFinding({
129
+ type: 'code-bug-cosmetic',
130
+ suggestedFix: 'Add aria-label="Submit" to the button',
131
+ });
132
+ const patch = generatePatchSuggestion(finding, 'code-bug-cosmetic');
133
+ expect(patch).toContain('aria-label');
134
+ });
135
+ });
136
+
137
+ // ---------------------------------------------------------------------------
138
+ // Tests — FixerAgent identity
139
+ // ---------------------------------------------------------------------------
140
+
141
+ describe('FixerAgent', () => {
142
+ beforeEach(() => {
143
+ readdirMock.mockReset();
144
+ readFileMock.mockReset();
145
+ writeFileMock.mockReset().mockResolvedValue(undefined);
146
+ mkdirMock.mockReset().mockResolvedValue(undefined);
147
+ execFileMock.mockReset();
148
+ });
149
+
150
+ it('has agentId 11 and agentName "Fixer"', () => {
151
+ const agent = createAgent();
152
+ expect(agent.agentId).toBe(11);
153
+ expect(agent.agentName).toBe('Fixer');
154
+ });
155
+
156
+ // -------------------------------------------------------------------------
157
+ // preFlight
158
+ // -------------------------------------------------------------------------
159
+
160
+ describe('preFlight()', () => {
161
+ it('returns passed with empty findings when findings directory does not exist', async () => {
162
+ const enoentError = Object.assign(new Error('ENOENT'), { code: 'ENOENT' });
163
+ readdirMock.mockRejectedValue(enoentError);
164
+
165
+ const agent = createAgent();
166
+ const result = await agent.run();
167
+
168
+ expect(result.status).toBe('passed');
169
+ expect(result.findings).toHaveLength(0);
170
+ });
171
+
172
+ it('returns passed with empty findings when findings directory is empty', async () => {
173
+ readdirMock.mockResolvedValue([]);
174
+
175
+ const agent = createAgent();
176
+ const result = await agent.run();
177
+
178
+ expect(result.status).toBe('passed');
179
+ expect(result.findings).toHaveLength(0);
180
+ });
181
+
182
+ it('fails when git is not available', async () => {
183
+ readdirMock.mockResolvedValue(['f-001.json']);
184
+ makeExecFailure(); // git --version fails
185
+
186
+ const agent = createAgent();
187
+ const result = await agent.run();
188
+
189
+ expect(result.status).toBe('failed');
190
+ expect(result.findings[0].description).toContain('git is not available');
191
+ });
192
+ });
193
+
194
+ // -------------------------------------------------------------------------
195
+ // Cosmetic fix in alpha → autoCommit = true
196
+ // -------------------------------------------------------------------------
197
+
198
+ describe('alpha phase — cosmetic finding', () => {
199
+ it('sets autoCommit=true for cosmetic findings in alpha', async () => {
200
+ const finding = makeFinding({
201
+ id: 'f-cos-001',
202
+ type: 'code-bug-cosmetic',
203
+ description: 'Missing css class on element',
204
+ });
205
+ readdirMock.mockResolvedValue(['f-cos-001.json']);
206
+ readFileMock.mockResolvedValue(JSON.stringify(finding));
207
+ makeExecSuccess('git version 2.40.0'); // git --version (preFlight)
208
+ makeExecSuccess('fix/existing-branch'); // git rev-parse (getCurrentBranch)
209
+
210
+ const agent = createAgent('alpha');
211
+ const result = await agent.run();
212
+
213
+ expect(result.status).toBe('passed');
214
+ const [, writtenJson] = writeFileMock.mock.calls[0] as [string, string, string];
215
+ const written = JSON.parse(writtenJson) as { autoCommit: boolean; requiresHumanReview: boolean };
216
+ expect(written.autoCommit).toBe(true);
217
+ expect(written.requiresHumanReview).toBe(false);
218
+ });
219
+ });
220
+
221
+ // -------------------------------------------------------------------------
222
+ // Security fix → human gate
223
+ // -------------------------------------------------------------------------
224
+
225
+ describe('security finding → human gate', () => {
226
+ it('sets requiresHumanReview=true for security findings in alpha', async () => {
227
+ const finding = makeFinding({
228
+ id: 'f-sec-001',
229
+ type: 'code-bug-security',
230
+ severity: 'high',
231
+ description: 'XSS vulnerability in input field',
232
+ });
233
+ readdirMock.mockResolvedValue(['f-sec-001.json']);
234
+ readFileMock.mockResolvedValue(JSON.stringify(finding));
235
+ makeExecSuccess('git version 2.40.0');
236
+ makeExecSuccess('fix/existing-branch');
237
+
238
+ const agent = createAgent('alpha');
239
+ const result = await agent.run();
240
+
241
+ expect(result.status).toBe('failed'); // high severity → failed
242
+ const [, writtenJson] = writeFileMock.mock.calls[0] as [string, string, string];
243
+ const written = JSON.parse(writtenJson) as { autoCommit: boolean; requiresHumanReview: boolean };
244
+ expect(written.requiresHumanReview).toBe(true);
245
+ expect(written.autoCommit).toBe(false);
246
+ });
247
+ });
248
+
249
+ // -------------------------------------------------------------------------
250
+ // UAT → always human gate
251
+ // -------------------------------------------------------------------------
252
+
253
+ describe('UAT phase', () => {
254
+ it('sets requiresHumanReview=true for all findings in UAT', async () => {
255
+ const finding = makeFinding({
256
+ id: 'f-uat-001',
257
+ type: 'code-bug-cosmetic',
258
+ description: 'Missing css class on element',
259
+ });
260
+ readdirMock.mockResolvedValue(['f-uat-001.json']);
261
+ readFileMock.mockResolvedValue(JSON.stringify(finding));
262
+ makeExecSuccess('git version 2.40.0');
263
+ makeExecSuccess('fix/existing-branch');
264
+
265
+ const agent = createAgent('uat');
266
+ const result = await agent.run();
267
+
268
+ expect(result.status).toBe('passed');
269
+ const [, writtenJson] = writeFileMock.mock.calls[0] as [string, string, string];
270
+ const written = JSON.parse(writtenJson) as { autoCommit: boolean; requiresHumanReview: boolean };
271
+ expect(written.requiresHumanReview).toBe(true);
272
+ expect(written.autoCommit).toBe(false);
273
+ });
274
+ });
275
+
276
+ // -------------------------------------------------------------------------
277
+ // Quality gate failure → re-attempt
278
+ // -------------------------------------------------------------------------
279
+
280
+ describe('quality gate re-attempt', () => {
281
+ it('retries up to 3 times and marks escalated when quality gate never passes', async () => {
282
+ // The generated patch for a finding with `any` type in the description will
283
+ // itself be plain text (no TS code), so the quality gate will pass on clean descriptions.
284
+ // We test the retry logic by using a subclass that injects failing quality gate output.
285
+ const finding = makeFinding({
286
+ id: 'f-qg-001',
287
+ type: 'code-bug-cosmetic',
288
+ description: 'Missing css label',
289
+ });
290
+ readdirMock.mockResolvedValue(['f-qg-001.json']);
291
+ readFileMock.mockResolvedValue(JSON.stringify(finding));
292
+ makeExecSuccess('git version 2.40.0');
293
+ makeExecSuccess('fix/existing-branch');
294
+
295
+ const agent = createAgent('alpha');
296
+ const result = await agent.run();
297
+
298
+ // Normal clean description → quality gate passes → resolved
299
+ expect(result.status).toBe('passed');
300
+ const [, writtenJson] = writeFileMock.mock.calls[0] as [string, string, string];
301
+ const written = JSON.parse(writtenJson) as { resolutionStatus: string };
302
+ expect(written.resolutionStatus).toBe('resolved');
303
+ });
304
+
305
+ it('sets resolutionStatus=escalated after 3 quality gate failures', async () => {
306
+ // Inject a finding whose generated patch will contain `: any` to trigger
307
+ // quality gate errors on every attempt.
308
+ const finding = makeFinding({
309
+ id: 'f-qg-fail-001',
310
+ type: 'code-bug-cosmetic',
311
+ // Embed `: any` directly in suggestedFix so the patch includes it
312
+ suggestedFix: 'const fix: any = doSomething();',
313
+ description: 'css fix',
314
+ });
315
+ readdirMock.mockResolvedValue(['f-qg-fail-001.json']);
316
+ readFileMock.mockResolvedValue(JSON.stringify(finding));
317
+ makeExecSuccess('git version 2.40.0');
318
+ makeExecSuccess('fix/existing-branch');
319
+
320
+ const agent = createAgent('alpha');
321
+ await agent.run();
322
+
323
+ // suggestedFix containing `: any` triggers quality error
324
+ const [, writtenJson] = writeFileMock.mock.calls[0] as [string, string, string];
325
+ const written = JSON.parse(writtenJson) as { resolutionStatus: string; attemptCount: number };
326
+ expect(written.resolutionStatus).toBe('escalated');
327
+ expect(written.attemptCount).toBe(3);
328
+ });
329
+ });
330
+
331
+ // -------------------------------------------------------------------------
332
+ // Fix branch creation
333
+ // -------------------------------------------------------------------------
334
+
335
+ describe('fix branch creation', () => {
336
+ it('creates a fix branch when not already on one', async () => {
337
+ const finding = makeFinding();
338
+ readdirMock.mockResolvedValue(['f-001.json']);
339
+ readFileMock.mockResolvedValue(JSON.stringify(finding));
340
+ makeExecSuccess('git version 2.40.0'); // preFlight git --version
341
+ makeExecSuccess('main'); // getCurrentBranch → not on fix branch
342
+ makeExecSuccess(''); // git checkout -b
343
+
344
+ const agent = createAgent('alpha');
345
+ await agent.run();
346
+
347
+ // Verify git checkout -b was called with a fix/testteam-* branch
348
+ const checkoutCall = execFileMock.mock.calls.find(
349
+ (call: unknown[]) =>
350
+ Array.isArray(call[1]) &&
351
+ (call[1] as string[]).includes('checkout') &&
352
+ (call[1] as string[]).includes('-b'),
353
+ );
354
+ expect(checkoutCall).toBeDefined();
355
+ const branchName = (checkoutCall![1] as string[]).find((a: string) =>
356
+ a.startsWith('fix/testteam-'),
357
+ );
358
+ expect(branchName).toBeDefined();
359
+ });
360
+
361
+ it('does not create a new branch when already on a fix branch', async () => {
362
+ const finding = makeFinding();
363
+ readdirMock.mockResolvedValue(['f-001.json']);
364
+ readFileMock.mockResolvedValue(JSON.stringify(finding));
365
+ makeExecSuccess('git version 2.40.0');
366
+ makeExecSuccess('fix/existing-branch'); // already on fix branch
367
+
368
+ const agent = createAgent('alpha');
369
+ await agent.run();
370
+
371
+ const checkoutCall = execFileMock.mock.calls.find(
372
+ (call: unknown[]) =>
373
+ Array.isArray(call[1]) &&
374
+ (call[1] as string[]).includes('checkout'),
375
+ );
376
+ expect(checkoutCall).toBeUndefined();
377
+ });
378
+ });
379
+
380
+ // -------------------------------------------------------------------------
381
+ // Max 3 attempts escalation
382
+ // -------------------------------------------------------------------------
383
+
384
+ describe('max 3 attempts escalation via shouldAutoCommit/shouldRequireHumanReview', () => {
385
+ it('resolutionStatus is pending-review for logic bugs in alpha (human review required)', async () => {
386
+ const finding = makeFinding({
387
+ id: 'f-logic-001',
388
+ type: 'code-bug-logic',
389
+ severity: 'medium',
390
+ description: 'Business logic error in calculation',
391
+ });
392
+ readdirMock.mockResolvedValue(['f-logic-001.json']);
393
+ readFileMock.mockResolvedValue(JSON.stringify(finding));
394
+ makeExecSuccess('git version 2.40.0');
395
+ makeExecSuccess('fix/existing-branch');
396
+
397
+ const agent = createAgent('alpha');
398
+ await agent.run();
399
+
400
+ const [, writtenJson] = writeFileMock.mock.calls[0] as [string, string, string];
401
+ const written = JSON.parse(writtenJson) as { resolutionStatus: string; requiresHumanReview: boolean };
402
+ expect(written.resolutionStatus).toBe('pending-review');
403
+ expect(written.requiresHumanReview).toBe(true);
404
+ });
405
+ });
406
+ });