@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,430 @@
1
+ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
2
+ import {
3
+ detectCIEnvironment,
4
+ determineExitCode,
5
+ generateGitHubSummary,
6
+ generateAzureTags,
7
+ writeCISummary,
8
+ EXIT_CODE,
9
+ } from '../ci-output';
10
+ import type { CIOutputIO } from '../ci-output';
11
+ import type { RunState, PhaseGateResult, AgentResult } from '../types';
12
+
13
+ /** Helper to build a minimal AgentResult. */
14
+ function makeAgentResult(
15
+ overrides: Partial<AgentResult> & { agentId: number; status: AgentResult['status'] },
16
+ ): AgentResult {
17
+ return {
18
+ agentName: `Agent ${overrides.agentId}`,
19
+ phase: 'alpha',
20
+ startedAt: '2026-01-01T00:00:00Z',
21
+ completedAt: '2026-01-01T00:01:00Z',
22
+ durationMs: 60_000,
23
+ findings: [],
24
+ evidence: [],
25
+ summary: 'Test result',
26
+ ...overrides,
27
+ };
28
+ }
29
+
30
+ /** Helper to build a minimal PhaseGateResult. */
31
+ function makeGate(overrides: Partial<PhaseGateResult> = {}): PhaseGateResult {
32
+ return {
33
+ phase: 'alpha',
34
+ passed: true,
35
+ blockingResults: [],
36
+ advisoryResults: [],
37
+ failedBlockingAgents: [],
38
+ timestamp: '2026-01-01T00:01:00Z',
39
+ ...overrides,
40
+ };
41
+ }
42
+
43
+ /** Helper to build a minimal RunState. */
44
+ function makeRunState(overrides: Partial<RunState> = {}): RunState {
45
+ return {
46
+ runId: 'run-test-123',
47
+ startedAt: '2026-01-01T00:00:00Z',
48
+ currentPhase: 'alpha',
49
+ phaseAttempt: 1,
50
+ agentResults: new Map(),
51
+ gateResults: [],
52
+ totalCost: 0,
53
+ ...overrides,
54
+ };
55
+ }
56
+
57
+ describe('detectCIEnvironment', () => {
58
+ const originalEnv = process.env;
59
+
60
+ beforeEach(() => {
61
+ process.env = { ...originalEnv };
62
+ // Clear all CI env vars
63
+ delete process.env['GITHUB_ACTIONS'];
64
+ delete process.env['TF_BUILD'];
65
+ delete process.env['GITLAB_CI'];
66
+ });
67
+
68
+ afterEach(() => {
69
+ process.env = originalEnv;
70
+ });
71
+
72
+ it('detects GitHub Actions', () => {
73
+ process.env['GITHUB_ACTIONS'] = 'true';
74
+ expect(detectCIEnvironment()).toBe('github');
75
+ });
76
+
77
+ it('detects Azure Pipelines', () => {
78
+ process.env['TF_BUILD'] = 'True';
79
+ expect(detectCIEnvironment()).toBe('azure');
80
+ });
81
+
82
+ it('detects GitLab CI', () => {
83
+ process.env['GITLAB_CI'] = 'true';
84
+ expect(detectCIEnvironment()).toBe('gitlab');
85
+ });
86
+
87
+ it('returns none when no CI environment is detected', () => {
88
+ expect(detectCIEnvironment()).toBe('none');
89
+ });
90
+
91
+ it('prioritises GitHub when multiple CI env vars are set', () => {
92
+ process.env['GITHUB_ACTIONS'] = 'true';
93
+ process.env['TF_BUILD'] = 'True';
94
+ expect(detectCIEnvironment()).toBe('github');
95
+ });
96
+ });
97
+
98
+ describe('determineExitCode', () => {
99
+ it('returns SUCCESS (0) when all gates pass and no advisory failures', () => {
100
+ const gates = [makeGate({ passed: true, advisoryResults: [] })];
101
+ expect(determineExitCode(gates)).toBe(EXIT_CODE.SUCCESS);
102
+ });
103
+
104
+ it('returns BLOCKING_FAILURE (1) when blocking agents failed', () => {
105
+ const gates = [
106
+ makeGate({
107
+ passed: false,
108
+ failedBlockingAgents: [1, 2],
109
+ }),
110
+ ];
111
+ expect(determineExitCode(gates)).toBe(EXIT_CODE.BLOCKING_FAILURE);
112
+ });
113
+
114
+ it('returns ADVISORY_WARNINGS (2) when only advisory agents failed', () => {
115
+ const advisoryResult = makeAgentResult({ agentId: 7, status: 'failed' });
116
+ const gates = [
117
+ makeGate({
118
+ passed: true,
119
+ advisoryResults: [advisoryResult],
120
+ }),
121
+ ];
122
+ expect(determineExitCode(gates)).toBe(EXIT_CODE.ADVISORY_WARNINGS);
123
+ });
124
+
125
+ it('returns BLOCKING_FAILURE (1) when both blocking and advisory fail', () => {
126
+ const advisoryResult = makeAgentResult({ agentId: 7, status: 'failed' });
127
+ const gates = [
128
+ makeGate({
129
+ passed: false,
130
+ failedBlockingAgents: [1],
131
+ advisoryResults: [advisoryResult],
132
+ }),
133
+ ];
134
+ expect(determineExitCode(gates)).toBe(EXIT_CODE.BLOCKING_FAILURE);
135
+ });
136
+
137
+ it('returns SUCCESS (0) when advisory agents pass', () => {
138
+ const advisoryResult = makeAgentResult({ agentId: 7, status: 'passed' });
139
+ const gates = [
140
+ makeGate({
141
+ passed: true,
142
+ advisoryResults: [advisoryResult],
143
+ }),
144
+ ];
145
+ expect(determineExitCode(gates)).toBe(EXIT_CODE.SUCCESS);
146
+ });
147
+
148
+ it('handles multiple phases — fails if any phase has blocking failure', () => {
149
+ const gates = [
150
+ makeGate({ phase: 'alpha', passed: true }),
151
+ makeGate({ phase: 'beta', passed: false, failedBlockingAgents: [5] }),
152
+ ];
153
+ expect(determineExitCode(gates)).toBe(EXIT_CODE.BLOCKING_FAILURE);
154
+ });
155
+
156
+ it('returns SUCCESS (0) for empty gate results', () => {
157
+ expect(determineExitCode([])).toBe(EXIT_CODE.SUCCESS);
158
+ });
159
+ });
160
+
161
+ describe('generateGitHubSummary', () => {
162
+ it('generates markdown with overall passed status', () => {
163
+ const state = makeRunState({
164
+ gateResults: [makeGate({ passed: true })],
165
+ });
166
+ const md = generateGitHubSummary(state);
167
+
168
+ expect(md).toContain('## TestTeam');
169
+ expect(md).toContain('All Gates Passed');
170
+ expect(md).toContain('run-test-123');
171
+ });
172
+
173
+ it('generates markdown with blocking failure status', () => {
174
+ const state = makeRunState({
175
+ gateResults: [
176
+ makeGate({
177
+ passed: false,
178
+ failedBlockingAgents: [1, 3],
179
+ blockingResults: [
180
+ makeAgentResult({ agentId: 1, status: 'failed' }),
181
+ makeAgentResult({ agentId: 3, status: 'failed' }),
182
+ ],
183
+ }),
184
+ ],
185
+ });
186
+ const md = generateGitHubSummary(state);
187
+
188
+ expect(md).toContain('Blocking Failures');
189
+ expect(md).toContain('Failed blocking agents');
190
+ expect(md).toContain('1, 3');
191
+ });
192
+
193
+ it('generates markdown with advisory warnings status', () => {
194
+ const advisoryResult = makeAgentResult({ agentId: 7, status: 'failed' });
195
+ const state = makeRunState({
196
+ gateResults: [
197
+ makeGate({
198
+ passed: true,
199
+ advisoryResults: [advisoryResult],
200
+ }),
201
+ ],
202
+ });
203
+ const md = generateGitHubSummary(state);
204
+
205
+ expect(md).toContain('Advisory Warnings');
206
+ });
207
+
208
+ it('includes findings summary when findings exist', () => {
209
+ const agentResult = makeAgentResult({
210
+ agentId: 1,
211
+ status: 'passed',
212
+ findings: [
213
+ {
214
+ id: 'f1',
215
+ type: 'code-bug-logic',
216
+ severity: 'high',
217
+ agentId: 1,
218
+ module: 'auth',
219
+ description: 'Logic error in auth module',
220
+ },
221
+ {
222
+ id: 'f2',
223
+ type: 'code-bug-cosmetic',
224
+ severity: 'low',
225
+ agentId: 1,
226
+ module: 'ui',
227
+ description: 'Cosmetic issue',
228
+ },
229
+ ],
230
+ });
231
+
232
+ const agentMap = new Map<number, AgentResult>();
233
+ agentMap.set(1, agentResult);
234
+
235
+ const state = makeRunState({
236
+ agentResults: agentMap,
237
+ gateResults: [makeGate()],
238
+ });
239
+
240
+ const md = generateGitHubSummary(state);
241
+
242
+ expect(md).toContain('Findings Summary');
243
+ expect(md).toContain('high');
244
+ expect(md).toContain('low');
245
+ });
246
+
247
+ it('includes agent results table with duration', () => {
248
+ const result = makeAgentResult({
249
+ agentId: 1,
250
+ agentName: 'Smoke Tester',
251
+ status: 'passed',
252
+ durationMs: 5_000,
253
+ });
254
+
255
+ const state = makeRunState({
256
+ gateResults: [
257
+ makeGate({
258
+ blockingResults: [result],
259
+ }),
260
+ ],
261
+ });
262
+
263
+ const md = generateGitHubSummary(state);
264
+
265
+ expect(md).toContain('Smoke Tester');
266
+ expect(md).toContain('5s');
267
+ });
268
+ });
269
+
270
+ describe('generateAzureTags', () => {
271
+ it('returns passed tag when all gates pass', () => {
272
+ const state = makeRunState({
273
+ gateResults: [makeGate({ passed: true })],
274
+ });
275
+ const tags = generateAzureTags(state);
276
+
277
+ expect(tags).toContain('testteam:passed');
278
+ expect(tags).toContain('testteam:phase:alpha');
279
+ expect(tags).toContain('testteam:phase:alpha:passed');
280
+ });
281
+
282
+ it('returns failed tag when blocking agents fail', () => {
283
+ const state = makeRunState({
284
+ gateResults: [
285
+ makeGate({ passed: false, failedBlockingAgents: [1] }),
286
+ ],
287
+ });
288
+ const tags = generateAzureTags(state);
289
+
290
+ expect(tags).toContain('testteam:failed');
291
+ expect(tags).toContain('testteam:phase:alpha:failed');
292
+ });
293
+
294
+ it('returns warnings tag for advisory-only failures', () => {
295
+ const advisoryResult = makeAgentResult({ agentId: 7, status: 'failed' });
296
+ const state = makeRunState({
297
+ gateResults: [
298
+ makeGate({ passed: true, advisoryResults: [advisoryResult] }),
299
+ ],
300
+ });
301
+ const tags = generateAzureTags(state);
302
+
303
+ expect(tags).toContain('testteam:warnings');
304
+ });
305
+
306
+ it('includes severity tags from findings', () => {
307
+ const agentResult = makeAgentResult({
308
+ agentId: 1,
309
+ status: 'passed',
310
+ findings: [
311
+ {
312
+ id: 'f1',
313
+ type: 'code-bug-security',
314
+ severity: 'critical',
315
+ agentId: 1,
316
+ module: 'auth',
317
+ description: 'Security vulnerability',
318
+ },
319
+ ],
320
+ });
321
+
322
+ const agentMap = new Map<number, AgentResult>();
323
+ agentMap.set(1, agentResult);
324
+
325
+ const state = makeRunState({
326
+ agentResults: agentMap,
327
+ gateResults: [makeGate()],
328
+ });
329
+ const tags = generateAzureTags(state);
330
+
331
+ expect(tags).toContain('testteam:severity:critical');
332
+ });
333
+
334
+ it('includes tags for multiple phases', () => {
335
+ const state = makeRunState({
336
+ gateResults: [
337
+ makeGate({ phase: 'alpha', passed: true }),
338
+ makeGate({ phase: 'beta', passed: true }),
339
+ ],
340
+ });
341
+ const tags = generateAzureTags(state);
342
+
343
+ expect(tags).toContain('testteam:phase:alpha');
344
+ expect(tags).toContain('testteam:phase:beta');
345
+ });
346
+ });
347
+
348
+ describe('writeCISummary', () => {
349
+ const originalEnv = process.env;
350
+
351
+ function mockIO(): CIOutputIO & {
352
+ appendFileCalls: Array<{ path: string; data: string }>;
353
+ writeFileCalls: Array<{ path: string; data: string }>;
354
+ logCalls: string[];
355
+ } {
356
+ const appendFileCalls: Array<{ path: string; data: string }> = [];
357
+ const writeFileCalls: Array<{ path: string; data: string }> = [];
358
+ const logCalls: string[] = [];
359
+ return {
360
+ appendFile: (filePath: string, data: string) => appendFileCalls.push({ path: filePath, data }),
361
+ writeFile: (filePath: string, data: string) => writeFileCalls.push({ path: filePath, data }),
362
+ log: (message: string) => logCalls.push(message),
363
+ appendFileCalls,
364
+ writeFileCalls,
365
+ logCalls,
366
+ };
367
+ }
368
+
369
+ beforeEach(() => {
370
+ process.env = { ...originalEnv };
371
+ delete process.env['GITHUB_ACTIONS'];
372
+ delete process.env['TF_BUILD'];
373
+ delete process.env['GITLAB_CI'];
374
+ delete process.env['GITHUB_STEP_SUMMARY'];
375
+ });
376
+
377
+ afterEach(() => {
378
+ process.env = originalEnv;
379
+ });
380
+
381
+ it('writes markdown to GITHUB_STEP_SUMMARY in GitHub Actions', () => {
382
+ process.env['GITHUB_ACTIONS'] = 'true';
383
+ process.env['GITHUB_STEP_SUMMARY'] = '/tmp/test-summary.md';
384
+
385
+ const io = mockIO();
386
+ const state = makeRunState({ gateResults: [makeGate()] });
387
+
388
+ writeCISummary(state, io);
389
+
390
+ expect(io.appendFileCalls).toHaveLength(1);
391
+ expect(io.appendFileCalls[0].path).toBe('/tmp/test-summary.md');
392
+ expect(io.appendFileCalls[0].data).toContain('TestTeam');
393
+ });
394
+
395
+ it('writes tags to console in Azure Pipelines', () => {
396
+ process.env['TF_BUILD'] = 'True';
397
+
398
+ const io = mockIO();
399
+ const state = makeRunState({ gateResults: [makeGate()] });
400
+
401
+ writeCISummary(state, io);
402
+
403
+ const vsoCalls = io.logCalls.filter(msg => msg.includes('##vso[build.addbuildtag]'));
404
+ expect(vsoCalls.length).toBeGreaterThan(0);
405
+ });
406
+
407
+ it('writes summary file in GitLab CI', () => {
408
+ process.env['GITLAB_CI'] = 'true';
409
+
410
+ const io = mockIO();
411
+ const state = makeRunState({ gateResults: [makeGate()] });
412
+
413
+ writeCISummary(state, io);
414
+
415
+ expect(io.writeFileCalls).toHaveLength(1);
416
+ expect(io.writeFileCalls[0].path).toBe('.run/testteam-summary.md');
417
+ expect(io.writeFileCalls[0].data).toContain('TestTeam');
418
+ });
419
+
420
+ it('does nothing when not in CI', () => {
421
+ const io = mockIO();
422
+ const state = makeRunState({ gateResults: [makeGate()] });
423
+
424
+ writeCISummary(state, io);
425
+
426
+ expect(io.appendFileCalls).toHaveLength(0);
427
+ expect(io.writeFileCalls).toHaveLength(0);
428
+ expect(io.logCalls).toHaveLength(0);
429
+ });
430
+ });