@dotsetlabs/bellwether 0.10.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 (403) hide show
  1. package/CHANGELOG.md +291 -0
  2. package/LICENSE +21 -0
  3. package/README.md +739 -0
  4. package/dist/auth/credentials.d.ts +64 -0
  5. package/dist/auth/credentials.js +218 -0
  6. package/dist/auth/index.d.ts +6 -0
  7. package/dist/auth/index.js +6 -0
  8. package/dist/auth/keychain.d.ts +64 -0
  9. package/dist/auth/keychain.js +268 -0
  10. package/dist/baseline/ab-testing.d.ts +80 -0
  11. package/dist/baseline/ab-testing.js +236 -0
  12. package/dist/baseline/ai-compatibility-scorer.d.ts +95 -0
  13. package/dist/baseline/ai-compatibility-scorer.js +606 -0
  14. package/dist/baseline/calibration.d.ts +77 -0
  15. package/dist/baseline/calibration.js +136 -0
  16. package/dist/baseline/category-matching.d.ts +85 -0
  17. package/dist/baseline/category-matching.js +289 -0
  18. package/dist/baseline/change-impact-analyzer.d.ts +98 -0
  19. package/dist/baseline/change-impact-analyzer.js +592 -0
  20. package/dist/baseline/comparator.d.ts +64 -0
  21. package/dist/baseline/comparator.js +916 -0
  22. package/dist/baseline/confidence.d.ts +55 -0
  23. package/dist/baseline/confidence.js +122 -0
  24. package/dist/baseline/converter.d.ts +61 -0
  25. package/dist/baseline/converter.js +585 -0
  26. package/dist/baseline/dependency-analyzer.d.ts +89 -0
  27. package/dist/baseline/dependency-analyzer.js +567 -0
  28. package/dist/baseline/deprecation-tracker.d.ts +133 -0
  29. package/dist/baseline/deprecation-tracker.js +322 -0
  30. package/dist/baseline/diff.d.ts +55 -0
  31. package/dist/baseline/diff.js +1584 -0
  32. package/dist/baseline/documentation-scorer.d.ts +205 -0
  33. package/dist/baseline/documentation-scorer.js +466 -0
  34. package/dist/baseline/embeddings.d.ts +118 -0
  35. package/dist/baseline/embeddings.js +251 -0
  36. package/dist/baseline/error-analyzer.d.ts +198 -0
  37. package/dist/baseline/error-analyzer.js +721 -0
  38. package/dist/baseline/evaluation/evaluator.d.ts +42 -0
  39. package/dist/baseline/evaluation/evaluator.js +323 -0
  40. package/dist/baseline/evaluation/expanded-dataset.d.ts +45 -0
  41. package/dist/baseline/evaluation/expanded-dataset.js +1164 -0
  42. package/dist/baseline/evaluation/golden-dataset.d.ts +58 -0
  43. package/dist/baseline/evaluation/golden-dataset.js +717 -0
  44. package/dist/baseline/evaluation/index.d.ts +15 -0
  45. package/dist/baseline/evaluation/index.js +15 -0
  46. package/dist/baseline/evaluation/types.d.ts +186 -0
  47. package/dist/baseline/evaluation/types.js +8 -0
  48. package/dist/baseline/external-dependency-detector.d.ts +181 -0
  49. package/dist/baseline/external-dependency-detector.js +524 -0
  50. package/dist/baseline/golden-output.d.ts +162 -0
  51. package/dist/baseline/golden-output.js +636 -0
  52. package/dist/baseline/health-scorer.d.ts +174 -0
  53. package/dist/baseline/health-scorer.js +451 -0
  54. package/dist/baseline/incremental-checker.d.ts +97 -0
  55. package/dist/baseline/incremental-checker.js +174 -0
  56. package/dist/baseline/index.d.ts +31 -0
  57. package/dist/baseline/index.js +42 -0
  58. package/dist/baseline/migration-generator.d.ts +137 -0
  59. package/dist/baseline/migration-generator.js +554 -0
  60. package/dist/baseline/migrations.d.ts +60 -0
  61. package/dist/baseline/migrations.js +197 -0
  62. package/dist/baseline/performance-tracker.d.ts +214 -0
  63. package/dist/baseline/performance-tracker.js +577 -0
  64. package/dist/baseline/pr-comment-generator.d.ts +117 -0
  65. package/dist/baseline/pr-comment-generator.js +546 -0
  66. package/dist/baseline/response-fingerprint.d.ts +127 -0
  67. package/dist/baseline/response-fingerprint.js +728 -0
  68. package/dist/baseline/response-schema-tracker.d.ts +129 -0
  69. package/dist/baseline/response-schema-tracker.js +420 -0
  70. package/dist/baseline/risk-scorer.d.ts +54 -0
  71. package/dist/baseline/risk-scorer.js +434 -0
  72. package/dist/baseline/saver.d.ts +89 -0
  73. package/dist/baseline/saver.js +554 -0
  74. package/dist/baseline/scenario-generator.d.ts +151 -0
  75. package/dist/baseline/scenario-generator.js +905 -0
  76. package/dist/baseline/schema-compare.d.ts +86 -0
  77. package/dist/baseline/schema-compare.js +557 -0
  78. package/dist/baseline/schema-evolution.d.ts +189 -0
  79. package/dist/baseline/schema-evolution.js +467 -0
  80. package/dist/baseline/semantic.d.ts +203 -0
  81. package/dist/baseline/semantic.js +908 -0
  82. package/dist/baseline/synonyms.d.ts +60 -0
  83. package/dist/baseline/synonyms.js +386 -0
  84. package/dist/baseline/telemetry.d.ts +165 -0
  85. package/dist/baseline/telemetry.js +294 -0
  86. package/dist/baseline/test-pruner.d.ts +120 -0
  87. package/dist/baseline/test-pruner.js +387 -0
  88. package/dist/baseline/types.d.ts +449 -0
  89. package/dist/baseline/types.js +5 -0
  90. package/dist/baseline/version.d.ts +138 -0
  91. package/dist/baseline/version.js +206 -0
  92. package/dist/cache/index.d.ts +5 -0
  93. package/dist/cache/index.js +5 -0
  94. package/dist/cache/response-cache.d.ts +151 -0
  95. package/dist/cache/response-cache.js +287 -0
  96. package/dist/ci/index.d.ts +60 -0
  97. package/dist/ci/index.js +342 -0
  98. package/dist/cli/commands/auth.d.ts +12 -0
  99. package/dist/cli/commands/auth.js +352 -0
  100. package/dist/cli/commands/badge.d.ts +3 -0
  101. package/dist/cli/commands/badge.js +74 -0
  102. package/dist/cli/commands/baseline-accept.d.ts +15 -0
  103. package/dist/cli/commands/baseline-accept.js +178 -0
  104. package/dist/cli/commands/baseline-migrate.d.ts +12 -0
  105. package/dist/cli/commands/baseline-migrate.js +164 -0
  106. package/dist/cli/commands/baseline.d.ts +14 -0
  107. package/dist/cli/commands/baseline.js +449 -0
  108. package/dist/cli/commands/beta.d.ts +10 -0
  109. package/dist/cli/commands/beta.js +231 -0
  110. package/dist/cli/commands/check.d.ts +11 -0
  111. package/dist/cli/commands/check.js +820 -0
  112. package/dist/cli/commands/cloud/badge.d.ts +3 -0
  113. package/dist/cli/commands/cloud/badge.js +74 -0
  114. package/dist/cli/commands/cloud/diff.d.ts +6 -0
  115. package/dist/cli/commands/cloud/diff.js +79 -0
  116. package/dist/cli/commands/cloud/history.d.ts +6 -0
  117. package/dist/cli/commands/cloud/history.js +102 -0
  118. package/dist/cli/commands/cloud/link.d.ts +9 -0
  119. package/dist/cli/commands/cloud/link.js +119 -0
  120. package/dist/cli/commands/cloud/login.d.ts +7 -0
  121. package/dist/cli/commands/cloud/login.js +499 -0
  122. package/dist/cli/commands/cloud/projects.d.ts +6 -0
  123. package/dist/cli/commands/cloud/projects.js +44 -0
  124. package/dist/cli/commands/cloud/shared.d.ts +7 -0
  125. package/dist/cli/commands/cloud/shared.js +42 -0
  126. package/dist/cli/commands/cloud/teams.d.ts +8 -0
  127. package/dist/cli/commands/cloud/teams.js +169 -0
  128. package/dist/cli/commands/cloud/upload.d.ts +8 -0
  129. package/dist/cli/commands/cloud/upload.js +181 -0
  130. package/dist/cli/commands/contract.d.ts +11 -0
  131. package/dist/cli/commands/contract.js +280 -0
  132. package/dist/cli/commands/discover.d.ts +3 -0
  133. package/dist/cli/commands/discover.js +82 -0
  134. package/dist/cli/commands/eval.d.ts +9 -0
  135. package/dist/cli/commands/eval.js +187 -0
  136. package/dist/cli/commands/explore.d.ts +11 -0
  137. package/dist/cli/commands/explore.js +437 -0
  138. package/dist/cli/commands/feedback.d.ts +9 -0
  139. package/dist/cli/commands/feedback.js +174 -0
  140. package/dist/cli/commands/golden.d.ts +12 -0
  141. package/dist/cli/commands/golden.js +407 -0
  142. package/dist/cli/commands/history.d.ts +10 -0
  143. package/dist/cli/commands/history.js +202 -0
  144. package/dist/cli/commands/init.d.ts +9 -0
  145. package/dist/cli/commands/init.js +219 -0
  146. package/dist/cli/commands/interview.d.ts +3 -0
  147. package/dist/cli/commands/interview.js +903 -0
  148. package/dist/cli/commands/link.d.ts +10 -0
  149. package/dist/cli/commands/link.js +169 -0
  150. package/dist/cli/commands/login.d.ts +7 -0
  151. package/dist/cli/commands/login.js +499 -0
  152. package/dist/cli/commands/preset.d.ts +33 -0
  153. package/dist/cli/commands/preset.js +297 -0
  154. package/dist/cli/commands/profile.d.ts +33 -0
  155. package/dist/cli/commands/profile.js +286 -0
  156. package/dist/cli/commands/registry.d.ts +11 -0
  157. package/dist/cli/commands/registry.js +146 -0
  158. package/dist/cli/commands/shared.d.ts +79 -0
  159. package/dist/cli/commands/shared.js +196 -0
  160. package/dist/cli/commands/teams.d.ts +8 -0
  161. package/dist/cli/commands/teams.js +169 -0
  162. package/dist/cli/commands/test.d.ts +9 -0
  163. package/dist/cli/commands/test.js +500 -0
  164. package/dist/cli/commands/upload.d.ts +8 -0
  165. package/dist/cli/commands/upload.js +223 -0
  166. package/dist/cli/commands/validate-config.d.ts +6 -0
  167. package/dist/cli/commands/validate-config.js +35 -0
  168. package/dist/cli/commands/verify.d.ts +11 -0
  169. package/dist/cli/commands/verify.js +283 -0
  170. package/dist/cli/commands/watch.d.ts +12 -0
  171. package/dist/cli/commands/watch.js +253 -0
  172. package/dist/cli/index.d.ts +3 -0
  173. package/dist/cli/index.js +178 -0
  174. package/dist/cli/interactive.d.ts +47 -0
  175. package/dist/cli/interactive.js +216 -0
  176. package/dist/cli/output/terminal-reporter.d.ts +19 -0
  177. package/dist/cli/output/terminal-reporter.js +104 -0
  178. package/dist/cli/output.d.ts +226 -0
  179. package/dist/cli/output.js +438 -0
  180. package/dist/cli/utils/env.d.ts +5 -0
  181. package/dist/cli/utils/env.js +14 -0
  182. package/dist/cli/utils/progress.d.ts +59 -0
  183. package/dist/cli/utils/progress.js +206 -0
  184. package/dist/cli/utils/server-context.d.ts +10 -0
  185. package/dist/cli/utils/server-context.js +36 -0
  186. package/dist/cloud/auth.d.ts +144 -0
  187. package/dist/cloud/auth.js +374 -0
  188. package/dist/cloud/client.d.ts +24 -0
  189. package/dist/cloud/client.js +65 -0
  190. package/dist/cloud/http-client.d.ts +38 -0
  191. package/dist/cloud/http-client.js +215 -0
  192. package/dist/cloud/index.d.ts +23 -0
  193. package/dist/cloud/index.js +25 -0
  194. package/dist/cloud/mock-client.d.ts +107 -0
  195. package/dist/cloud/mock-client.js +545 -0
  196. package/dist/cloud/types.d.ts +515 -0
  197. package/dist/cloud/types.js +15 -0
  198. package/dist/config/defaults.d.ts +160 -0
  199. package/dist/config/defaults.js +169 -0
  200. package/dist/config/loader.d.ts +24 -0
  201. package/dist/config/loader.js +122 -0
  202. package/dist/config/template.d.ts +42 -0
  203. package/dist/config/template.js +647 -0
  204. package/dist/config/validator.d.ts +2112 -0
  205. package/dist/config/validator.js +658 -0
  206. package/dist/constants/cloud.d.ts +107 -0
  207. package/dist/constants/cloud.js +110 -0
  208. package/dist/constants/core.d.ts +521 -0
  209. package/dist/constants/core.js +556 -0
  210. package/dist/constants/testing.d.ts +1283 -0
  211. package/dist/constants/testing.js +1568 -0
  212. package/dist/constants.d.ts +10 -0
  213. package/dist/constants.js +10 -0
  214. package/dist/contract/index.d.ts +6 -0
  215. package/dist/contract/index.js +5 -0
  216. package/dist/contract/validator.d.ts +177 -0
  217. package/dist/contract/validator.js +574 -0
  218. package/dist/cost/index.d.ts +6 -0
  219. package/dist/cost/index.js +5 -0
  220. package/dist/cost/tracker.d.ts +134 -0
  221. package/dist/cost/tracker.js +313 -0
  222. package/dist/discovery/discovery.d.ts +16 -0
  223. package/dist/discovery/discovery.js +173 -0
  224. package/dist/discovery/types.d.ts +51 -0
  225. package/dist/discovery/types.js +2 -0
  226. package/dist/docs/agents.d.ts +3 -0
  227. package/dist/docs/agents.js +995 -0
  228. package/dist/docs/contract.d.ts +51 -0
  229. package/dist/docs/contract.js +1681 -0
  230. package/dist/docs/generator.d.ts +4 -0
  231. package/dist/docs/generator.js +4 -0
  232. package/dist/docs/html-reporter.d.ts +9 -0
  233. package/dist/docs/html-reporter.js +757 -0
  234. package/dist/docs/index.d.ts +10 -0
  235. package/dist/docs/index.js +11 -0
  236. package/dist/docs/junit-reporter.d.ts +18 -0
  237. package/dist/docs/junit-reporter.js +210 -0
  238. package/dist/docs/report.d.ts +14 -0
  239. package/dist/docs/report.js +44 -0
  240. package/dist/docs/sarif-reporter.d.ts +19 -0
  241. package/dist/docs/sarif-reporter.js +335 -0
  242. package/dist/docs/shared.d.ts +35 -0
  243. package/dist/docs/shared.js +162 -0
  244. package/dist/docs/templates.d.ts +12 -0
  245. package/dist/docs/templates.js +76 -0
  246. package/dist/errors/index.d.ts +6 -0
  247. package/dist/errors/index.js +6 -0
  248. package/dist/errors/retry.d.ts +92 -0
  249. package/dist/errors/retry.js +323 -0
  250. package/dist/errors/types.d.ts +321 -0
  251. package/dist/errors/types.js +584 -0
  252. package/dist/index.d.ts +32 -0
  253. package/dist/index.js +32 -0
  254. package/dist/interview/dependency-resolver.d.ts +11 -0
  255. package/dist/interview/dependency-resolver.js +32 -0
  256. package/dist/interview/interviewer.d.ts +232 -0
  257. package/dist/interview/interviewer.js +1939 -0
  258. package/dist/interview/mock-response-generator.d.ts +7 -0
  259. package/dist/interview/mock-response-generator.js +102 -0
  260. package/dist/interview/orchestrator.d.ts +237 -0
  261. package/dist/interview/orchestrator.js +1296 -0
  262. package/dist/interview/rate-limiter.d.ts +15 -0
  263. package/dist/interview/rate-limiter.js +55 -0
  264. package/dist/interview/response-validator.d.ts +10 -0
  265. package/dist/interview/response-validator.js +132 -0
  266. package/dist/interview/schema-inferrer.d.ts +8 -0
  267. package/dist/interview/schema-inferrer.js +71 -0
  268. package/dist/interview/schema-test-generator.d.ts +71 -0
  269. package/dist/interview/schema-test-generator.js +834 -0
  270. package/dist/interview/smart-value-generator.d.ts +155 -0
  271. package/dist/interview/smart-value-generator.js +554 -0
  272. package/dist/interview/stateful-test-runner.d.ts +19 -0
  273. package/dist/interview/stateful-test-runner.js +106 -0
  274. package/dist/interview/types.d.ts +561 -0
  275. package/dist/interview/types.js +2 -0
  276. package/dist/llm/anthropic.d.ts +41 -0
  277. package/dist/llm/anthropic.js +355 -0
  278. package/dist/llm/client.d.ts +123 -0
  279. package/dist/llm/client.js +42 -0
  280. package/dist/llm/factory.d.ts +38 -0
  281. package/dist/llm/factory.js +145 -0
  282. package/dist/llm/fallback.d.ts +140 -0
  283. package/dist/llm/fallback.js +379 -0
  284. package/dist/llm/index.d.ts +18 -0
  285. package/dist/llm/index.js +15 -0
  286. package/dist/llm/ollama.d.ts +37 -0
  287. package/dist/llm/ollama.js +330 -0
  288. package/dist/llm/openai.d.ts +25 -0
  289. package/dist/llm/openai.js +320 -0
  290. package/dist/llm/token-budget.d.ts +161 -0
  291. package/dist/llm/token-budget.js +395 -0
  292. package/dist/logging/logger.d.ts +70 -0
  293. package/dist/logging/logger.js +130 -0
  294. package/dist/metrics/collector.d.ts +106 -0
  295. package/dist/metrics/collector.js +547 -0
  296. package/dist/metrics/index.d.ts +7 -0
  297. package/dist/metrics/index.js +7 -0
  298. package/dist/metrics/prometheus.d.ts +20 -0
  299. package/dist/metrics/prometheus.js +241 -0
  300. package/dist/metrics/types.d.ts +209 -0
  301. package/dist/metrics/types.js +5 -0
  302. package/dist/persona/builtins.d.ts +54 -0
  303. package/dist/persona/builtins.js +219 -0
  304. package/dist/persona/index.d.ts +8 -0
  305. package/dist/persona/index.js +8 -0
  306. package/dist/persona/loader.d.ts +30 -0
  307. package/dist/persona/loader.js +190 -0
  308. package/dist/persona/types.d.ts +144 -0
  309. package/dist/persona/types.js +5 -0
  310. package/dist/persona/validation.d.ts +94 -0
  311. package/dist/persona/validation.js +332 -0
  312. package/dist/prompts/index.d.ts +5 -0
  313. package/dist/prompts/index.js +5 -0
  314. package/dist/prompts/templates.d.ts +180 -0
  315. package/dist/prompts/templates.js +431 -0
  316. package/dist/registry/client.d.ts +49 -0
  317. package/dist/registry/client.js +191 -0
  318. package/dist/registry/index.d.ts +7 -0
  319. package/dist/registry/index.js +6 -0
  320. package/dist/registry/types.d.ts +140 -0
  321. package/dist/registry/types.js +6 -0
  322. package/dist/scenarios/evaluator.d.ts +43 -0
  323. package/dist/scenarios/evaluator.js +206 -0
  324. package/dist/scenarios/index.d.ts +10 -0
  325. package/dist/scenarios/index.js +9 -0
  326. package/dist/scenarios/loader.d.ts +20 -0
  327. package/dist/scenarios/loader.js +285 -0
  328. package/dist/scenarios/types.d.ts +153 -0
  329. package/dist/scenarios/types.js +8 -0
  330. package/dist/security/index.d.ts +17 -0
  331. package/dist/security/index.js +18 -0
  332. package/dist/security/payloads.d.ts +61 -0
  333. package/dist/security/payloads.js +268 -0
  334. package/dist/security/security-tester.d.ts +42 -0
  335. package/dist/security/security-tester.js +582 -0
  336. package/dist/security/types.d.ts +166 -0
  337. package/dist/security/types.js +8 -0
  338. package/dist/transport/base-transport.d.ts +59 -0
  339. package/dist/transport/base-transport.js +38 -0
  340. package/dist/transport/http-transport.d.ts +67 -0
  341. package/dist/transport/http-transport.js +238 -0
  342. package/dist/transport/mcp-client.d.ts +141 -0
  343. package/dist/transport/mcp-client.js +496 -0
  344. package/dist/transport/sse-transport.d.ts +88 -0
  345. package/dist/transport/sse-transport.js +316 -0
  346. package/dist/transport/stdio-transport.d.ts +43 -0
  347. package/dist/transport/stdio-transport.js +238 -0
  348. package/dist/transport/types.d.ts +125 -0
  349. package/dist/transport/types.js +16 -0
  350. package/dist/utils/concurrency.d.ts +123 -0
  351. package/dist/utils/concurrency.js +213 -0
  352. package/dist/utils/formatters.d.ts +16 -0
  353. package/dist/utils/formatters.js +37 -0
  354. package/dist/utils/index.d.ts +8 -0
  355. package/dist/utils/index.js +8 -0
  356. package/dist/utils/jsonpath.d.ts +87 -0
  357. package/dist/utils/jsonpath.js +326 -0
  358. package/dist/utils/markdown.d.ts +113 -0
  359. package/dist/utils/markdown.js +265 -0
  360. package/dist/utils/network.d.ts +14 -0
  361. package/dist/utils/network.js +17 -0
  362. package/dist/utils/sanitize.d.ts +92 -0
  363. package/dist/utils/sanitize.js +191 -0
  364. package/dist/utils/semantic.d.ts +194 -0
  365. package/dist/utils/semantic.js +1051 -0
  366. package/dist/utils/smart-truncate.d.ts +94 -0
  367. package/dist/utils/smart-truncate.js +361 -0
  368. package/dist/utils/timeout.d.ts +153 -0
  369. package/dist/utils/timeout.js +205 -0
  370. package/dist/utils/yaml-parser.d.ts +58 -0
  371. package/dist/utils/yaml-parser.js +86 -0
  372. package/dist/validation/index.d.ts +32 -0
  373. package/dist/validation/index.js +32 -0
  374. package/dist/validation/semantic-test-generator.d.ts +50 -0
  375. package/dist/validation/semantic-test-generator.js +176 -0
  376. package/dist/validation/semantic-types.d.ts +66 -0
  377. package/dist/validation/semantic-types.js +94 -0
  378. package/dist/validation/semantic-validator.d.ts +38 -0
  379. package/dist/validation/semantic-validator.js +340 -0
  380. package/dist/verification/index.d.ts +6 -0
  381. package/dist/verification/index.js +5 -0
  382. package/dist/verification/types.d.ts +133 -0
  383. package/dist/verification/types.js +5 -0
  384. package/dist/verification/verifier.d.ts +30 -0
  385. package/dist/verification/verifier.js +309 -0
  386. package/dist/version.d.ts +19 -0
  387. package/dist/version.js +48 -0
  388. package/dist/workflow/auto-generator.d.ts +27 -0
  389. package/dist/workflow/auto-generator.js +513 -0
  390. package/dist/workflow/discovery.d.ts +40 -0
  391. package/dist/workflow/discovery.js +195 -0
  392. package/dist/workflow/executor.d.ts +82 -0
  393. package/dist/workflow/executor.js +611 -0
  394. package/dist/workflow/index.d.ts +10 -0
  395. package/dist/workflow/index.js +10 -0
  396. package/dist/workflow/loader.d.ts +24 -0
  397. package/dist/workflow/loader.js +194 -0
  398. package/dist/workflow/state-tracker.d.ts +98 -0
  399. package/dist/workflow/state-tracker.js +424 -0
  400. package/dist/workflow/types.d.ts +337 -0
  401. package/dist/workflow/types.js +5 -0
  402. package/package.json +94 -0
  403. package/schemas/bellwether-check.schema.json +651 -0
