@avi770/testteam 1.2.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (300) hide show
  1. package/CHANGELOG.md +29 -1
  2. package/README.md +53 -6
  3. package/agents/24-signup-onboarding-tester.ts +429 -0
  4. package/agents/25-crud-flow-tester.ts +302 -0
  5. package/agents/26-form-validator.ts +297 -0
  6. package/agents/27-search-filter-tester.ts +326 -0
  7. package/agents/28-navigation-routing-tester.ts +425 -0
  8. package/agents/29-responsive-interaction-tester.ts +350 -0
  9. package/agents/30-multi-user-scenario-tester.ts +319 -0
  10. package/agents/31-load-tester.ts +134 -0
  11. package/agents/32-memory-leak-detector.ts +194 -0
  12. package/agents/33-bundle-analyzer.ts +132 -0
  13. package/agents/34-xss-scanner.ts +191 -0
  14. package/agents/35-csrf-tester.ts +82 -0
  15. package/agents/36-auth-fuzzer.ts +194 -0
  16. package/agents/37-dependency-scanner.ts +176 -0
  17. package/agents/38-secrets-scanner.ts +137 -0
  18. package/agents/39-api-contract-tester.ts +199 -0
  19. package/agents/40-rate-limit-tester.ts +94 -0
  20. package/agents/41-api-pagination-tester.ts +97 -0
  21. package/agents/42-graphql-tester.ts +222 -0
  22. package/agents/43-data-consistency-checker.ts +205 -0
  23. package/agents/44-backup-recovery-tester.ts +152 -0
  24. package/agents/45-data-privacy-scanner.ts +125 -0
  25. package/agents/46-seo-auditor.ts +294 -0
  26. package/agents/47-social-preview-tester.ts +232 -0
  27. package/agents/48-lighthouse-auditor.ts +213 -0
  28. package/agents/49-i18n-tester.ts +198 -0
  29. package/agents/50-timezone-tester.ts +173 -0
  30. package/agents/51-error-recovery-tester.ts +155 -0
  31. package/agents/52-offline-mode-tester.ts +180 -0
  32. package/agents/53-graceful-degradation-tester.ts +156 -0
  33. package/agents/54-websocket-tester.ts +151 -0
  34. package/agents/55-realtime-sync-tester.ts +194 -0
  35. package/agents/56-file-upload-tester.ts +194 -0
  36. package/agents/57-export-tester.ts +174 -0
  37. package/agents/58-payment-flow-tester.ts +183 -0
  38. package/agents/59-ssl-tls-auditor.ts +141 -0
  39. package/agents/60-dns-cdn-tester.ts +117 -0
  40. package/agents/61-docker-health-checker.ts +111 -0
  41. package/agents/62-env-config-validator.ts +152 -0
  42. package/agents/63-log-quality-auditor.ts +136 -0
  43. package/agents/64-analytics-tracker-tester.ts +165 -0
  44. package/agents/65-gdpr-compliance-tester.ts +215 -0
  45. package/agents/66-soc2-control-validator.ts +210 -0
  46. package/agents/67-wcag-aaa-tester.ts +241 -0
  47. package/agents/68-dead-code-detector.ts +135 -0
  48. package/agents/69-type-safety-auditor.ts +164 -0
  49. package/agents/70-complexity-analyzer.ts +179 -0
  50. package/agents/__tests__/24-signup-onboarding-tester.test.ts +274 -0
  51. package/agents/__tests__/25-crud-flow-tester.test.ts +322 -0
  52. package/agents/__tests__/26-form-validator.test.ts +345 -0
  53. package/agents/__tests__/27-search-filter-tester.test.ts +311 -0
  54. package/agents/__tests__/28-navigation-routing-tester.test.ts +328 -0
  55. package/agents/__tests__/29-responsive-interaction-tester.test.ts +297 -0
  56. package/agents/__tests__/30-multi-user-scenario-tester.test.ts +328 -0
  57. package/agents/__tests__/31-load-tester.test.ts +189 -0
  58. package/agents/__tests__/32-memory-leak-detector.test.ts +251 -0
  59. package/agents/__tests__/33-bundle-analyzer.test.ts +237 -0
  60. package/agents/__tests__/34-xss-scanner.test.ts +258 -0
  61. package/agents/__tests__/35-csrf-tester.test.ts +200 -0
  62. package/agents/__tests__/36-auth-fuzzer.test.ts +214 -0
  63. package/agents/__tests__/37-dependency-scanner.test.ts +266 -0
  64. package/agents/__tests__/38-secrets-scanner.test.ts +224 -0
  65. package/agents/__tests__/39-api-contract-tester.test.ts +312 -0
  66. package/agents/__tests__/40-rate-limit-tester.test.ts +192 -0
  67. package/agents/__tests__/41-api-pagination-tester.test.ts +198 -0
  68. package/agents/__tests__/42-graphql-tester.test.ts +252 -0
  69. package/agents/__tests__/43-data-consistency-checker.test.ts +232 -0
  70. package/agents/__tests__/44-backup-recovery-tester.test.ts +222 -0
  71. package/agents/__tests__/45-data-privacy-scanner.test.ts +223 -0
  72. package/agents/__tests__/46-seo-auditor.test.ts +261 -0
  73. package/agents/__tests__/47-social-preview-tester.test.ts +245 -0
  74. package/agents/__tests__/48-lighthouse-auditor.test.ts +276 -0
  75. package/agents/__tests__/49-i18n-tester.test.ts +201 -0
  76. package/agents/__tests__/50-timezone-tester.test.ts +172 -0
  77. package/agents/__tests__/51-error-recovery-tester.test.ts +162 -0
  78. package/agents/__tests__/52-offline-mode-tester.test.ts +164 -0
  79. package/agents/__tests__/53-graceful-degradation-tester.test.ts +168 -0
  80. package/agents/__tests__/54-websocket-tester.test.ts +157 -0
  81. package/agents/__tests__/55-realtime-sync-tester.test.ts +181 -0
  82. package/agents/__tests__/56-file-upload-tester.test.ts +172 -0
  83. package/agents/__tests__/57-export-tester.test.ts +169 -0
  84. package/agents/__tests__/58-payment-flow-tester.test.ts +182 -0
  85. package/agents/__tests__/59-ssl-tls-auditor.test.ts +179 -0
  86. package/agents/__tests__/60-dns-cdn-tester.test.ts +176 -0
  87. package/agents/__tests__/61-docker-health-checker.test.ts +150 -0
  88. package/agents/__tests__/62-env-config-validator.test.ts +166 -0
  89. package/agents/__tests__/63-log-quality-auditor.test.ts +175 -0
  90. package/agents/__tests__/64-analytics-tracker-tester.test.ts +158 -0
  91. package/agents/__tests__/65-gdpr-compliance-tester.test.ts +174 -0
  92. package/agents/__tests__/66-soc2-control-validator.test.ts +183 -0
  93. package/agents/__tests__/67-wcag-aaa-tester.test.ts +190 -0
  94. package/agents/__tests__/68-dead-code-detector.test.ts +174 -0
  95. package/agents/__tests__/69-type-safety-auditor.test.ts +173 -0
  96. package/agents/__tests__/70-complexity-analyzer.test.ts +177 -0
  97. package/agents/__tests__/registry.test.ts +13 -13
  98. package/agents/registry.ts +146 -5
  99. package/core/__tests__/integration.test.ts +4 -4
  100. package/core/__tests__/orchestrator.test.ts +17 -16
  101. package/core/license.ts +208 -211
  102. package/core/orchestrator.ts +6 -4
  103. package/dist/agents/24-signup-onboarding-tester.d.ts +35 -0
  104. package/dist/agents/24-signup-onboarding-tester.d.ts.map +1 -0
  105. package/dist/agents/24-signup-onboarding-tester.js +357 -0
  106. package/dist/agents/24-signup-onboarding-tester.js.map +1 -0
  107. package/dist/agents/25-crud-flow-tester.d.ts +11 -0
  108. package/dist/agents/25-crud-flow-tester.d.ts.map +1 -0
  109. package/dist/agents/25-crud-flow-tester.js +253 -0
  110. package/dist/agents/25-crud-flow-tester.js.map +1 -0
  111. package/dist/agents/26-form-validator.d.ts +12 -0
  112. package/dist/agents/26-form-validator.d.ts.map +1 -0
  113. package/dist/agents/26-form-validator.js +257 -0
  114. package/dist/agents/26-form-validator.js.map +1 -0
  115. package/dist/agents/27-search-filter-tester.d.ts +20 -0
  116. package/dist/agents/27-search-filter-tester.d.ts.map +1 -0
  117. package/dist/agents/27-search-filter-tester.js +267 -0
  118. package/dist/agents/27-search-filter-tester.js.map +1 -0
  119. package/dist/agents/28-navigation-routing-tester.d.ts +32 -0
  120. package/dist/agents/28-navigation-routing-tester.d.ts.map +1 -0
  121. package/dist/agents/28-navigation-routing-tester.js +363 -0
  122. package/dist/agents/28-navigation-routing-tester.js.map +1 -0
  123. package/dist/agents/29-responsive-interaction-tester.d.ts +26 -0
  124. package/dist/agents/29-responsive-interaction-tester.d.ts.map +1 -0
  125. package/dist/agents/29-responsive-interaction-tester.js +272 -0
  126. package/dist/agents/29-responsive-interaction-tester.js.map +1 -0
  127. package/dist/agents/30-multi-user-scenario-tester.d.ts +24 -0
  128. package/dist/agents/30-multi-user-scenario-tester.d.ts.map +1 -0
  129. package/dist/agents/30-multi-user-scenario-tester.js +254 -0
  130. package/dist/agents/30-multi-user-scenario-tester.js.map +1 -0
  131. package/dist/agents/31-load-tester.d.ts +12 -0
  132. package/dist/agents/31-load-tester.d.ts.map +1 -0
  133. package/dist/agents/31-load-tester.js +110 -0
  134. package/dist/agents/31-load-tester.js.map +1 -0
  135. package/dist/agents/32-memory-leak-detector.d.ts +12 -0
  136. package/dist/agents/32-memory-leak-detector.d.ts.map +1 -0
  137. package/dist/agents/32-memory-leak-detector.js +167 -0
  138. package/dist/agents/32-memory-leak-detector.js.map +1 -0
  139. package/dist/agents/33-bundle-analyzer.d.ts +10 -0
  140. package/dist/agents/33-bundle-analyzer.d.ts.map +1 -0
  141. package/dist/agents/33-bundle-analyzer.js +111 -0
  142. package/dist/agents/33-bundle-analyzer.js.map +1 -0
  143. package/dist/agents/34-xss-scanner.d.ts +11 -0
  144. package/dist/agents/34-xss-scanner.d.ts.map +1 -0
  145. package/dist/agents/34-xss-scanner.js +164 -0
  146. package/dist/agents/34-xss-scanner.js.map +1 -0
  147. package/dist/agents/35-csrf-tester.d.ts +11 -0
  148. package/dist/agents/35-csrf-tester.d.ts.map +1 -0
  149. package/dist/agents/35-csrf-tester.js +70 -0
  150. package/dist/agents/35-csrf-tester.js.map +1 -0
  151. package/dist/agents/36-auth-fuzzer.d.ts +13 -0
  152. package/dist/agents/36-auth-fuzzer.d.ts.map +1 -0
  153. package/dist/agents/36-auth-fuzzer.js +163 -0
  154. package/dist/agents/36-auth-fuzzer.js.map +1 -0
  155. package/dist/agents/37-dependency-scanner.d.ts +11 -0
  156. package/dist/agents/37-dependency-scanner.d.ts.map +1 -0
  157. package/dist/agents/37-dependency-scanner.js +139 -0
  158. package/dist/agents/37-dependency-scanner.js.map +1 -0
  159. package/dist/agents/38-secrets-scanner.d.ts +11 -0
  160. package/dist/agents/38-secrets-scanner.d.ts.map +1 -0
  161. package/dist/agents/38-secrets-scanner.js +116 -0
  162. package/dist/agents/38-secrets-scanner.js.map +1 -0
  163. package/dist/agents/39-api-contract-tester.d.ts +12 -0
  164. package/dist/agents/39-api-contract-tester.d.ts.map +1 -0
  165. package/dist/agents/39-api-contract-tester.js +142 -0
  166. package/dist/agents/39-api-contract-tester.js.map +1 -0
  167. package/dist/agents/40-rate-limit-tester.d.ts +12 -0
  168. package/dist/agents/40-rate-limit-tester.d.ts.map +1 -0
  169. package/dist/agents/40-rate-limit-tester.js +79 -0
  170. package/dist/agents/40-rate-limit-tester.js.map +1 -0
  171. package/dist/agents/41-api-pagination-tester.d.ts +12 -0
  172. package/dist/agents/41-api-pagination-tester.d.ts.map +1 -0
  173. package/dist/agents/41-api-pagination-tester.js +79 -0
  174. package/dist/agents/41-api-pagination-tester.js.map +1 -0
  175. package/dist/agents/42-graphql-tester.d.ts +13 -0
  176. package/dist/agents/42-graphql-tester.d.ts.map +1 -0
  177. package/dist/agents/42-graphql-tester.js +187 -0
  178. package/dist/agents/42-graphql-tester.js.map +1 -0
  179. package/dist/agents/43-data-consistency-checker.d.ts +11 -0
  180. package/dist/agents/43-data-consistency-checker.d.ts.map +1 -0
  181. package/dist/agents/43-data-consistency-checker.js +176 -0
  182. package/dist/agents/43-data-consistency-checker.js.map +1 -0
  183. package/dist/agents/44-backup-recovery-tester.d.ts +11 -0
  184. package/dist/agents/44-backup-recovery-tester.d.ts.map +1 -0
  185. package/dist/agents/44-backup-recovery-tester.js +128 -0
  186. package/dist/agents/44-backup-recovery-tester.js.map +1 -0
  187. package/dist/agents/45-data-privacy-scanner.d.ts +11 -0
  188. package/dist/agents/45-data-privacy-scanner.d.ts.map +1 -0
  189. package/dist/agents/45-data-privacy-scanner.js +100 -0
  190. package/dist/agents/45-data-privacy-scanner.js.map +1 -0
  191. package/dist/agents/46-seo-auditor.d.ts +12 -0
  192. package/dist/agents/46-seo-auditor.d.ts.map +1 -0
  193. package/dist/agents/46-seo-auditor.js +275 -0
  194. package/dist/agents/46-seo-auditor.js.map +1 -0
  195. package/dist/agents/47-social-preview-tester.d.ts +11 -0
  196. package/dist/agents/47-social-preview-tester.d.ts.map +1 -0
  197. package/dist/agents/47-social-preview-tester.js +219 -0
  198. package/dist/agents/47-social-preview-tester.js.map +1 -0
  199. package/dist/agents/48-lighthouse-auditor.d.ts +11 -0
  200. package/dist/agents/48-lighthouse-auditor.d.ts.map +1 -0
  201. package/dist/agents/48-lighthouse-auditor.js +192 -0
  202. package/dist/agents/48-lighthouse-auditor.js.map +1 -0
  203. package/dist/agents/49-i18n-tester.d.ts +13 -0
  204. package/dist/agents/49-i18n-tester.d.ts.map +1 -0
  205. package/dist/agents/49-i18n-tester.js +172 -0
  206. package/dist/agents/49-i18n-tester.js.map +1 -0
  207. package/dist/agents/50-timezone-tester.d.ts +11 -0
  208. package/dist/agents/50-timezone-tester.d.ts.map +1 -0
  209. package/dist/agents/50-timezone-tester.js +152 -0
  210. package/dist/agents/50-timezone-tester.js.map +1 -0
  211. package/dist/agents/51-error-recovery-tester.d.ts +11 -0
  212. package/dist/agents/51-error-recovery-tester.d.ts.map +1 -0
  213. package/dist/agents/51-error-recovery-tester.js +134 -0
  214. package/dist/agents/51-error-recovery-tester.js.map +1 -0
  215. package/dist/agents/52-offline-mode-tester.d.ts +12 -0
  216. package/dist/agents/52-offline-mode-tester.d.ts.map +1 -0
  217. package/dist/agents/52-offline-mode-tester.js +161 -0
  218. package/dist/agents/52-offline-mode-tester.js.map +1 -0
  219. package/dist/agents/53-graceful-degradation-tester.d.ts +12 -0
  220. package/dist/agents/53-graceful-degradation-tester.d.ts.map +1 -0
  221. package/dist/agents/53-graceful-degradation-tester.js +130 -0
  222. package/dist/agents/53-graceful-degradation-tester.js.map +1 -0
  223. package/dist/agents/54-websocket-tester.d.ts +10 -0
  224. package/dist/agents/54-websocket-tester.d.ts.map +1 -0
  225. package/dist/agents/54-websocket-tester.js +132 -0
  226. package/dist/agents/54-websocket-tester.js.map +1 -0
  227. package/dist/agents/55-realtime-sync-tester.d.ts +11 -0
  228. package/dist/agents/55-realtime-sync-tester.d.ts.map +1 -0
  229. package/dist/agents/55-realtime-sync-tester.js +175 -0
  230. package/dist/agents/55-realtime-sync-tester.js.map +1 -0
  231. package/dist/agents/56-file-upload-tester.d.ts +12 -0
  232. package/dist/agents/56-file-upload-tester.d.ts.map +1 -0
  233. package/dist/agents/56-file-upload-tester.js +166 -0
  234. package/dist/agents/56-file-upload-tester.js.map +1 -0
  235. package/dist/agents/57-export-tester.d.ts +11 -0
  236. package/dist/agents/57-export-tester.d.ts.map +1 -0
  237. package/dist/agents/57-export-tester.js +155 -0
  238. package/dist/agents/57-export-tester.js.map +1 -0
  239. package/dist/agents/58-payment-flow-tester.d.ts +11 -0
  240. package/dist/agents/58-payment-flow-tester.d.ts.map +1 -0
  241. package/dist/agents/58-payment-flow-tester.js +159 -0
  242. package/dist/agents/58-payment-flow-tester.js.map +1 -0
  243. package/dist/agents/59-ssl-tls-auditor.d.ts +10 -0
  244. package/dist/agents/59-ssl-tls-auditor.d.ts.map +1 -0
  245. package/dist/agents/59-ssl-tls-auditor.js +132 -0
  246. package/dist/agents/59-ssl-tls-auditor.js.map +1 -0
  247. package/dist/agents/60-dns-cdn-tester.d.ts +10 -0
  248. package/dist/agents/60-dns-cdn-tester.d.ts.map +1 -0
  249. package/dist/agents/60-dns-cdn-tester.js +105 -0
  250. package/dist/agents/60-dns-cdn-tester.js.map +1 -0
  251. package/dist/agents/61-docker-health-checker.d.ts +10 -0
  252. package/dist/agents/61-docker-health-checker.d.ts.map +1 -0
  253. package/dist/agents/61-docker-health-checker.js +95 -0
  254. package/dist/agents/61-docker-health-checker.js.map +1 -0
  255. package/dist/agents/62-env-config-validator.d.ts +10 -0
  256. package/dist/agents/62-env-config-validator.d.ts.map +1 -0
  257. package/dist/agents/62-env-config-validator.js +132 -0
  258. package/dist/agents/62-env-config-validator.js.map +1 -0
  259. package/dist/agents/63-log-quality-auditor.d.ts +9 -0
  260. package/dist/agents/63-log-quality-auditor.d.ts.map +1 -0
  261. package/dist/agents/63-log-quality-auditor.js +121 -0
  262. package/dist/agents/63-log-quality-auditor.js.map +1 -0
  263. package/dist/agents/64-analytics-tracker-tester.d.ts +10 -0
  264. package/dist/agents/64-analytics-tracker-tester.d.ts.map +1 -0
  265. package/dist/agents/64-analytics-tracker-tester.js +146 -0
  266. package/dist/agents/64-analytics-tracker-tester.js.map +1 -0
  267. package/dist/agents/65-gdpr-compliance-tester.d.ts +13 -0
  268. package/dist/agents/65-gdpr-compliance-tester.d.ts.map +1 -0
  269. package/dist/agents/65-gdpr-compliance-tester.js +186 -0
  270. package/dist/agents/65-gdpr-compliance-tester.js.map +1 -0
  271. package/dist/agents/66-soc2-control-validator.d.ts +14 -0
  272. package/dist/agents/66-soc2-control-validator.d.ts.map +1 -0
  273. package/dist/agents/66-soc2-control-validator.js +178 -0
  274. package/dist/agents/66-soc2-control-validator.js.map +1 -0
  275. package/dist/agents/67-wcag-aaa-tester.d.ts +13 -0
  276. package/dist/agents/67-wcag-aaa-tester.d.ts.map +1 -0
  277. package/dist/agents/67-wcag-aaa-tester.js +207 -0
  278. package/dist/agents/67-wcag-aaa-tester.js.map +1 -0
  279. package/dist/agents/68-dead-code-detector.d.ts +9 -0
  280. package/dist/agents/68-dead-code-detector.d.ts.map +1 -0
  281. package/dist/agents/68-dead-code-detector.js +116 -0
  282. package/dist/agents/68-dead-code-detector.js.map +1 -0
  283. package/dist/agents/69-type-safety-auditor.d.ts +9 -0
  284. package/dist/agents/69-type-safety-auditor.d.ts.map +1 -0
  285. package/dist/agents/69-type-safety-auditor.js +148 -0
  286. package/dist/agents/69-type-safety-auditor.js.map +1 -0
  287. package/dist/agents/70-complexity-analyzer.d.ts +10 -0
  288. package/dist/agents/70-complexity-analyzer.d.ts.map +1 -0
  289. package/dist/agents/70-complexity-analyzer.js +154 -0
  290. package/dist/agents/70-complexity-analyzer.js.map +1 -0
  291. package/dist/agents/registry.d.ts +3 -3
  292. package/dist/agents/registry.d.ts.map +1 -1
  293. package/dist/agents/registry.js +146 -5
  294. package/dist/agents/registry.js.map +1 -1
  295. package/dist/core/license.js +2 -5
  296. package/dist/core/license.js.map +1 -1
  297. package/dist/core/orchestrator.d.ts.map +1 -1
  298. package/dist/core/orchestrator.js +6 -4
  299. package/dist/core/orchestrator.js.map +1 -1
  300. package/package.json +2 -2