@@ -0,0 +1,905 @@
1
+ /**
2
+ * Auto-generated test scenario generation from schema analysis.
3
+ *
4
+ * This module analyzes tool schemas and generates comprehensive test scenarios
5
+ * covering happy paths, edge cases, error handling, and security testing.
6
+ */
7
+ import { SCENARIO_GENERATION, ORCHESTRATOR } from '../constants.js';
8
+ /**
9
+ * Extract properties from a JSON schema.
10
+ */
11
+ function extractSchemaProperties(schema) {
12
+ const properties = [];
13
+ const schemaProps = schema.properties;
14
+ const required = schema.required || [];
15
+ if (!schemaProps) {
16
+ return properties;
17
+ }
18
+ for (const [name, prop] of Object.entries(schemaProps)) {
19
+ const property = {
20
+ name,
21
+ type: prop.type || 'unknown',
22
+ required: required.includes(name),
23
+ description: prop.description,
24
+ };
25
+ // Extract constraints
26
+ if (prop.enum)
27
+ property.enum = prop.enum;
28
+ if (typeof prop.minimum === 'number')
29
+ property.minimum = prop.minimum;
30
+ if (typeof prop.maximum === 'number')
31
+ property.maximum = prop.maximum;
32
+ if (typeof prop.minLength === 'number')
33
+ property.minLength = prop.minLength;
34
+ if (typeof prop.maxLength === 'number')
35
+ property.maxLength = prop.maxLength;
36
+ if (prop.pattern)
37
+ property.pattern = prop.pattern;
38
+ if (prop.format)
39
+ property.format = prop.format;
40
+ if (prop.default !== undefined)
41
+ property.default = prop.default;
42
+ // Handle array items
43
+ if (prop.type === 'array' && prop.items) {
44
+ const items = prop.items;
45
+ property.items = {
46
+ name: `${name}[]`,
47
+ type: items.type || 'unknown',
48
+ required: false,
49
+ };
50
+ }
51
+ properties.push(property);
52
+ }
53
+ return properties;
54
+ }
55
+ /**
56
+ * Generate a valid value for a schema property.
57
+ */
58
+ function generateValidValue(prop) {
59
+ // Use default if available
60
+ if (prop.default !== undefined) {
61
+ return prop.default;
62
+ }
63
+ // Use first enum value if available
64
+ if (prop.enum && prop.enum.length > 0) {
65
+ return prop.enum[0];
66
+ }
67
+ // Generate based on type
68
+ switch (prop.type) {
69
+ case 'string':
70
+ if (prop.format === 'email')
71
+ return 'test@example.com';
72
+ if (prop.format === 'uri' || prop.format === 'url')
73
+ return 'https://example.com';
74
+ if (prop.format === 'date')
75
+ return '2025-01-15';
76
+ if (prop.format === 'date-time')
77
+ return '2025-01-15T10:30:00Z';
78
+ if (prop.format === 'uuid')
79
+ return '550e8400-e29b-41d4-a716-446655440000';
80
+ if (prop.pattern)
81
+ return generateFromPattern(prop.pattern);
82
+ if (prop.minLength)
83
+ return 'a'.repeat(prop.minLength);
84
+ return 'test-value';
85
+ case 'number':
86
+ case 'integer':
87
+ if (prop.minimum !== undefined && prop.maximum !== undefined) {
88
+ return Math.floor((prop.minimum + prop.maximum) / 2);
89
+ }
90
+ if (prop.minimum !== undefined)
91
+ return prop.minimum;
92
+ if (prop.maximum !== undefined)
93
+ return Math.min(prop.maximum, 100);
94
+ return prop.type === 'integer' ? 42 : 42.5;
95
+ case 'boolean':
96
+ return true;
97
+ case 'array':
98
+ if (prop.items) {
99
+ return [generateValidValue(prop.items)];
100
+ }
101
+ return [];
102
+ case 'object':
103
+ return {};
104
+ default:
105
+ return null;
106
+ }
107
+ }
108
+ /**
109
+ * Generate a simple value matching a regex pattern.
110
+ */
111
+ function generateFromPattern(pattern) {
112
+ // Simple pattern matching for common cases
113
+ if (pattern.includes('[a-z]'))
114
+ return 'abc';
115
+ if (pattern.includes('[A-Z]'))
116
+ return 'ABC';
117
+ if (pattern.includes('[0-9]') || pattern.includes('\\d'))
118
+ return '123';
119
+ if (pattern.includes('@'))
120
+ return 'test@example.com';
121
+ return 'pattern-value';
122
+ }
123
+ /**
124
+ * Generate an invalid value for a schema property.
125
+ */
126
+ function generateInvalidValue(prop) {
127
+ switch (prop.type) {
128
+ case 'string':
129
+ return 12345; // Number instead of string
130
+ case 'number':
131
+ case 'integer':
132
+ return 'not-a-number';
133
+ case 'boolean':
134
+ return 'not-a-boolean';
135
+ case 'array':
136
+ return 'not-an-array';
137
+ case 'object':
138
+ return 'not-an-object';
139
+ default:
140
+ return Symbol('invalid');
141
+ }
142
+ }
143
+ /**
144
+ * Generate boundary values for a property.
145
+ */
146
+ function generateBoundaryValues(prop) {
147
+ const values = [];
148
+ switch (prop.type) {
149
+ case 'string':
150
+ if (prop.minLength !== undefined) {
151
+ // At minimum
152
+ values.push('a'.repeat(prop.minLength));
153
+ // Just below minimum (if > 0)
154
+ if (prop.minLength > 0) {
155
+ values.push('a'.repeat(prop.minLength - 1));
156
+ }
157
+ }
158
+ if (prop.maxLength !== undefined) {
159
+ // At maximum
160
+ values.push('a'.repeat(prop.maxLength));
161
+ // Just above maximum
162
+ values.push('a'.repeat(prop.maxLength + 1));
163
+ }
164
+ // Empty string
165
+ values.push('');
166
+ break;
167
+ case 'number':
168
+ case 'integer':
169
+ if (prop.minimum !== undefined) {
170
+ values.push(prop.minimum);
171
+ values.push(prop.minimum - 1);
172
+ }
173
+ if (prop.maximum !== undefined) {
174
+ values.push(prop.maximum);
175
+ values.push(prop.maximum + 1);
176
+ }
177
+ // Zero and negative
178
+ values.push(0);
179
+ values.push(-1);
180
+ // Very large number
181
+ values.push(Number.MAX_SAFE_INTEGER);
182
+ break;
183
+ case 'array':
184
+ values.push([]); // Empty array
185
+ values.push([generateValidValue(prop.items || { name: 'item', type: 'string', required: false })]); // Single item
186
+ break;
187
+ }
188
+ return values;
189
+ }
190
+ /**
191
+ * Generate a unique scenario ID using the provided counter state.
192
+ */
193
+ function generateScenarioId(toolName, category, counter) {
194
+ return `${toolName}-${category}-${++counter.value}`;
195
+ }
196
+ /**
197
+ * Generate happy path scenarios for a tool.
198
+ */
199
+ function generateHappyPathScenarios(tool, properties, maxScenarios, counter) {
200
+ const scenarios = [];
201
+ // Scenario 1: All required parameters with valid values
202
+ const requiredProps = properties.filter(p => p.required);
203
+ if (requiredProps.length > 0 || properties.length > 0) {
204
+ const input = {};
205
+ const targetProps = requiredProps.length > 0 ? requiredProps : properties.slice(0, 3);
206
+ for (const prop of targetProps) {
207
+ input[prop.name] = generateValidValue(prop);
208
+ }
209
+ scenarios.push({
210
+ id: generateScenarioId(tool.name, 'happy_path', counter),
211
+ toolName: tool.name,
212
+ category: 'happy_path',
213
+ description: `Basic usage with ${requiredProps.length > 0 ? 'required' : 'minimal'} parameters`,
214
+ input,
215
+ expectedBehavior: 'Tool should execute successfully and return valid response',
216
+ priority: 'critical',
217
+ tags: ['basic', 'smoke-test'],
218
+ rationale: 'Validates core functionality with minimal valid input',
219
+ });
220
+ }
221
+ // Scenario 2: All parameters with valid values
222
+ if (properties.length > requiredProps.length && scenarios.length < maxScenarios) {
223
+ const input = {};
224
+ for (const prop of properties) {
225
+ input[prop.name] = generateValidValue(prop);
226
+ }
227
+ scenarios.push({
228
+ id: generateScenarioId(tool.name, 'happy_path', counter),
229
+ toolName: tool.name,
230
+ category: 'happy_path',
231
+ description: 'Full usage with all parameters',
232
+ input,
233
+ expectedBehavior: 'Tool should execute successfully with all options',
234
+ priority: 'high',
235
+ tags: ['full', 'comprehensive'],
236
+ rationale: 'Validates all parameters work together correctly',
237
+ });
238
+ }
239
+ // Scenario 3-N: Test different enum values
240
+ for (const prop of properties) {
241
+ if (prop.enum && prop.enum.length > 1 && scenarios.length < maxScenarios) {
242
+ for (let i = 1; i < Math.min(prop.enum.length, ORCHESTRATOR.MAX_ENUM_TESTS + 1); i++) {
243
+ if (scenarios.length >= maxScenarios)
244
+ break;
245
+ const input = {};
246
+ // Set required params
247
+ for (const p of properties.filter(p => p.required && p.name !== prop.name)) {
248
+ input[p.name] = generateValidValue(p);
249
+ }
250
+ input[prop.name] = prop.enum[i];
251
+ scenarios.push({
252
+ id: generateScenarioId(tool.name, 'happy_path', counter),
253
+ toolName: tool.name,
254
+ category: 'happy_path',
255
+ description: `Test ${prop.name} with value: ${JSON.stringify(prop.enum[i])}`,
256
+ input,
257
+ expectedBehavior: `Tool should handle ${prop.name}=${JSON.stringify(prop.enum[i])} correctly`,
258
+ priority: 'medium',
259
+ tags: ['enum', prop.name],
260
+ rationale: `Validates enum value: ${JSON.stringify(prop.enum[i])}`,
261
+ targetParameter: prop.name,
262
+ });
263
+ }
264
+ }
265
+ }
266
+ // Scenario: Test with default values
267
+ const propsWithDefaults = properties.filter(p => p.default !== undefined);
268
+ if (propsWithDefaults.length > 0 && scenarios.length < maxScenarios) {
269
+ const input = {};
270
+ // Only set required params, let defaults apply
271
+ for (const prop of properties.filter(p => p.required)) {
272
+ input[prop.name] = generateValidValue(prop);
273
+ }
274
+ scenarios.push({
275
+ id: generateScenarioId(tool.name, 'happy_path', counter),
276
+ toolName: tool.name,
277
+ category: 'happy_path',
278
+ description: 'Test with default values applied',
279
+ input,
280
+ expectedBehavior: 'Tool should apply defaults correctly',
281
+ priority: 'medium',
282
+ tags: ['defaults'],
283
+ rationale: `Tests default value behavior for: ${propsWithDefaults.map(p => p.name).join(', ')}`,
284
+ });
285
+ }
286
+ return scenarios.slice(0, maxScenarios);
287
+ }
288
+ /**
289
+ * Generate edge case scenarios for a tool.
290
+ */
291
+ function generateEdgeCaseScenarios(tool, properties, maxScenarios, counter) {
292
+ const scenarios = [];
293
+ for (const prop of properties) {
294
+ if (scenarios.length >= maxScenarios)
295
+ break;
296
+ const boundaryValues = generateBoundaryValues(prop);
297
+ for (const value of boundaryValues) {
298
+ if (scenarios.length >= maxScenarios)
299
+ break;
300
+ const input = {};
301
+ // Set required params
302
+ for (const p of properties.filter(p => p.required && p.name !== prop.name)) {
303
+ input[p.name] = generateValidValue(p);
304
+ }
305
+ input[prop.name] = value;
306
+ const isInvalid = isBoundaryValueInvalid(prop, value);
307
+ scenarios.push({
308
+ id: generateScenarioId(tool.name, 'edge_cases', counter),
309
+ toolName: tool.name,
310
+ category: 'edge_cases',
311
+ description: `Boundary test: ${prop.name} = ${formatValue(value)}`,
312
+ input,
313
+ expectedBehavior: isInvalid
314
+ ? `Tool should reject or handle invalid ${prop.name} gracefully`
315
+ : `Tool should handle boundary value for ${prop.name}`,
316
+ priority: isInvalid ? 'high' : 'medium',
317
+ tags: ['boundary', prop.name, isInvalid ? 'invalid' : 'valid'],
318
+ rationale: `Tests ${prop.name} at boundary: ${formatValue(value)}`,
319
+ targetParameter: prop.name,
320
+ });
321
+ }
322
+ }
323
+ // Add empty object test
324
+ if (scenarios.length < maxScenarios) {
325
+ const requiredProps = properties.filter(p => p.required);
326
+ if (requiredProps.length === 0) {
327
+ scenarios.push({
328
+ id: generateScenarioId(tool.name, 'edge_cases', counter),
329
+ toolName: tool.name,
330
+ category: 'edge_cases',
331
+ description: 'Empty input (no parameters)',
332
+ input: {},
333
+ expectedBehavior: 'Tool should handle empty input gracefully',
334
+ priority: 'medium',
335
+ tags: ['empty', 'minimal'],
336
+ rationale: 'Tests behavior with no parameters provided',
337
+ });
338
+ }
339
+ }
340
+ // Add null/undefined tests for optional params
341
+ const optionalProps = properties.filter(p => !p.required);
342
+ for (const prop of optionalProps) {
343
+ if (scenarios.length >= maxScenarios)
344
+ break;
345
+ const input = {};
346
+ for (const p of properties.filter(p => p.required)) {
347
+ input[p.name] = generateValidValue(p);
348
+ }
349
+ input[prop.name] = null;
350
+ scenarios.push({
351
+ id: generateScenarioId(tool.name, 'edge_cases', counter),
352
+ toolName: tool.name,
353
+ category: 'edge_cases',
354
+ description: `Null value for optional: ${prop.name}`,
355
+ input,
356
+ expectedBehavior: `Tool should handle null ${prop.name} gracefully`,
357
+ priority: 'low',
358
+ tags: ['null', prop.name],
359
+ rationale: 'Tests null handling for optional parameter',
360
+ targetParameter: prop.name,
361
+ });
362
+ }
363
+ return scenarios.slice(0, maxScenarios);
364
+ }
365
+ /**
366
+ * Check if a boundary value is invalid according to the schema.
367
+ */
368
+ function isBoundaryValueInvalid(prop, value) {
369
+ if (value === null || value === undefined)
370
+ return true;
371
+ switch (prop.type) {
372
+ case 'string':
373
+ if (typeof value !== 'string')
374
+ return true;
375
+ if (prop.minLength !== undefined && value.length < prop.minLength)
376
+ return true;
377
+ if (prop.maxLength !== undefined && value.length > prop.maxLength)
378
+ return true;
379
+ break;
380
+ case 'number':
381
+ case 'integer':
382
+ if (typeof value !== 'number')
383
+ return true;
384
+ if (prop.minimum !== undefined && value < prop.minimum)
385
+ return true;
386
+ if (prop.maximum !== undefined && value > prop.maximum)
387
+ return true;
388
+ break;
389
+ case 'array':
390
+ if (!Array.isArray(value))
391
+ return true;
392
+ break;
393
+ }
394
+ return false;
395
+ }
396
+ /**
397
+ * Format a value for display.
398
+ */
399
+ function formatValue(value) {
400
+ if (value === null)
401
+ return 'null';
402
+ if (value === undefined)
403
+ return 'undefined';
404
+ if (typeof value === 'string') {
405
+ if (value.length > 30)
406
+ return `"${value.substring(0, 27)}..."`;
407
+ return `"${value}"`;
408
+ }
409
+ if (Array.isArray(value)) {
410
+ if (value.length === 0)
411
+ return '[]';
412
+ return `[${value.length} items]`;
413
+ }
414
+ return String(value);
415
+ }
416
+ /**
417
+ * Generate error case scenarios for a tool.
418
+ */
419
+ function generateErrorCaseScenarios(tool, properties, maxScenarios, counter) {
420
+ const scenarios = [];
421
+ // Test missing required parameters
422
+ const requiredProps = properties.filter(p => p.required);
423
+ for (const prop of requiredProps) {
424
+ if (scenarios.length >= maxScenarios)
425
+ break;
426
+ const input = {};
427
+ // Set all required except this one
428
+ for (const p of requiredProps.filter(p => p.name !== prop.name)) {
429
+ input[p.name] = generateValidValue(p);
430
+ }
431
+ scenarios.push({
432
+ id: generateScenarioId(tool.name, 'error_handling', counter),
433
+ toolName: tool.name,
434
+ category: 'error_handling',
435
+ description: `Missing required parameter: ${prop.name}`,
436
+ input,
437
+ expectedBehavior: `Tool should return error for missing ${prop.name}`,
438
+ priority: 'critical',
439
+ tags: ['missing-required', prop.name],
440
+ rationale: 'Validates required parameter enforcement',
441
+ targetParameter: prop.name,
442
+ });
443
+ }
444
+ // Test invalid types
445
+ for (const prop of properties) {
446
+ if (scenarios.length >= maxScenarios)
447
+ break;
448
+ const input = {};
449
+ // Set required params
450
+ for (const p of properties.filter(p => p.required && p.name !== prop.name)) {
451
+ input[p.name] = generateValidValue(p);
452
+ }
453
+ input[prop.name] = generateInvalidValue(prop);
454
+ scenarios.push({
455
+ id: generateScenarioId(tool.name, 'error_handling', counter),
456
+ toolName: tool.name,
457
+ category: 'error_handling',
458
+ description: `Invalid type for ${prop.name}: expected ${prop.type}`,
459
+ input,
460
+ expectedBehavior: `Tool should reject invalid type for ${prop.name}`,
461
+ priority: 'high',
462
+ tags: ['invalid-type', prop.name],
463
+ rationale: `Tests type validation for ${prop.name} (expected ${prop.type})`,
464
+ targetParameter: prop.name,
465
+ });
466
+ }
467
+ // Test invalid enum values
468
+ for (const prop of properties.filter(p => p.enum)) {
469
+ if (scenarios.length >= maxScenarios)
470
+ break;
471
+ const input = {};
472
+ for (const p of properties.filter(p => p.required && p.name !== prop.name)) {
473
+ input[p.name] = generateValidValue(p);
474
+ }
475
+ input[prop.name] = 'invalid-enum-value-xyz';
476
+ scenarios.push({
477
+ id: generateScenarioId(tool.name, 'error_handling', counter),
478
+ toolName: tool.name,
479
+ category: 'error_handling',
480
+ description: `Invalid enum value for ${prop.name}`,
481
+ input,
482
+ expectedBehavior: `Tool should reject invalid enum value for ${prop.name}`,
483
+ priority: 'high',
484
+ tags: ['invalid-enum', prop.name],
485
+ rationale: `Tests enum validation for ${prop.name}`,
486
+ targetParameter: prop.name,
487
+ });
488
+ }
489
+ return scenarios.slice(0, maxScenarios);
490
+ }
491
+ /**
492
+ * Generate security test scenarios for a tool.
493
+ */
494
+ function generateSecurityScenarios(tool, properties, maxScenarios, includePayloads, counter) {
495
+ const scenarios = [];
496
+ const stringProps = properties.filter(p => p.type === 'string');
497
+ if (!includePayloads || stringProps.length === 0) {
498
+ return scenarios;
499
+ }
500
+ // SQL Injection tests
501
+ for (const prop of stringProps) {
502
+ if (scenarios.length >= maxScenarios)
503
+ break;
504
+ const payload = SCENARIO_GENERATION.SQL_INJECTION_PAYLOADS[0];
505
+ const input = {};
506
+ for (const p of properties.filter(p => p.required && p.name !== prop.name)) {
507
+ input[p.name] = generateValidValue(p);
508
+ }
509
+ input[prop.name] = payload;
510
+ scenarios.push({
511
+ id: generateScenarioId(tool.name, 'security', counter),
512
+ toolName: tool.name,
513
+ category: 'security',
514
+ description: `SQL injection test on ${prop.name}`,
515
+ input,
516
+ expectedBehavior: 'Tool should sanitize input and not execute SQL',
517
+ priority: 'critical',
518
+ tags: ['sql-injection', 'security', prop.name],
519
+ rationale: 'Tests SQL injection vulnerability',
520
+ targetParameter: prop.name,
521
+ });
522
+ }
523
+ // XSS tests
524
+ for (const prop of stringProps) {
525
+ if (scenarios.length >= maxScenarios)
526
+ break;
527
+ const payload = SCENARIO_GENERATION.XSS_PAYLOADS[0];
528
+ const input = {};
529
+ for (const p of properties.filter(p => p.required && p.name !== prop.name)) {
530
+ input[p.name] = generateValidValue(p);
531
+ }
532
+ input[prop.name] = payload;
533
+ scenarios.push({
534
+ id: generateScenarioId(tool.name, 'security', counter),
535
+ toolName: tool.name,
536
+ category: 'security',
537
+ description: `XSS test on ${prop.name}`,
538
+ input,
539
+ expectedBehavior: 'Tool should sanitize or escape HTML/script content',
540
+ priority: 'critical',
541
+ tags: ['xss', 'security', prop.name],
542
+ rationale: 'Tests cross-site scripting vulnerability',
543
+ targetParameter: prop.name,
544
+ });
545
+ }
546
+ // Path traversal tests (for file-related parameters)
547
+ const fileProps = stringProps.filter(p => p.name.toLowerCase().includes('path') ||
548
+ p.name.toLowerCase().includes('file') ||
549
+ p.name.toLowerCase().includes('dir') ||
550
+ p.description?.toLowerCase().includes('path') ||
551
+ p.description?.toLowerCase().includes('file'));
552
+ for (const prop of fileProps) {
553
+ if (scenarios.length >= maxScenarios)
554
+ break;
555
+ const payload = SCENARIO_GENERATION.PATH_TRAVERSAL_PAYLOADS[0];
556
+ const input = {};
557
+ for (const p of properties.filter(p => p.required && p.name !== prop.name)) {
558
+ input[p.name] = generateValidValue(p);
559
+ }
560
+ input[prop.name] = payload;
561
+ scenarios.push({
562
+ id: generateScenarioId(tool.name, 'security', counter),
563
+ toolName: tool.name,
564
+ category: 'security',
565
+ description: `Path traversal test on ${prop.name}`,
566
+ input,
567
+ expectedBehavior: 'Tool should reject path traversal attempts',
568
+ priority: 'critical',
569
+ tags: ['path-traversal', 'security', prop.name],
570
+ rationale: 'Tests path traversal vulnerability',
571
+ targetParameter: prop.name,
572
+ });
573
+ }
574
+ // Command injection tests (for command-like parameters)
575
+ const cmdProps = stringProps.filter(p => p.name.toLowerCase().includes('command') ||
576
+ p.name.toLowerCase().includes('cmd') ||
577
+ p.name.toLowerCase().includes('exec') ||
578
+ p.name.toLowerCase().includes('shell'));
579
+ for (const prop of cmdProps) {
580
+ if (scenarios.length >= maxScenarios)
581
+ break;
582
+ const input = {};
583
+ for (const p of properties.filter(p => p.required && p.name !== prop.name)) {
584
+ input[p.name] = generateValidValue(p);
585
+ }
586
+ input[prop.name] = '; rm -rf /';
587
+ scenarios.push({
588
+ id: generateScenarioId(tool.name, 'security', counter),
589
+ toolName: tool.name,
590
+ category: 'security',
591
+ description: `Command injection test on ${prop.name}`,
592
+ input,
593
+ expectedBehavior: 'Tool should sanitize command input',
594
+ priority: 'critical',
595
+ tags: ['command-injection', 'security', prop.name],
596
+ rationale: 'Tests command injection vulnerability',
597
+ targetParameter: prop.name,
598
+ });
599
+ }
600
+ return scenarios.slice(0, maxScenarios);
601
+ }
602
+ /**
603
+ * Generate test scenarios for a single tool.
604
+ */
605
+ export function generateToolScenarios(tool, config = {}) {
606
+ const { maxHappyPath = SCENARIO_GENERATION.MAX_HAPPY_PATH_SCENARIOS, maxEdgeCases = SCENARIO_GENERATION.MAX_EDGE_CASE_SCENARIOS, maxErrorCases = SCENARIO_GENERATION.MAX_ERROR_CASE_SCENARIOS, maxSecurityScenarios = SCENARIO_GENERATION.MAX_SECURITY_SCENARIOS, categories = SCENARIO_GENERATION.CATEGORIES, includeSecurityPayloads = true, } = config;
607
+ // Counter for generating unique scenario IDs within this tool
608
+ const counter = { value: 0 };
609
+ const properties = tool.inputSchema ? extractSchemaProperties(tool.inputSchema) : [];
610
+ const happyPath = categories.includes('happy_path')
611
+ ? generateHappyPathScenarios(tool, properties, maxHappyPath, counter)
612
+ : [];
613
+ const edgeCases = categories.includes('edge_cases')
614
+ ? generateEdgeCaseScenarios(tool, properties, maxEdgeCases, counter)
615
+ : [];
616
+ const errorCases = categories.includes('error_handling')
617
+ ? generateErrorCaseScenarios(tool, properties, maxErrorCases, counter)
618
+ : [];
619
+ const securityTests = categories.includes('security')
620
+ ? generateSecurityScenarios(tool, properties, maxSecurityScenarios, includeSecurityPayloads, counter)
621
+ : [];
622
+ // Calculate coverage
623
+ const totalScenarios = happyPath.length + edgeCases.length + errorCases.length + securityTests.length;
624
+ const coveredParams = new Set();
625
+ for (const scenario of [...happyPath, ...edgeCases, ...errorCases, ...securityTests]) {
626
+ if (scenario.targetParameter) {
627
+ coveredParams.add(scenario.targetParameter);
628
+ }
629
+ // Also count parameters in input
630
+ for (const param of Object.keys(scenario.input)) {
631
+ coveredParams.add(param);
632
+ }
633
+ }
634
+ const allParams = properties.map(p => p.name);
635
+ const uncovered = allParams.filter(p => !coveredParams.has(p));
636
+ // Estimate coverage based on parameters covered and scenario diversity
637
+ let coverageEstimate = 0;
638
+ if (allParams.length > 0) {
639
+ const paramCoverage = (coveredParams.size / allParams.length) * 100;
640
+ const categoryBonus = categories.length * 5; // Bonus for testing multiple categories
641
+ coverageEstimate = Math.min(100, Math.round(paramCoverage + categoryBonus));
642
+ }
643
+ else if (totalScenarios > 0) {
644
+ coverageEstimate = 50; // Basic coverage for tools without schema
645
+ }
646
+ return {
647
+ toolName: tool.name,
648
+ toolDescription: tool.description,
649
+ happyPath,
650
+ edgeCases,
651
+ errorCases,
652
+ securityTests,
653
+ coverageEstimate,
654
+ coveredParameters: Array.from(coveredParams),
655
+ uncoveredParameters: uncovered,
656
+ generatedAt: new Date(),
657
+ };
658
+ }
659
+ /**
660
+ * Generate test scenarios for all tools in a baseline.
661
+ */
662
+ export function generateBaselineScenarios(baseline, config = {}) {
663
+ const { tools: targetTools, minCoverage = SCENARIO_GENERATION.DEFAULT_MIN_COVERAGE } = config;
664
+ const toolsToProcess = targetTools
665
+ ? baseline.tools.filter(t => targetTools.includes(t.name))
666
+ : baseline.tools;
667
+ const scenarios = [];
668
+ let toolsWithScenarios = 0;
669
+ let toolsSkipped = 0;
670
+ const categoryTotals = {
671
+ happy_path: 0,
672
+ edge_cases: 0,
673
+ error_handling: 0,
674
+ security: 0,
675
+ };
676
+ const priorityTotals = {
677
+ critical: 0,
678
+ high: 0,
679
+ medium: 0,
680
+ low: 0,
681
+ };
682
+ for (const tool of toolsToProcess) {
683
+ // Skip tools without schema (can't generate meaningful scenarios)
684
+ if (!tool.inputSchema) {
685
+ toolsSkipped++;
686
+ continue;
687
+ }
688
+ const toolScenarios = generateToolScenarios(tool, config);
689
+ const totalForTool = toolScenarios.happyPath.length +
690
+ toolScenarios.edgeCases.length +
691
+ toolScenarios.errorCases.length +
692
+ toolScenarios.securityTests.length;
693
+ if (totalForTool > 0) {
694
+ toolsWithScenarios++;
695
+ scenarios.push(toolScenarios);
696
+ // Update category totals
697
+ categoryTotals.happy_path += toolScenarios.happyPath.length;
698
+ categoryTotals.edge_cases += toolScenarios.edgeCases.length;
699
+ categoryTotals.error_handling += toolScenarios.errorCases.length;
700
+ categoryTotals.security += toolScenarios.securityTests.length;
701
+ // Update priority totals
702
+ for (const s of [
703
+ ...toolScenarios.happyPath,
704
+ ...toolScenarios.edgeCases,
705
+ ...toolScenarios.errorCases,
706
+ ...toolScenarios.securityTests,
707
+ ]) {
708
+ priorityTotals[s.priority]++;
709
+ }
710
+ }
711
+ else {
712
+ toolsSkipped++;
713
+ }
714
+ }
715
+ const totalScenarios = Object.values(categoryTotals).reduce((a, b) => a + b, 0);
716
+ const averageCoverage = scenarios.length > 0
717
+ ? Math.round(scenarios.reduce((sum, s) => sum + s.coverageEstimate, 0) / scenarios.length)
718
+ : 0;
719
+ const lowCoverageTools = scenarios
720
+ .filter(s => s.coverageEstimate < minCoverage)
721
+ .map(s => s.toolName);
722
+ return {
723
+ scenarios,
724
+ summary: {
725
+ toolsProcessed: toolsToProcess.length,
726
+ toolsWithScenarios,
727
+ toolsSkipped,
728
+ totalScenarios,
729
+ scenariosByCategory: categoryTotals,
730
+ scenariosByPriority: priorityTotals,
731
+ averageCoverage,
732
+ lowCoverageTools,
733
+ generatedAt: new Date(),
734
+ },
735
+ };
736
+ }
737
+ /**
738
+ * Format scenarios as YAML for test execution.
739
+ */
740
+ export function formatScenariosAsYaml(result) {
741
+ const lines = [
742
+ '# Auto-generated test scenarios',
743
+ `# Generated: ${result.summary.generatedAt.toISOString()}`,
744
+ `# Total scenarios: ${result.summary.totalScenarios}`,
745
+ '',
746
+ 'scenarios:',
747
+ ];
748
+ for (const toolScenarios of result.scenarios) {
749
+ lines.push(` # Tool: ${toolScenarios.toolName}`);
750
+ lines.push(` # Coverage: ${toolScenarios.coverageEstimate}%`);
751
+ const allScenarios = [
752
+ ...toolScenarios.happyPath,
753
+ ...toolScenarios.edgeCases,
754
+ ...toolScenarios.errorCases,
755
+ ...toolScenarios.securityTests,
756
+ ];
757
+ for (const scenario of allScenarios) {
758
+ lines.push(` - id: ${scenario.id}`);
759
+ lines.push(` tool: ${scenario.toolName}`);
760
+ lines.push(` category: ${scenario.category}`);
761
+ lines.push(` description: "${scenario.description.replace(/"/g, '\\"')}"`);
762
+ lines.push(` priority: ${scenario.priority}`);
763
+ lines.push(` input:`);
764
+ for (const [key, value] of Object.entries(scenario.input)) {
765
+ const yamlValue = formatYamlValue(value);
766
+ lines.push(` ${key}: ${yamlValue}`);
767
+ }
768
+ lines.push(` expected: "${scenario.expectedBehavior.replace(/"/g, '\\"')}"`);
769
+ lines.push(` tags: [${scenario.tags.map(t => `"${t}"`).join(', ')}]`);
770
+ lines.push('');
771
+ }
772
+ }
773
+ return lines.join('\n');
774
+ }
775
+ /**
776
+ * Format a value for YAML output.
777
+ */
778
+ function formatYamlValue(value) {
779
+ if (value === null)
780
+ return 'null';
781
+ if (value === undefined)
782
+ return 'null';
783
+ if (typeof value === 'string') {
784
+ // Escape special characters
785
+ if (value.includes('\n') || value.includes('"') || value.includes(':')) {
786
+ return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/\n/g, '\\n')}"`;
787
+ }
788
+ return `"${value}"`;
789
+ }
790
+ if (typeof value === 'number' || typeof value === 'boolean') {
791
+ return String(value);
792
+ }
793
+ if (Array.isArray(value)) {
794
+ if (value.length === 0)
795
+ return '[]';
796
+ return `[${value.map(v => formatYamlValue(v)).join(', ')}]`;
797
+ }
798
+ if (typeof value === 'object') {
799
+ return JSON.stringify(value);
800
+ }
801
+ return String(value);
802
+ }
803
+ /**
804
+ * Format scenarios as a human-readable report.
805
+ */
806
+ export function formatScenariosReport(result) {
807
+ const lines = [
808
+ '═══════════════════════════════════════════════════════════════',
809
+ ' AUTO-GENERATED TEST SCENARIOS ',
810
+ '═══════════════════════════════════════════════════════════════',
811
+ '',
812
+ `Generated: ${result.summary.generatedAt.toISOString()}`,
813
+ '',
814
+ '── Summary ──────────────────────────────────────────────────────',
815
+ ` Tools processed: ${result.summary.toolsProcessed}`,
816
+ ` Tools with scenarios: ${result.summary.toolsWithScenarios}`,
817
+ ` Tools skipped: ${result.summary.toolsSkipped}`,
818
+ ` Total scenarios: ${result.summary.totalScenarios}`,
819
+ ` Average coverage: ${result.summary.averageCoverage}%`,
820
+ '',
821
+ '── By Category ──────────────────────────────────────────────────',
822
+ ` Happy Path: ${result.summary.scenariosByCategory.happy_path}`,
823
+ ` Edge Cases: ${result.summary.scenariosByCategory.edge_cases}`,
824
+ ` Error Handling: ${result.summary.scenariosByCategory.error_handling}`,
825
+ ` Security: ${result.summary.scenariosByCategory.security}`,
826
+ '',
827
+ '── By Priority ──────────────────────────────────────────────────',
828
+ ` Critical: ${result.summary.scenariosByPriority.critical}`,
829
+ ` High: ${result.summary.scenariosByPriority.high}`,
830
+ ` Medium: ${result.summary.scenariosByPriority.medium}`,
831
+ ` Low: ${result.summary.scenariosByPriority.low}`,
832
+ ];
833
+ if (result.summary.lowCoverageTools.length > 0) {
834
+ lines.push('');
835
+ lines.push('── Low Coverage Tools (need attention) ─────────────────────────');
836
+ for (const tool of result.summary.lowCoverageTools) {
837
+ const toolData = result.scenarios.find(s => s.toolName === tool);
838
+ lines.push(` • ${tool} (${toolData?.coverageEstimate ?? 0}%)`);
839
+ }
840
+ }
841
+ lines.push('');
842
+ lines.push('── Tool Details ─────────────────────────────────────────────────');
843
+ for (const toolScenarios of result.scenarios) {
844
+ const total = toolScenarios.happyPath.length +
845
+ toolScenarios.edgeCases.length +
846
+ toolScenarios.errorCases.length +
847
+ toolScenarios.securityTests.length;
848
+ lines.push('');
849
+ lines.push(` ${toolScenarios.toolName}`);
850
+ lines.push(` Scenarios: ${total} | Coverage: ${toolScenarios.coverageEstimate}%`);
851
+ lines.push(` HP: ${toolScenarios.happyPath.length} | EC: ${toolScenarios.edgeCases.length} | EH: ${toolScenarios.errorCases.length} | SEC: ${toolScenarios.securityTests.length}`);
852
+ if (toolScenarios.uncoveredParameters.length > 0) {
853
+ lines.push(` Uncovered: ${toolScenarios.uncoveredParameters.join(', ')}`);
854
+ }
855
+ }
856
+ lines.push('');
857
+ lines.push('═══════════════════════════════════════════════════════════════');
858
+ return lines.join('\n');
859
+ }
860
+ /**
861
+ * Get scenarios filtered by priority.
862
+ */
863
+ export function getScenariosByPriority(result, priority) {
864
+ const allScenarios = [];
865
+ for (const toolScenarios of result.scenarios) {
866
+ allScenarios.push(...toolScenarios.happyPath.filter(s => s.priority === priority), ...toolScenarios.edgeCases.filter(s => s.priority === priority), ...toolScenarios.errorCases.filter(s => s.priority === priority), ...toolScenarios.securityTests.filter(s => s.priority === priority));
867
+ }
868
+ return allScenarios;
869
+ }
870
+ /**
871
+ * Get scenarios filtered by category.
872
+ */
873
+ export function getScenariosByCategory(result, category) {
874
+ const allScenarios = [];
875
+ for (const toolScenarios of result.scenarios) {
876
+ switch (category) {
877
+ case 'happy_path':
878
+ allScenarios.push(...toolScenarios.happyPath);
879
+ break;
880
+ case 'edge_cases':
881
+ allScenarios.push(...toolScenarios.edgeCases);
882
+ break;
883
+ case 'error_handling':
884
+ allScenarios.push(...toolScenarios.errorCases);
885
+ break;
886
+ case 'security':
887
+ allScenarios.push(...toolScenarios.securityTests);
888
+ break;
889
+ }
890
+ }
891
+ return allScenarios;
892
+ }
893
+ /**
894
+ * Get critical scenarios for smoke testing.
895
+ */
896
+ export function getCriticalScenarios(result) {
897
+ return getScenariosByPriority(result, 'critical');
898
+ }
899
+ /**
900
+ * Get security-focused scenarios.
901
+ */
902
+ export function getSecurityScenarios(result) {
903
+ return getScenariosByCategory(result, 'security');
904
+ }
905
+ //# sourceMappingURL=scenario-generator.js.map