@@ -0,0 +1,312 @@
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 helpers
7
+ // ---------------------------------------------------------------------------
8
+
9
+ function makeConfig(overrides: Partial<ValidatedConfig> = {}): ValidatedConfig {
10
+ return {
11
+ schemaVersion: 1,
12
+ name: 'test',
13
+ auth: { loginUrl: 'http://localhost/login', credentials: {} },
14
+ modules: [{ id: 'tasks', route: '/tasks', sidebarIcon: true }],
15
+ environments: {
16
+ alpha: { baseUrl: 'http://localhost:3000', seed: false },
17
+ },
18
+ portals: [],
19
+ breakpoints: [],
20
+ integrations: [],
21
+ workers: [],
22
+ workflows: [],
23
+ accuracy: { enabled: false, decimalPrecision: 2, currencySymbol: '$', timezone: 'UTC' },
24
+ tenancy: { enabled: false, isolationField: 'firmId', testFirms: [] },
25
+ costBudget: { maxPerRun: 100, maxPerAgent: 10, alertThreshold: 0.8, onExceeded: 'abort' },
26
+ agentClassification: { blocking: [], advisory: [39] },
27
+ thresholds: {
28
+ unitCoverage: { lines: 80, functions: 80, branches: 80, statements: 80 },
29
+ lighthouse: { accessibility: 90, performance: 90, bestPractices: 90, seo: 90 },
30
+ apiResponseTime: 2000,
31
+ bundleSizeLimit: 500000,
32
+ maxConsoleErrors: 0,
33
+ },
34
+ disabledAgents: [],
35
+ disabledAgentSet: new Set<number>(),
36
+ ...overrides,
37
+ };
38
+ }
39
+
40
+ // ---------------------------------------------------------------------------
41
+ // Mock fetch & fs
42
+ // ---------------------------------------------------------------------------
43
+
44
+ const fetchMock = vi.fn();
45
+ vi.stubGlobal('fetch', fetchMock);
46
+
47
+ const mockReadFile = vi.fn();
48
+ vi.mock('node:fs/promises', () => ({
49
+ readFile: (...args: unknown[]) => mockReadFile(...args),
50
+ }));
51
+
52
+ // ---------------------------------------------------------------------------
53
+ // Dynamic import AFTER mocks
54
+ // ---------------------------------------------------------------------------
55
+
56
+ const { ApiContractTesterAgent } = await import('../39-api-contract-tester');
57
+
58
+ function createAgent(
59
+ config: ValidatedConfig = makeConfig(),
60
+ phase: Phase = 'beta',
61
+ ): InstanceType<typeof ApiContractTesterAgent> {
62
+ return new ApiContractTesterAgent(config, phase, '/tmp/test-run');
63
+ }
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // Tests
67
+ // ---------------------------------------------------------------------------
68
+
69
+ describe('ApiContractTesterAgent', () => {
70
+ beforeEach(() => {
71
+ fetchMock.mockReset().mockResolvedValue({ ok: true, status: 200 });
72
+ mockReadFile.mockReset();
73
+ });
74
+
75
+ it('has agentId 39 and correct agentName', () => {
76
+ const agent = createAgent();
77
+ expect(agent.agentId).toBe(39);
78
+ expect(agent.agentName).toBe('API Contract Tester');
79
+ });
80
+
81
+ it('produces low finding when no spec found', async () => {
82
+ mockReadFile.mockRejectedValue(new Error('ENOENT'));
83
+
84
+ const agent = createAgent();
85
+ const result = await agent.run();
86
+
87
+ const noSpec = result.findings.find(f => f.id.includes('no-spec'));
88
+ expect(noSpec).toBeDefined();
89
+ expect(noSpec!.severity).toBe('low');
90
+ });
91
+
92
+ it('detects missing required fields in response', async () => {
93
+ const spec = {
94
+ paths: {
95
+ '/api/tasks': {
96
+ get: {
97
+ responses: {
98
+ '200': {
99
+ content: {
100
+ 'application/json': {
101
+ schema: {
102
+ type: 'object',
103
+ properties: { id: { type: 'string' }, name: { type: 'string' } },
104
+ required: ['id', 'name'],
105
+ },
106
+ },
107
+ },
108
+ },
109
+ },
110
+ },
111
+ },
112
+ },
113
+ };
114
+ mockReadFile.mockResolvedValue(JSON.stringify(spec));
115
+
116
+ fetchMock.mockImplementation((url: string, opts?: Record<string, unknown>) => {
117
+ if (opts?.method === 'HEAD') return Promise.resolve({ ok: true, status: 200 }); // env check
118
+ return Promise.resolve({
119
+ ok: true,
120
+ status: 200,
121
+ headers: new Map([['content-type', 'application/json']]),
122
+ json: () => Promise.resolve({ id: '1' }), // missing 'name'
123
+ });
124
+ });
125
+
126
+ const agent = createAgent();
127
+ const result = await agent.run();
128
+
129
+ const missingField = result.findings.find(f => f.id.includes('missing-field'));
130
+ expect(missingField).toBeDefined();
131
+ expect(missingField!.severity).toBe('medium');
132
+ });
133
+
134
+ it('detects wrong field types in response', async () => {
135
+ const spec = {
136
+ paths: {
137
+ '/api/tasks': {
138
+ get: {
139
+ responses: {
140
+ '200': {
141
+ content: {
142
+ 'application/json': {
143
+ schema: {
144
+ type: 'object',
145
+ properties: { count: { type: 'number' } },
146
+ },
147
+ },
148
+ },
149
+ },
150
+ },
151
+ },
152
+ },
153
+ },
154
+ };
155
+ mockReadFile.mockResolvedValue(JSON.stringify(spec));
156
+
157
+ fetchMock.mockImplementation((url: string, opts?: Record<string, unknown>) => {
158
+ if (opts?.method === 'HEAD') return Promise.resolve({ ok: true, status: 200 });
159
+ return Promise.resolve({
160
+ ok: true,
161
+ status: 200,
162
+ headers: new Map([['content-type', 'application/json']]),
163
+ json: () => Promise.resolve({ count: 'not-a-number' }),
164
+ });
165
+ });
166
+
167
+ const agent = createAgent();
168
+ const result = await agent.run();
169
+
170
+ const wrongType = result.findings.find(f => f.id.includes('wrong-type'));
171
+ expect(wrongType).toBeDefined();
172
+ expect(wrongType!.severity).toBe('high');
173
+ });
174
+
175
+ it('passes when response matches schema', async () => {
176
+ const spec = {
177
+ paths: {
178
+ '/api/tasks': {
179
+ get: {
180
+ responses: {
181
+ '200': {
182
+ content: {
183
+ 'application/json': {
184
+ schema: {
185
+ type: 'object',
186
+ properties: { id: { type: 'string' } },
187
+ required: ['id'],
188
+ },
189
+ },
190
+ },
191
+ },
192
+ },
193
+ },
194
+ },
195
+ },
196
+ };
197
+ mockReadFile.mockResolvedValue(JSON.stringify(spec));
198
+
199
+ fetchMock.mockImplementation((url: string, opts?: Record<string, unknown>) => {
200
+ if (opts?.method === 'HEAD') return Promise.resolve({ ok: true, status: 200 });
201
+ return Promise.resolve({
202
+ ok: true,
203
+ status: 200,
204
+ headers: new Map([['content-type', 'application/json']]),
205
+ json: () => Promise.resolve({ id: '1' }),
206
+ });
207
+ });
208
+
209
+ const agent = createAgent();
210
+ const result = await agent.run();
211
+
212
+ const schemaFindings = result.findings.filter(f => f.id.includes('missing-field') || f.id.includes('wrong-type'));
213
+ expect(schemaFindings.length).toBe(0);
214
+ });
215
+
216
+ it('handles request failures gracefully', async () => {
217
+ const spec = { paths: { '/api/tasks': { get: { responses: {} } } } };
218
+ mockReadFile.mockResolvedValue(JSON.stringify(spec));
219
+ fetchMock.mockRejectedValue(new Error('Connection refused'));
220
+
221
+ const agent = createAgent();
222
+ const result = await agent.run();
223
+
224
+ // Should not crash
225
+ expect(result.status).toBeDefined();
226
+ });
227
+
228
+ it('skips non-HTTP methods in spec', async () => {
229
+ const spec = {
230
+ paths: {
231
+ '/api/tasks': {
232
+ 'x-custom': { responses: {} },
233
+ parameters: [],
234
+ },
235
+ },
236
+ };
237
+ mockReadFile.mockResolvedValue(JSON.stringify(spec));
238
+
239
+ const agent = createAgent();
240
+ const result = await agent.run();
241
+
242
+ expect(result.status).toBeDefined();
243
+ });
244
+
245
+ it('records evidence with spec path', async () => {
246
+ const spec = { paths: {} };
247
+ mockReadFile.mockResolvedValue(JSON.stringify(spec));
248
+
249
+ const agent = createAgent();
250
+ const result = await agent.run();
251
+
252
+ expect(result.evidence.some(e => e.description.includes('OpenAPI spec'))).toBe(true);
253
+ });
254
+
255
+ it('handles spec with empty paths', async () => {
256
+ const spec = { paths: {} };
257
+ mockReadFile.mockResolvedValue(JSON.stringify(spec));
258
+
259
+ const agent = createAgent();
260
+ const result = await agent.run();
261
+
262
+ const noSpec = result.findings.find(f => f.id.includes('no-spec'));
263
+ expect(noSpec).toBeUndefined();
264
+ });
265
+
266
+ it('treats integer type as number for validation', async () => {
267
+ const spec = {
268
+ paths: {
269
+ '/api/tasks': {
270
+ get: {
271
+ responses: {
272
+ '200': {
273
+ content: {
274
+ 'application/json': {
275
+ schema: {
276
+ type: 'object',
277
+ properties: { count: { type: 'integer' } },
278
+ },
279
+ },
280
+ },
281
+ },
282
+ },
283
+ },
284
+ },
285
+ },
286
+ };
287
+ mockReadFile.mockResolvedValue(JSON.stringify(spec));
288
+
289
+ fetchMock.mockImplementation((url: string, opts?: Record<string, unknown>) => {
290
+ if (opts?.method === 'HEAD') return Promise.resolve({ ok: true, status: 200 });
291
+ return Promise.resolve({
292
+ ok: true,
293
+ status: 200,
294
+ headers: new Map([['content-type', 'application/json']]),
295
+ json: () => Promise.resolve({ count: 42 }),
296
+ });
297
+ });
298
+
299
+ const agent = createAgent();
300
+ const result = await agent.run();
301
+
302
+ const wrongType = result.findings.find(f => f.id.includes('wrong-type'));
303
+ expect(wrongType).toBeUndefined();
304
+ });
305
+
306
+ it('resolves environment in preFlight', async () => {
307
+ mockReadFile.mockRejectedValue(new Error('ENOENT'));
308
+ const agent = createAgent();
309
+ const result = await agent.run();
310
+ expect(result.findings.every(f => !f.description.includes('preFlight'))).toBe(true);
311
+ });
312
+ });
@@ -0,0 +1,192 @@
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 helpers
7
+ // ---------------------------------------------------------------------------
8
+
9
+ function makeConfig(overrides: Partial<ValidatedConfig> = {}): ValidatedConfig {
10
+ return {
11
+ schemaVersion: 1,
12
+ name: 'test',
13
+ auth: { loginUrl: 'http://localhost/login', credentials: {} },
14
+ modules: [{ id: 'tasks', route: '/tasks', sidebarIcon: true }],
15
+ environments: {
16
+ alpha: { baseUrl: 'http://localhost:3000', seed: false },
17
+ },
18
+ portals: [],
19
+ breakpoints: [],
20
+ integrations: [],
21
+ workers: [],
22
+ workflows: [],
23
+ accuracy: { enabled: false, decimalPrecision: 2, currencySymbol: '$', timezone: 'UTC' },
24
+ tenancy: { enabled: false, isolationField: 'firmId', testFirms: [] },
25
+ costBudget: { maxPerRun: 100, maxPerAgent: 10, alertThreshold: 0.8, onExceeded: 'abort' },
26
+ agentClassification: { blocking: [], advisory: [40] },
27
+ thresholds: {
28
+ unitCoverage: { lines: 80, functions: 80, branches: 80, statements: 80 },
29
+ lighthouse: { accessibility: 90, performance: 90, bestPractices: 90, seo: 90 },
30
+ apiResponseTime: 2000,
31
+ bundleSizeLimit: 500000,
32
+ maxConsoleErrors: 0,
33
+ },
34
+ disabledAgents: [],
35
+ disabledAgentSet: new Set<number>(),
36
+ ...overrides,
37
+ };
38
+ }
39
+
40
+ // ---------------------------------------------------------------------------
41
+ // Mock fetch
42
+ // ---------------------------------------------------------------------------
43
+
44
+ const fetchMock = vi.fn();
45
+ vi.stubGlobal('fetch', fetchMock);
46
+
47
+ // ---------------------------------------------------------------------------
48
+ // Dynamic import AFTER mocks
49
+ // ---------------------------------------------------------------------------
50
+
51
+ const { RateLimitTesterAgent } = await import('../40-rate-limit-tester');
52
+
53
+ function createAgent(
54
+ config: ValidatedConfig = makeConfig(),
55
+ phase: Phase = 'beta',
56
+ ): InstanceType<typeof RateLimitTesterAgent> {
57
+ return new RateLimitTesterAgent(config, phase, '/tmp/test-run');
58
+ }
59
+
60
+ // ---------------------------------------------------------------------------
61
+ // Tests
62
+ // ---------------------------------------------------------------------------
63
+
64
+ describe('RateLimitTesterAgent', () => {
65
+ beforeEach(() => {
66
+ fetchMock.mockReset().mockResolvedValue({ ok: true, status: 200 });
67
+ });
68
+
69
+ it('has agentId 40 and correct agentName', () => {
70
+ const agent = createAgent();
71
+ expect(agent.agentId).toBe(40);
72
+ expect(agent.agentName).toBe('Rate Limit Tester');
73
+ });
74
+
75
+ it('produces medium finding when no 429 received', async () => {
76
+ fetchMock.mockResolvedValue({ ok: true, status: 200 });
77
+
78
+ const agent = createAgent();
79
+ const result = await agent.run();
80
+
81
+ const noRateLimit = result.findings.find(f => f.id.includes('no-rate-limit'));
82
+ expect(noRateLimit).toBeDefined();
83
+ expect(noRateLimit!.severity).toBe('medium');
84
+ });
85
+
86
+ it('does not flag when 429 is received', async () => {
87
+ let callCount = 0;
88
+ fetchMock.mockImplementation(() => {
89
+ callCount++;
90
+ if (callCount <= 1) return Promise.resolve({ ok: true, status: 200 }); // env check
91
+ if (callCount <= 30) return Promise.resolve({ ok: true, status: 200 });
92
+ return Promise.resolve({ ok: false, status: 429 });
93
+ });
94
+
95
+ const agent = createAgent();
96
+ const result = await agent.run();
97
+
98
+ const noRateLimit = result.findings.find(f => f.id.includes('no-rate-limit'));
99
+ expect(noRateLimit).toBeUndefined();
100
+ });
101
+
102
+ it('sends 50 rapid requests per endpoint', async () => {
103
+ const config = makeConfig({
104
+ modules: [{ id: 'test', route: '/test', sidebarIcon: true }],
105
+ });
106
+
107
+ const agent = createAgent(config);
108
+ await agent.run();
109
+
110
+ // env check + 50 requests
111
+ expect(fetchMock.mock.calls.length).toBeGreaterThanOrEqual(50);
112
+ });
113
+
114
+ it('uses module routes for endpoints', async () => {
115
+ const config = makeConfig({
116
+ modules: [{ id: 'invoices', route: '/invoices', sidebarIcon: true }],
117
+ });
118
+ fetchMock.mockResolvedValue({ ok: true, status: 200 });
119
+
120
+ const agent = createAgent(config);
121
+ await agent.run();
122
+
123
+ const invoiceCalls = fetchMock.mock.calls.filter(
124
+ (call: unknown[]) => typeof call[0] === 'string' && (call[0] as string).includes('/api/invoices'),
125
+ );
126
+ expect(invoiceCalls.length).toBeGreaterThan(0);
127
+ });
128
+
129
+ it('falls back to default endpoints when no modules', async () => {
130
+ const config = makeConfig({ modules: undefined as unknown as ValidatedConfig['modules'] });
131
+ fetchMock.mockResolvedValue({ ok: true, status: 200 });
132
+
133
+ const agent = createAgent(config);
134
+ await agent.run();
135
+
136
+ const authCalls = fetchMock.mock.calls.filter(
137
+ (call: unknown[]) => typeof call[0] === 'string' && (call[0] as string).includes('/api/auth/login'),
138
+ );
139
+ expect(authCalls.length).toBeGreaterThan(0);
140
+ });
141
+
142
+ it('handles fetch errors gracefully', async () => {
143
+ fetchMock
144
+ .mockResolvedValueOnce({ ok: true, status: 200 }) // env check
145
+ .mockRejectedValue(new Error('Connection refused'));
146
+
147
+ const agent = createAgent();
148
+ const result = await agent.run();
149
+
150
+ // Should not crash
151
+ expect(result.status).toBeDefined();
152
+ });
153
+
154
+ it('records evidence for each endpoint', async () => {
155
+ fetchMock.mockResolvedValue({ ok: true, status: 200 });
156
+
157
+ const agent = createAgent();
158
+ const result = await agent.run();
159
+
160
+ expect(result.evidence.length).toBeGreaterThan(0);
161
+ expect(result.evidence.some(e => e.description.includes('Rate limit test'))).toBe(true);
162
+ });
163
+
164
+ it('resolves environment in preFlight', async () => {
165
+ const agent = createAgent();
166
+ const result = await agent.run();
167
+ expect(result.findings.every(f => !f.description.includes('preFlight'))).toBe(true);
168
+ });
169
+
170
+ it('uses GET method for rate limit testing', async () => {
171
+ fetchMock.mockResolvedValue({ ok: true, status: 200 });
172
+
173
+ const agent = createAgent();
174
+ await agent.run();
175
+
176
+ const getCalls = fetchMock.mock.calls.filter((call: unknown[]) => {
177
+ const opts = call[1] as Record<string, unknown> | undefined;
178
+ return opts?.method === 'GET';
179
+ });
180
+ expect(getCalls.length).toBeGreaterThan(0);
181
+ });
182
+
183
+ it('includes endpoint name in finding description', async () => {
184
+ fetchMock.mockResolvedValue({ ok: true, status: 200 });
185
+
186
+ const agent = createAgent();
187
+ const result = await agent.run();
188
+
189
+ const noRateLimit = result.findings.find(f => f.id.includes('no-rate-limit'));
190
+ expect(noRateLimit?.description).toContain('/api/tasks');
191
+ });
192
+ });
@@ -0,0 +1,198 @@
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 helpers
7
+ // ---------------------------------------------------------------------------
8
+
9
+ function makeConfig(overrides: Partial<ValidatedConfig> = {}): ValidatedConfig {
10
+ return {
11
+ schemaVersion: 1,
12
+ name: 'test',
13
+ auth: { loginUrl: 'http://localhost/login', credentials: {} },
14
+ modules: [{ id: 'tasks', route: '/tasks', sidebarIcon: true }],
15
+ environments: {
16
+ alpha: { baseUrl: 'http://localhost:3000', seed: false },
17
+ },
18
+ portals: [],
19
+ breakpoints: [],
20
+ integrations: [],
21
+ workers: [],
22
+ workflows: [],
23
+ accuracy: { enabled: false, decimalPrecision: 2, currencySymbol: '$', timezone: 'UTC' },
24
+ tenancy: { enabled: false, isolationField: 'firmId', testFirms: [] },
25
+ costBudget: { maxPerRun: 100, maxPerAgent: 10, alertThreshold: 0.8, onExceeded: 'abort' },
26
+ agentClassification: { blocking: [], advisory: [41] },
27
+ thresholds: {
28
+ unitCoverage: { lines: 80, functions: 80, branches: 80, statements: 80 },
29
+ lighthouse: { accessibility: 90, performance: 90, bestPractices: 90, seo: 90 },
30
+ apiResponseTime: 2000,
31
+ bundleSizeLimit: 500000,
32
+ maxConsoleErrors: 0,
33
+ },
34
+ disabledAgents: [],
35
+ disabledAgentSet: new Set<number>(),
36
+ ...overrides,
37
+ };
38
+ }
39
+
40
+ // ---------------------------------------------------------------------------
41
+ // Mock fetch
42
+ // ---------------------------------------------------------------------------
43
+
44
+ const fetchMock = vi.fn();
45
+ vi.stubGlobal('fetch', fetchMock);
46
+
47
+ // ---------------------------------------------------------------------------
48
+ // Dynamic import AFTER mocks
49
+ // ---------------------------------------------------------------------------
50
+
51
+ const { ApiPaginationTesterAgent } = await import('../41-api-pagination-tester');
52
+
53
+ function createAgent(
54
+ config: ValidatedConfig = makeConfig(),
55
+ phase: Phase = 'beta',
56
+ ): InstanceType<typeof ApiPaginationTesterAgent> {
57
+ return new ApiPaginationTesterAgent(config, phase, '/tmp/test-run');
58
+ }
59
+
60
+ // ---------------------------------------------------------------------------
61
+ // Tests
62
+ // ---------------------------------------------------------------------------
63
+
64
+ describe('ApiPaginationTesterAgent', () => {
65
+ beforeEach(() => {
66
+ fetchMock.mockReset().mockResolvedValue({ ok: true, status: 200 });
67
+ });
68
+
69
+ it('has agentId 41 and correct agentName', () => {
70
+ const agent = createAgent();
71
+ expect(agent.agentId).toBe(41);
72
+ expect(agent.agentName).toBe('API Pagination Tester');
73
+ });
74
+
75
+ it('flags server errors (5xx) on edge-case pagination', async () => {
76
+ fetchMock
77
+ .mockResolvedValueOnce({ ok: true, status: 200 }) // env check
78
+ .mockResolvedValue({ ok: false, status: 500 });
79
+
80
+ const agent = createAgent();
81
+ const result = await agent.run();
82
+
83
+ const crashFinding = result.findings.find(f => f.id.includes('crash'));
84
+ expect(crashFinding).toBeDefined();
85
+ expect(crashFinding!.severity).toBe('high');
86
+ });
87
+
88
+ it('does not flag successful responses', async () => {
89
+ fetchMock.mockResolvedValue({ ok: true, status: 200 });
90
+
91
+ const agent = createAgent();
92
+ const result = await agent.run();
93
+
94
+ const crashFindings = result.findings.filter(f => f.id.includes('crash'));
95
+ expect(crashFindings.length).toBe(0);
96
+ });
97
+
98
+ it('does not flag 400 responses (proper validation)', async () => {
99
+ fetchMock
100
+ .mockResolvedValueOnce({ ok: true, status: 200 }) // env check
101
+ .mockResolvedValue({ ok: false, status: 400 });
102
+
103
+ const agent = createAgent();
104
+ const result = await agent.run();
105
+
106
+ const crashFindings = result.findings.filter(f => f.id.includes('crash'));
107
+ expect(crashFindings.length).toBe(0);
108
+ });
109
+
110
+ it('tests 5 pagination edge cases per endpoint', async () => {
111
+ const config = makeConfig({
112
+ modules: [{ id: 'test', route: '/test', sidebarIcon: true }],
113
+ });
114
+ fetchMock.mockResolvedValue({ ok: true, status: 200 });
115
+
116
+ const agent = createAgent(config);
117
+ await agent.run();
118
+
119
+ // env check + 5 pagination tests
120
+ const queryCalls = fetchMock.mock.calls.filter(
121
+ (call: unknown[]) => typeof call[0] === 'string' && (call[0] as string).includes('?'),
122
+ );
123
+ expect(queryCalls.length).toBe(5);
124
+ });
125
+
126
+ it('tests page=0, page=-1, page=99999, limit=0, limit=10000', async () => {
127
+ fetchMock.mockResolvedValue({ ok: true, status: 200 });
128
+
129
+ const agent = createAgent();
130
+ await agent.run();
131
+
132
+ const urls = fetchMock.mock.calls
133
+ .map((call: unknown[]) => call[0] as string)
134
+ .filter((u: string) => u.includes('?'));
135
+
136
+ expect(urls.some((u: string) => u.includes('page=0'))).toBe(true);
137
+ expect(urls.some((u: string) => u.includes('page=-1'))).toBe(true);
138
+ expect(urls.some((u: string) => u.includes('page=99999'))).toBe(true);
139
+ expect(urls.some((u: string) => u.includes('limit=0'))).toBe(true);
140
+ expect(urls.some((u: string) => u.includes('limit=10000'))).toBe(true);
141
+ });
142
+
143
+ it('handles fetch errors gracefully', async () => {
144
+ fetchMock
145
+ .mockResolvedValueOnce({ ok: true, status: 200 }) // env check
146
+ .mockRejectedValue(new Error('Network error'));
147
+
148
+ const agent = createAgent();
149
+ const result = await agent.run();
150
+
151
+ const errorFinding = result.findings.find(f => f.id.includes('error'));
152
+ expect(errorFinding).toBeDefined();
153
+ expect(errorFinding!.severity).toBe('low');
154
+ });
155
+
156
+ it('records evidence for each test', async () => {
157
+ fetchMock.mockResolvedValue({ ok: true, status: 200 });
158
+
159
+ const agent = createAgent();
160
+ const result = await agent.run();
161
+
162
+ expect(result.evidence.length).toBeGreaterThan(0);
163
+ });
164
+
165
+ it('uses module routes for list endpoints', async () => {
166
+ const config = makeConfig({
167
+ modules: [{ id: 'invoices', route: '/invoices', sidebarIcon: true }],
168
+ });
169
+ fetchMock.mockResolvedValue({ ok: true, status: 200 });
170
+
171
+ const agent = createAgent(config);
172
+ await agent.run();
173
+
174
+ const invoiceCalls = fetchMock.mock.calls.filter(
175
+ (call: unknown[]) => typeof call[0] === 'string' && (call[0] as string).includes('/api/invoices'),
176
+ );
177
+ expect(invoiceCalls.length).toBeGreaterThan(0);
178
+ });
179
+
180
+ it('falls back to defaults when no modules', async () => {
181
+ const config = makeConfig({ modules: undefined as unknown as ValidatedConfig['modules'] });
182
+ fetchMock.mockResolvedValue({ ok: true, status: 200 });
183
+
184
+ const agent = createAgent(config);
185
+ await agent.run();
186
+
187
+ const tasksCalls = fetchMock.mock.calls.filter(
188
+ (call: unknown[]) => typeof call[0] === 'string' && (call[0] as string).includes('/api/tasks'),
189
+ );
190
+ expect(tasksCalls.length).toBeGreaterThan(0);
191
+ });
192
+
193
+ it('resolves environment in preFlight', async () => {
194
+ const agent = createAgent();
195
+ const result = await agent.run();
196
+ expect(result.findings.every(f => !f.description.includes('preFlight'))).toBe(true);
197
+ });
198
+ });