@agentic-qe/v3 3.0.0-alpha.6 → 3.0.0-alpha.8

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 (612) hide show
  1. package/assets/agents/v3/subagents/v3-qe-code-reviewer.md +339 -0
  2. package/assets/agents/v3/subagents/v3-qe-integration-reviewer.md +344 -0
  3. package/assets/agents/v3/subagents/v3-qe-performance-reviewer.md +351 -0
  4. package/assets/agents/v3/subagents/v3-qe-security-reviewer.md +374 -0
  5. package/assets/agents/v3/subagents/v3-qe-tdd-green.md +334 -0
  6. package/assets/agents/v3/subagents/v3-qe-tdd-red.md +329 -0
  7. package/assets/agents/v3/subagents/v3-qe-tdd-refactor.md +361 -0
  8. package/assets/agents/v3/v3-qe-accessibility-auditor.md +266 -0
  9. package/assets/agents/v3/v3-qe-bdd-generator.md +279 -0
  10. package/assets/agents/v3/v3-qe-chaos-engineer.md +265 -0
  11. package/assets/agents/v3/v3-qe-code-complexity.md +298 -0
  12. package/assets/agents/v3/v3-qe-code-intelligence.md +262 -0
  13. package/assets/agents/v3/v3-qe-contract-validator.md +267 -0
  14. package/assets/agents/v3/v3-qe-coverage-specialist.md +227 -0
  15. package/assets/agents/v3/v3-qe-defect-predictor.md +251 -0
  16. package/assets/agents/v3/v3-qe-dependency-mapper.md +277 -0
  17. package/assets/agents/v3/v3-qe-deployment-advisor.md +275 -0
  18. package/assets/agents/v3/v3-qe-flaky-hunter.md +248 -0
  19. package/assets/agents/v3/v3-qe-fleet-commander.md +293 -0
  20. package/assets/agents/v3/v3-qe-gap-detector.md +260 -0
  21. package/assets/agents/v3/v3-qe-graphql-tester.md +308 -0
  22. package/assets/agents/v3/v3-qe-impact-analyzer.md +299 -0
  23. package/assets/agents/v3/v3-qe-integration-tester.md +238 -0
  24. package/assets/agents/v3/v3-qe-kg-builder.md +273 -0
  25. package/assets/agents/v3/v3-qe-learning-coordinator.md +226 -0
  26. package/assets/agents/v3/v3-qe-load-tester.md +280 -0
  27. package/assets/agents/v3/v3-qe-metrics-optimizer.md +300 -0
  28. package/assets/agents/v3/v3-qe-mutation-tester.md +301 -0
  29. package/assets/agents/v3/v3-qe-parallel-executor.md +240 -0
  30. package/assets/agents/v3/v3-qe-pattern-learner.md +271 -0
  31. package/assets/agents/v3/v3-qe-performance-tester.md +262 -0
  32. package/assets/agents/v3/v3-qe-property-tester.md +247 -0
  33. package/assets/agents/v3/v3-qe-quality-gate.md +218 -0
  34. package/assets/agents/v3/v3-qe-queen-coordinator.md +214 -0
  35. package/assets/agents/v3/v3-qe-qx-partner.md +313 -0
  36. package/assets/agents/v3/v3-qe-regression-analyzer.md +322 -0
  37. package/assets/agents/v3/v3-qe-requirements-validator.md +360 -0
  38. package/assets/agents/v3/v3-qe-responsive-tester.md +311 -0
  39. package/assets/agents/v3/v3-qe-retry-handler.md +256 -0
  40. package/assets/agents/v3/v3-qe-risk-assessor.md +273 -0
  41. package/assets/agents/v3/v3-qe-root-cause-analyzer.md +286 -0
  42. package/assets/agents/v3/v3-qe-security-auditor.md +299 -0
  43. package/assets/agents/v3/v3-qe-security-scanner.md +235 -0
  44. package/assets/agents/v3/v3-qe-tdd-specialist.md +239 -0
  45. package/assets/agents/v3/v3-qe-test-architect.md +233 -0
  46. package/assets/agents/v3/v3-qe-transfer-specialist.md +295 -0
  47. package/assets/agents/v3/v3-qe-visual-tester.md +232 -0
  48. package/assets/skills/accessibility-testing/SKILL.md +216 -0
  49. package/assets/skills/agentdb-advanced/SKILL.md +550 -0
  50. package/assets/skills/agentdb-learning/SKILL.md +545 -0
  51. package/assets/skills/agentdb-memory-patterns/SKILL.md +339 -0
  52. package/assets/skills/agentdb-optimization/SKILL.md +509 -0
  53. package/assets/skills/agentdb-vector-search/SKILL.md +339 -0
  54. package/assets/skills/agentic-jujutsu/SKILL.md +645 -0
  55. package/assets/skills/agentic-quality-engineering/SKILL.md +335 -0
  56. package/assets/skills/api-testing-patterns/SKILL.md +294 -0
  57. package/assets/skills/aqe-v2-v3-migration/skill.md +322 -0
  58. package/assets/skills/brutal-honesty-review/README.md +218 -0
  59. package/assets/skills/brutal-honesty-review/SKILL.md +235 -0
  60. package/assets/skills/brutal-honesty-review/resources/assessment-rubrics.md +295 -0
  61. package/assets/skills/brutal-honesty-review/resources/review-template.md +102 -0
  62. package/assets/skills/brutal-honesty-review/scripts/assess-code.sh +179 -0
  63. package/assets/skills/brutal-honesty-review/scripts/assess-tests.sh +223 -0
  64. package/assets/skills/bug-reporting-excellence/SKILL.md +225 -0
  65. package/assets/skills/chaos-engineering-resilience/SKILL.md +158 -0
  66. package/assets/skills/cicd-pipeline-qe-orchestrator/README.md +304 -0
  67. package/assets/skills/cicd-pipeline-qe-orchestrator/SKILL.md +315 -0
  68. package/assets/skills/cicd-pipeline-qe-orchestrator/resources/workflows/microservice-pipeline.md +239 -0
  69. package/assets/skills/cicd-pipeline-qe-orchestrator/resources/workflows/mobile-pipeline.md +375 -0
  70. package/assets/skills/cicd-pipeline-qe-orchestrator/resources/workflows/monolith-pipeline.md +268 -0
  71. package/assets/skills/code-review-quality/SKILL.md +227 -0
  72. package/assets/skills/compatibility-testing/SKILL.md +205 -0
  73. package/assets/skills/compliance-testing/SKILL.md +225 -0
  74. package/assets/skills/consultancy-practices/SKILL.md +202 -0
  75. package/assets/skills/context-driven-testing/SKILL.md +196 -0
  76. package/assets/skills/contract-testing/SKILL.md +222 -0
  77. package/assets/skills/database-testing/SKILL.md +244 -0
  78. package/assets/skills/exploratory-testing-advanced/SKILL.md +201 -0
  79. package/assets/skills/flow-nexus-neural/SKILL.md +738 -0
  80. package/assets/skills/flow-nexus-platform/SKILL.md +1157 -0
  81. package/assets/skills/flow-nexus-swarm/SKILL.md +610 -0
  82. package/assets/skills/github-code-review/SKILL.md +1140 -0
  83. package/assets/skills/github-multi-repo/SKILL.md +874 -0
  84. package/assets/skills/github-project-management/SKILL.md +1277 -0
  85. package/assets/skills/github-release-management/SKILL.md +1081 -0
  86. package/assets/skills/github-workflow-automation/SKILL.md +1065 -0
  87. package/assets/skills/hive-mind-advanced/SKILL.md +712 -0
  88. package/assets/skills/holistic-testing-pact/SKILL.md +171 -0
  89. package/assets/skills/hooks-automation/SKILL.md +1201 -0
  90. package/assets/skills/localization-testing/SKILL.md +221 -0
  91. package/assets/skills/mobile-testing/SKILL.md +219 -0
  92. package/assets/skills/mutation-testing/SKILL.md +229 -0
  93. package/assets/skills/n8n-expression-testing/SKILL.md +434 -0
  94. package/assets/skills/n8n-integration-testing-patterns/SKILL.md +540 -0
  95. package/assets/skills/n8n-security-testing/SKILL.md +599 -0
  96. package/assets/skills/n8n-trigger-testing-strategies/SKILL.md +541 -0
  97. package/assets/skills/n8n-workflow-testing-fundamentals/SKILL.md +447 -0
  98. package/assets/skills/pair-programming/SKILL.md +1202 -0
  99. package/assets/skills/performance-analysis/SKILL.md +563 -0
  100. package/assets/skills/performance-testing/SKILL.md +310 -0
  101. package/assets/skills/quality-metrics/SKILL.md +225 -0
  102. package/assets/skills/reasoningbank-agentdb/SKILL.md +446 -0
  103. package/assets/skills/reasoningbank-intelligence/SKILL.md +201 -0
  104. package/assets/skills/refactoring-patterns/SKILL.md +205 -0
  105. package/assets/skills/regression-testing/SKILL.md +227 -0
  106. package/assets/skills/risk-based-testing/SKILL.md +206 -0
  107. package/assets/skills/security-testing/SKILL.md +306 -0
  108. package/assets/skills/sherlock-review/SKILL.md +250 -0
  109. package/assets/skills/shift-left-testing/SKILL.md +225 -0
  110. package/assets/skills/shift-right-testing/SKILL.md +227 -0
  111. package/assets/skills/six-thinking-hats/README.md +190 -0
  112. package/assets/skills/six-thinking-hats/SKILL.md +280 -0
  113. package/assets/skills/six-thinking-hats/resources/examples/api-testing-example.md +345 -0
  114. package/assets/skills/six-thinking-hats/resources/templates/solo-session-template.md +167 -0
  115. package/assets/skills/six-thinking-hats/resources/templates/team-session-template.md +336 -0
  116. package/assets/skills/skill-builder/SKILL.md +910 -0
  117. package/assets/skills/sparc-methodology/SKILL.md +1115 -0
  118. package/assets/skills/stream-chain/SKILL.md +563 -0
  119. package/assets/skills/swarm-advanced/SKILL.md +973 -0
  120. package/assets/skills/swarm-orchestration/SKILL.md +179 -0
  121. package/assets/skills/tdd-london-chicago/SKILL.md +244 -0
  122. package/assets/skills/technical-writing/SKILL.md +178 -0
  123. package/assets/skills/test-automation-strategy/SKILL.md +230 -0
  124. package/assets/skills/test-data-management/SKILL.md +270 -0
  125. package/assets/skills/test-design-techniques/SKILL.md +244 -0
  126. package/assets/skills/test-environment-management/SKILL.md +243 -0
  127. package/assets/skills/test-reporting-analytics/SKILL.md +214 -0
  128. package/assets/skills/testability-scoring/README.md +71 -0
  129. package/assets/skills/testability-scoring/SKILL.md +346 -0
  130. package/assets/skills/testability-scoring/resources/templates/config.template.js +84 -0
  131. package/assets/skills/testability-scoring/resources/templates/testability-scoring.spec.template.js +532 -0
  132. package/assets/skills/testability-scoring/scripts/generate-html-report.js +1007 -0
  133. package/assets/skills/testability-scoring/scripts/run-assessment.sh +70 -0
  134. package/assets/skills/v3-qe-chaos-resilience/SKILL.md +238 -0
  135. package/assets/skills/v3-qe-code-intelligence/SKILL.md +209 -0
  136. package/assets/skills/v3-qe-contract-testing/SKILL.md +218 -0
  137. package/assets/skills/v3-qe-coverage-analysis/SKILL.md +187 -0
  138. package/assets/skills/v3-qe-defect-intelligence/SKILL.md +205 -0
  139. package/assets/skills/v3-qe-learning-optimization/SKILL.md +238 -0
  140. package/assets/skills/v3-qe-quality-assessment/SKILL.md +213 -0
  141. package/assets/skills/v3-qe-requirements-validation/SKILL.md +248 -0
  142. package/assets/skills/v3-qe-test-execution/SKILL.md +182 -0
  143. package/assets/skills/v3-qe-test-generation/SKILL.md +141 -0
  144. package/assets/skills/v3-qe-visual-accessibility/SKILL.md +242 -0
  145. package/assets/skills/verification-quality/SKILL.md +649 -0
  146. package/assets/skills/visual-testing-advanced/SKILL.md +219 -0
  147. package/assets/skills/xp-practices/SKILL.md +229 -0
  148. package/dist/cli/bundle.js +23 -13
  149. package/dist/init/agents-installer.js +4 -4
  150. package/dist/init/agents-installer.js.map +1 -1
  151. package/dist/init/init-wizard.d.ts.map +1 -1
  152. package/dist/init/init-wizard.js +15 -5
  153. package/dist/init/init-wizard.js.map +1 -1
  154. package/dist/init/skills-installer.js +4 -4
  155. package/dist/init/skills-installer.js.map +1 -1
  156. package/package.json +7 -1
  157. package/docs/analysis/V3-INIT-REQUIREMENTS-ANALYSIS.md +0 -352
  158. package/implementation/README.md +0 -90
  159. package/implementation/adrs/ADR-030-coherence-gated-quality-gates.md +0 -312
  160. package/implementation/adrs/ADR-031-strange-loop-self-awareness.md +0 -484
  161. package/implementation/adrs/ADR-032-time-crystal-scheduling.md +0 -530
  162. package/implementation/adrs/ADR-033-early-exit-testing.md +0 -634
  163. package/implementation/adrs/ADR-034-neural-topology-optimizer.md +0 -589
  164. package/implementation/adrs/ADR-035-causal-discovery.md +0 -610
  165. package/implementation/adrs/ADR-036-result-persistence.md +0 -326
  166. package/implementation/adrs/ADR-037-v3-qe-agent-naming.md +0 -105
  167. package/implementation/adrs/ADR-038-v3-qe-memory-unification.md +0 -154
  168. package/implementation/adrs/ADR-039-v3-qe-mcp-optimization.md +0 -179
  169. package/implementation/adrs/ADR-040-v3-qe-agentic-flow-integration.md +0 -240
  170. package/implementation/adrs/ADR-041-v3-qe-cli-enhancement.md +0 -296
  171. package/implementation/adrs/ADR-042-v3-qe-token-tracking-integration.md +0 -517
  172. package/implementation/adrs/v3-adrs.md +0 -2783
  173. package/implementation/planning/AQE-V3-MASTER-PLAN.md +0 -815
  174. package/security-scan-report-2026-01-11.md +0 -410
  175. package/security-verification-report-2026-01-11.md +0 -278
  176. package/src/benchmarks/performance-benchmarks.ts +0 -646
  177. package/src/benchmarks/run-benchmarks.ts +0 -324
  178. package/src/causal-discovery/causal-graph.ts +0 -450
  179. package/src/causal-discovery/discovery-engine.ts +0 -438
  180. package/src/causal-discovery/index.ts +0 -117
  181. package/src/causal-discovery/types.ts +0 -456
  182. package/src/causal-discovery/weight-matrix.ts +0 -453
  183. package/src/cli/commands/qe-tools.ts +0 -634
  184. package/src/cli/index.ts +0 -1976
  185. package/src/compatibility/agent-mapper.ts +0 -291
  186. package/src/compatibility/cli-adapter.ts +0 -277
  187. package/src/compatibility/config-migrator.ts +0 -334
  188. package/src/compatibility/index.ts +0 -112
  189. package/src/compatibility/mcp-adapter.ts +0 -248
  190. package/src/compatibility/types.ts +0 -156
  191. package/src/coordination/claims/claim-repository.ts +0 -636
  192. package/src/coordination/claims/claim-service.ts +0 -675
  193. package/src/coordination/claims/handoff-manager.ts +0 -535
  194. package/src/coordination/claims/index.ts +0 -276
  195. package/src/coordination/claims/interfaces.ts +0 -687
  196. package/src/coordination/claims/work-stealing.ts +0 -436
  197. package/src/coordination/cross-domain-router.ts +0 -492
  198. package/src/coordination/index.ts +0 -127
  199. package/src/coordination/interfaces.ts +0 -691
  200. package/src/coordination/protocol-executor.ts +0 -760
  201. package/src/coordination/protocols/code-intelligence-index.ts +0 -855
  202. package/src/coordination/protocols/defect-investigation.ts +0 -1184
  203. package/src/coordination/protocols/index.ts +0 -11
  204. package/src/coordination/protocols/learning-consolidation.ts +0 -1181
  205. package/src/coordination/protocols/morning-sync.ts +0 -1055
  206. package/src/coordination/protocols/quality-gate.ts +0 -1566
  207. package/src/coordination/protocols/security-audit.ts +0 -1587
  208. package/src/coordination/queen-coordinator.ts +0 -1176
  209. package/src/coordination/result-saver.ts +0 -780
  210. package/src/coordination/task-executor.ts +0 -1146
  211. package/src/coordination/workflow-orchestrator.ts +0 -1917
  212. package/src/domains/chaos-resilience/coordinator.ts +0 -1032
  213. package/src/domains/chaos-resilience/index.ts +0 -143
  214. package/src/domains/chaos-resilience/interfaces.ts +0 -659
  215. package/src/domains/chaos-resilience/plugin.ts +0 -691
  216. package/src/domains/chaos-resilience/services/chaos-engineer.ts +0 -1097
  217. package/src/domains/chaos-resilience/services/index.ts +0 -19
  218. package/src/domains/chaos-resilience/services/load-tester.ts +0 -799
  219. package/src/domains/chaos-resilience/services/performance-profiler.ts +0 -792
  220. package/src/domains/code-intelligence/coordinator.ts +0 -631
  221. package/src/domains/code-intelligence/index.ts +0 -86
  222. package/src/domains/code-intelligence/interfaces.ts +0 -162
  223. package/src/domains/code-intelligence/plugin.ts +0 -451
  224. package/src/domains/code-intelligence/services/impact-analyzer.ts +0 -567
  225. package/src/domains/code-intelligence/services/index.ts +0 -26
  226. package/src/domains/code-intelligence/services/knowledge-graph.ts +0 -1067
  227. package/src/domains/code-intelligence/services/semantic-analyzer.ts +0 -901
  228. package/src/domains/contract-testing/coordinator.ts +0 -1038
  229. package/src/domains/contract-testing/index.ts +0 -122
  230. package/src/domains/contract-testing/interfaces.ts +0 -458
  231. package/src/domains/contract-testing/plugin.ts +0 -746
  232. package/src/domains/contract-testing/services/api-compatibility.ts +0 -748
  233. package/src/domains/contract-testing/services/contract-validator.ts +0 -1700
  234. package/src/domains/contract-testing/services/index.ts +0 -19
  235. package/src/domains/contract-testing/services/schema-validator.ts +0 -1102
  236. package/src/domains/coverage-analysis/coordinator.ts +0 -485
  237. package/src/domains/coverage-analysis/index.ts +0 -114
  238. package/src/domains/coverage-analysis/interfaces.ts +0 -142
  239. package/src/domains/coverage-analysis/plugin.ts +0 -172
  240. package/src/domains/coverage-analysis/services/coverage-analyzer.ts +0 -449
  241. package/src/domains/coverage-analysis/services/coverage-embedder.ts +0 -733
  242. package/src/domains/coverage-analysis/services/coverage-parser.ts +0 -753
  243. package/src/domains/coverage-analysis/services/gap-detector.ts +0 -592
  244. package/src/domains/coverage-analysis/services/hnsw-index.ts +0 -728
  245. package/src/domains/coverage-analysis/services/index.ts +0 -61
  246. package/src/domains/coverage-analysis/services/risk-scorer.ts +0 -540
  247. package/src/domains/coverage-analysis/services/sublinear-analyzer.ts +0 -747
  248. package/src/domains/defect-intelligence/coordinator.ts +0 -635
  249. package/src/domains/defect-intelligence/index.ts +0 -83
  250. package/src/domains/defect-intelligence/interfaces.ts +0 -152
  251. package/src/domains/defect-intelligence/plugin.ts +0 -483
  252. package/src/domains/defect-intelligence/services/causal-root-cause-analyzer.ts +0 -494
  253. package/src/domains/defect-intelligence/services/defect-predictor.ts +0 -852
  254. package/src/domains/defect-intelligence/services/index.ts +0 -37
  255. package/src/domains/defect-intelligence/services/pattern-learner.ts +0 -738
  256. package/src/domains/defect-intelligence/services/root-cause-analyzer.ts +0 -637
  257. package/src/domains/domain-interface.ts +0 -77
  258. package/src/domains/index.ts +0 -23
  259. package/src/domains/learning-optimization/coordinator.ts +0 -1215
  260. package/src/domains/learning-optimization/index.ts +0 -127
  261. package/src/domains/learning-optimization/interfaces.ts +0 -570
  262. package/src/domains/learning-optimization/plugin.ts +0 -851
  263. package/src/domains/learning-optimization/services/index.ts +0 -29
  264. package/src/domains/learning-optimization/services/learning-coordinator.ts +0 -972
  265. package/src/domains/learning-optimization/services/metrics-optimizer.ts +0 -915
  266. package/src/domains/learning-optimization/services/production-intel.ts +0 -971
  267. package/src/domains/learning-optimization/services/transfer-specialist.ts +0 -723
  268. package/src/domains/quality-assessment/coherence/gate-controller.ts +0 -549
  269. package/src/domains/quality-assessment/coherence/index.ts +0 -211
  270. package/src/domains/quality-assessment/coherence/lambda-calculator.ts +0 -384
  271. package/src/domains/quality-assessment/coherence/partition-detector.ts +0 -469
  272. package/src/domains/quality-assessment/coherence/types.ts +0 -384
  273. package/src/domains/quality-assessment/coordinator.ts +0 -605
  274. package/src/domains/quality-assessment/index.ts +0 -97
  275. package/src/domains/quality-assessment/interfaces.ts +0 -152
  276. package/src/domains/quality-assessment/plugin.ts +0 -496
  277. package/src/domains/quality-assessment/services/coherence-gate.ts +0 -358
  278. package/src/domains/quality-assessment/services/deployment-advisor.ts +0 -571
  279. package/src/domains/quality-assessment/services/index.ts +0 -34
  280. package/src/domains/quality-assessment/services/quality-analyzer.ts +0 -670
  281. package/src/domains/quality-assessment/services/quality-gate.ts +0 -384
  282. package/src/domains/requirements-validation/coordinator.ts +0 -812
  283. package/src/domains/requirements-validation/index.ts +0 -92
  284. package/src/domains/requirements-validation/interfaces.ts +0 -303
  285. package/src/domains/requirements-validation/plugin.ts +0 -576
  286. package/src/domains/requirements-validation/services/bdd-scenario-writer.ts +0 -676
  287. package/src/domains/requirements-validation/services/index.ts +0 -20
  288. package/src/domains/requirements-validation/services/requirements-validator.ts +0 -559
  289. package/src/domains/requirements-validation/services/testability-scorer.ts +0 -639
  290. package/src/domains/security-compliance/coordinator.ts +0 -757
  291. package/src/domains/security-compliance/index.ts +0 -120
  292. package/src/domains/security-compliance/interfaces.ts +0 -434
  293. package/src/domains/security-compliance/plugin.ts +0 -509
  294. package/src/domains/security-compliance/services/compliance-validator.ts +0 -1226
  295. package/src/domains/security-compliance/services/index.ts +0 -31
  296. package/src/domains/security-compliance/services/security-auditor.ts +0 -2227
  297. package/src/domains/security-compliance/services/security-scanner.ts +0 -2354
  298. package/src/domains/security-compliance/services/semgrep-integration.ts +0 -289
  299. package/src/domains/test-execution/coordinator.ts +0 -426
  300. package/src/domains/test-execution/index.ts +0 -76
  301. package/src/domains/test-execution/interfaces.ts +0 -119
  302. package/src/domains/test-execution/plugin.ts +0 -208
  303. package/src/domains/test-execution/services/flaky-detector.ts +0 -1240
  304. package/src/domains/test-execution/services/index.ts +0 -8
  305. package/src/domains/test-execution/services/retry-handler.ts +0 -820
  306. package/src/domains/test-execution/services/test-executor.ts +0 -885
  307. package/src/domains/test-generation/coordinator.ts +0 -656
  308. package/src/domains/test-generation/index.ts +0 -77
  309. package/src/domains/test-generation/interfaces.ts +0 -118
  310. package/src/domains/test-generation/plugin.ts +0 -397
  311. package/src/domains/test-generation/services/index.ts +0 -23
  312. package/src/domains/test-generation/services/pattern-matcher.ts +0 -1725
  313. package/src/domains/test-generation/services/test-generator.ts +0 -2750
  314. package/src/domains/visual-accessibility/coordinator.ts +0 -860
  315. package/src/domains/visual-accessibility/index.ts +0 -116
  316. package/src/domains/visual-accessibility/interfaces.ts +0 -435
  317. package/src/domains/visual-accessibility/plugin.ts +0 -568
  318. package/src/domains/visual-accessibility/services/accessibility-tester.ts +0 -982
  319. package/src/domains/visual-accessibility/services/axe-core-audit.ts +0 -630
  320. package/src/domains/visual-accessibility/services/index.ts +0 -28
  321. package/src/domains/visual-accessibility/services/responsive-tester.ts +0 -934
  322. package/src/domains/visual-accessibility/services/visual-tester.ts +0 -458
  323. package/src/early-exit/early-exit-controller.ts +0 -490
  324. package/src/early-exit/early-exit-decision.ts +0 -391
  325. package/src/early-exit/index.ts +0 -115
  326. package/src/early-exit/quality-signal.ts +0 -389
  327. package/src/early-exit/speculative-executor.ts +0 -505
  328. package/src/early-exit/types.ts +0 -407
  329. package/src/feedback/coverage-learner.ts +0 -456
  330. package/src/feedback/feedback-loop.ts +0 -426
  331. package/src/feedback/index.ts +0 -72
  332. package/src/feedback/pattern-promotion.ts +0 -373
  333. package/src/feedback/quality-score-calculator.ts +0 -334
  334. package/src/feedback/test-outcome-tracker.ts +0 -450
  335. package/src/feedback/types.ts +0 -497
  336. package/src/index.ts +0 -224
  337. package/src/init/agents-installer.ts +0 -536
  338. package/src/init/index.ts +0 -80
  339. package/src/init/init-wizard.ts +0 -1061
  340. package/src/init/project-analyzer.ts +0 -696
  341. package/src/init/self-configurator.ts +0 -488
  342. package/src/init/skills-installer.ts +0 -467
  343. package/src/init/types.ts +0 -432
  344. package/src/integrations/ruvector/ast-complexity.ts +0 -470
  345. package/src/integrations/ruvector/coverage-router.ts +0 -594
  346. package/src/integrations/ruvector/diff-risk-classifier.ts +0 -759
  347. package/src/integrations/ruvector/fallback.ts +0 -942
  348. package/src/integrations/ruvector/graph-boundaries.ts +0 -809
  349. package/src/integrations/ruvector/index.ts +0 -363
  350. package/src/integrations/ruvector/interfaces.ts +0 -609
  351. package/src/integrations/ruvector/q-learning-router.ts +0 -550
  352. package/src/kernel/agent-coordinator.ts +0 -165
  353. package/src/kernel/agentdb-backend.ts +0 -504
  354. package/src/kernel/event-bus.ts +0 -129
  355. package/src/kernel/hybrid-backend.ts +0 -538
  356. package/src/kernel/index.ts +0 -28
  357. package/src/kernel/interfaces.ts +0 -257
  358. package/src/kernel/kernel.ts +0 -285
  359. package/src/kernel/memory-backend.ts +0 -169
  360. package/src/kernel/memory-factory.ts +0 -293
  361. package/src/kernel/plugin-loader.ts +0 -179
  362. package/src/learning/index.ts +0 -219
  363. package/src/learning/pattern-store.ts +0 -990
  364. package/src/learning/qe-guidance.ts +0 -832
  365. package/src/learning/qe-hooks.ts +0 -644
  366. package/src/learning/qe-patterns.ts +0 -449
  367. package/src/learning/qe-reasoning-bank.ts +0 -951
  368. package/src/learning/real-embeddings.ts +0 -277
  369. package/src/learning/real-qe-reasoning-bank.ts +0 -833
  370. package/src/learning/sqlite-persistence.ts +0 -554
  371. package/src/mcp/entry.ts +0 -59
  372. package/src/mcp/handlers/agent-handlers.ts +0 -285
  373. package/src/mcp/handlers/core-handlers.ts +0 -317
  374. package/src/mcp/handlers/domain-handlers.ts +0 -1444
  375. package/src/mcp/handlers/index.ts +0 -57
  376. package/src/mcp/handlers/memory-handlers.ts +0 -338
  377. package/src/mcp/handlers/task-handlers.ts +0 -363
  378. package/src/mcp/index.ts +0 -30
  379. package/src/mcp/metrics/index.ts +0 -14
  380. package/src/mcp/metrics/metrics-collector.ts +0 -503
  381. package/src/mcp/protocol-server.ts +0 -752
  382. package/src/mcp/security/cve-prevention.ts +0 -742
  383. package/src/mcp/security/index.ts +0 -356
  384. package/src/mcp/security/oauth21-provider.ts +0 -821
  385. package/src/mcp/security/rate-limiter.ts +0 -615
  386. package/src/mcp/security/sampling-server.ts +0 -662
  387. package/src/mcp/security/schema-validator.ts +0 -855
  388. package/src/mcp/server.ts +0 -657
  389. package/src/mcp/tool-registry.ts +0 -391
  390. package/src/mcp/tools/base.ts +0 -399
  391. package/src/mcp/tools/chaos-resilience/inject.ts +0 -699
  392. package/src/mcp/tools/code-intelligence/analyze.ts +0 -745
  393. package/src/mcp/tools/contract-testing/validate.ts +0 -708
  394. package/src/mcp/tools/coverage-analysis/index.ts +0 -770
  395. package/src/mcp/tools/defect-intelligence/predict.ts +0 -466
  396. package/src/mcp/tools/index.ts +0 -214
  397. package/src/mcp/tools/learning-optimization/optimize.ts +0 -772
  398. package/src/mcp/tools/quality-assessment/evaluate.ts +0 -385
  399. package/src/mcp/tools/registry.ts +0 -248
  400. package/src/mcp/tools/requirements-validation/validate.ts +0 -394
  401. package/src/mcp/tools/security-compliance/scan.ts +0 -365
  402. package/src/mcp/tools/test-execution/execute.ts +0 -291
  403. package/src/mcp/tools/test-generation/generate.ts +0 -544
  404. package/src/mcp/tools/visual-accessibility/index.ts +0 -791
  405. package/src/mcp/transport/index.ts +0 -31
  406. package/src/mcp/transport/stdio.ts +0 -318
  407. package/src/mcp/types.ts +0 -543
  408. package/src/neural-optimizer/index.ts +0 -111
  409. package/src/neural-optimizer/replay-buffer.ts +0 -455
  410. package/src/neural-optimizer/swarm-topology.ts +0 -508
  411. package/src/neural-optimizer/topology-optimizer.ts +0 -828
  412. package/src/neural-optimizer/types.ts +0 -481
  413. package/src/neural-optimizer/value-network.ts +0 -351
  414. package/src/optimization/auto-tuner.ts +0 -817
  415. package/src/optimization/index.ts +0 -77
  416. package/src/optimization/metric-collectors.ts +0 -474
  417. package/src/optimization/qe-workers.ts +0 -704
  418. package/src/optimization/tuning-algorithm.ts +0 -401
  419. package/src/optimization/types.ts +0 -314
  420. package/src/routing/index.ts +0 -51
  421. package/src/routing/qe-agent-registry.ts +0 -963
  422. package/src/routing/qe-task-router.ts +0 -564
  423. package/src/routing/routing-feedback.ts +0 -365
  424. package/src/routing/types.ts +0 -406
  425. package/src/shared/embeddings/embedding-cache.ts +0 -157
  426. package/src/shared/embeddings/index.ts +0 -50
  427. package/src/shared/embeddings/nomic-embedder.ts +0 -404
  428. package/src/shared/embeddings/ollama-client.ts +0 -195
  429. package/src/shared/embeddings/types.ts +0 -147
  430. package/src/shared/entities/agent.ts +0 -141
  431. package/src/shared/entities/base-entity.ts +0 -79
  432. package/src/shared/entities/index.ts +0 -6
  433. package/src/shared/events/domain-events.ts +0 -259
  434. package/src/shared/events/index.ts +0 -5
  435. package/src/shared/git/git-analyzer.ts +0 -656
  436. package/src/shared/git/index.ts +0 -11
  437. package/src/shared/http/http-client.ts +0 -420
  438. package/src/shared/http/index.ts +0 -13
  439. package/src/shared/index.ts +0 -41
  440. package/src/shared/io/file-reader.ts +0 -525
  441. package/src/shared/io/index.ts +0 -25
  442. package/src/shared/llm/cache.ts +0 -473
  443. package/src/shared/llm/circuit-breaker.ts +0 -369
  444. package/src/shared/llm/cost-tracker.ts +0 -460
  445. package/src/shared/llm/index.ts +0 -140
  446. package/src/shared/llm/interfaces.ts +0 -629
  447. package/src/shared/llm/provider-manager.ts +0 -685
  448. package/src/shared/llm/providers/claude.ts +0 -524
  449. package/src/shared/llm/providers/index.ts +0 -8
  450. package/src/shared/llm/providers/ollama.ts +0 -575
  451. package/src/shared/llm/providers/openai.ts +0 -609
  452. package/src/shared/metrics/code-metrics.ts +0 -520
  453. package/src/shared/metrics/index.ts +0 -23
  454. package/src/shared/metrics/system-metrics.ts +0 -353
  455. package/src/shared/parsers/index.ts +0 -6
  456. package/src/shared/parsers/typescript-parser.ts +0 -841
  457. package/src/shared/security/compliance-patterns.ts +0 -666
  458. package/src/shared/security/index.ts +0 -30
  459. package/src/shared/security/osv-client.ts +0 -468
  460. package/src/shared/types/index.ts +0 -150
  461. package/src/shared/value-objects/index.ts +0 -273
  462. package/src/strange-loop/healing-controller.ts +0 -833
  463. package/src/strange-loop/index.ts +0 -104
  464. package/src/strange-loop/self-model.ts +0 -494
  465. package/src/strange-loop/strange-loop.ts +0 -446
  466. package/src/strange-loop/swarm-observer.ts +0 -448
  467. package/src/strange-loop/topology-analyzer.ts +0 -565
  468. package/src/strange-loop/types.ts +0 -640
  469. package/src/time-crystal/default-phases.ts +0 -520
  470. package/src/time-crystal/index.ts +0 -164
  471. package/src/time-crystal/oscillator.ts +0 -425
  472. package/src/time-crystal/phase-executor.ts +0 -521
  473. package/src/time-crystal/scheduler.ts +0 -1025
  474. package/src/time-crystal/test-runner.ts +0 -787
  475. package/src/time-crystal/types.ts +0 -421
  476. package/src/workers/base-worker.ts +0 -304
  477. package/src/workers/daemon.ts +0 -264
  478. package/src/workers/index.ts +0 -119
  479. package/src/workers/interfaces.ts +0 -393
  480. package/src/workers/worker-manager.ts +0 -424
  481. package/src/workers/workers/compliance-checker.ts +0 -445
  482. package/src/workers/workers/coverage-tracker.ts +0 -344
  483. package/src/workers/workers/defect-predictor.ts +0 -375
  484. package/src/workers/workers/flaky-detector.ts +0 -390
  485. package/src/workers/workers/index.ts +0 -17
  486. package/src/workers/workers/learning-consolidation.ts +0 -442
  487. package/src/workers/workers/performance-baseline.ts +0 -434
  488. package/src/workers/workers/quality-gate.ts +0 -419
  489. package/src/workers/workers/regression-monitor.ts +0 -357
  490. package/src/workers/workers/security-scan.ts +0 -349
  491. package/src/workers/workers/test-health.ts +0 -359
  492. package/tests/integration/code-intelligence/knowledge-graph-real.test.ts +0 -540
  493. package/tests/integration/coordination/cross-domain-router.test.ts +0 -403
  494. package/tests/integration/coordination/protocol-executor.test.ts +0 -454
  495. package/tests/integration/coordination/workflow-orchestrator.test.ts +0 -418
  496. package/tests/integration/feedback/feedback-loop-integration.test.ts +0 -560
  497. package/tests/integration/migration/v2-to-v3-migration.test.ts +0 -471
  498. package/tests/integration/parsers/typescript-parser.test.ts +0 -463
  499. package/tests/integration/security/vulnerability-detection.test.ts +0 -628
  500. package/tests/integration/test-execution/coordinator.test.ts +0 -410
  501. package/tests/integration/test-generation/coordinator.test.ts +0 -361
  502. package/tests/mocks/index.ts +0 -228
  503. package/tests/time-crystal/default-phases.test.ts +0 -476
  504. package/tests/time-crystal/oscillator.test.ts +0 -541
  505. package/tests/time-crystal/phase-executor.test.ts +0 -653
  506. package/tests/time-crystal/scheduler.test.ts +0 -626
  507. package/tests/time-crystal/test-runner.test.ts +0 -594
  508. package/tests/unit/causal-discovery/causal-graph.test.ts +0 -504
  509. package/tests/unit/causal-discovery/causal-root-cause-analyzer.test.ts +0 -347
  510. package/tests/unit/causal-discovery/discovery-engine.test.ts +0 -435
  511. package/tests/unit/causal-discovery/weight-matrix.test.ts +0 -328
  512. package/tests/unit/cli/cli.test.ts +0 -341
  513. package/tests/unit/cli/commands.test.ts +0 -414
  514. package/tests/unit/cli/init-command.test.ts +0 -274
  515. package/tests/unit/cli/migrate-command.test.ts +0 -396
  516. package/tests/unit/coordination/claims/claim-service.test.ts +0 -949
  517. package/tests/unit/coordination/claims/handoff-manager.test.ts +0 -773
  518. package/tests/unit/coordination/claims/work-stealing.test.ts +0 -492
  519. package/tests/unit/coordination/queen-coordinator.test.ts +0 -966
  520. package/tests/unit/coordination/result-saver.test.ts +0 -653
  521. package/tests/unit/coordination/task-executor.test.ts +0 -810
  522. package/tests/unit/domains/chaos-resilience/chaos-engineer.test.ts +0 -484
  523. package/tests/unit/domains/chaos-resilience/load-tester.test.ts +0 -559
  524. package/tests/unit/domains/chaos-resilience/performance-profiler.test.ts +0 -490
  525. package/tests/unit/domains/code-intelligence/impact-analyzer.test.ts +0 -560
  526. package/tests/unit/domains/code-intelligence/knowledge-graph.test.ts +0 -460
  527. package/tests/unit/domains/code-intelligence/semantic-analyzer.test.ts +0 -584
  528. package/tests/unit/domains/contract-testing/api-compatibility.test.ts +0 -483
  529. package/tests/unit/domains/contract-testing/contract-validator.test.ts +0 -370
  530. package/tests/unit/domains/contract-testing/schema-validator.test.ts +0 -610
  531. package/tests/unit/domains/coverage-analysis/coverage-embedder.test.ts +0 -298
  532. package/tests/unit/domains/coverage-analysis/hnsw-index.test.ts +0 -292
  533. package/tests/unit/domains/coverage-analysis/sublinear-analyzer.test.ts +0 -506
  534. package/tests/unit/domains/defect-intelligence/defect-predictor.test.ts +0 -370
  535. package/tests/unit/domains/defect-intelligence/pattern-learner.test.ts +0 -546
  536. package/tests/unit/domains/defect-intelligence/root-cause-analyzer.test.ts +0 -534
  537. package/tests/unit/domains/learning-optimization/learning-coordinator.test.ts +0 -541
  538. package/tests/unit/domains/learning-optimization/metrics-optimizer.test.ts +0 -552
  539. package/tests/unit/domains/learning-optimization/production-intel.test.ts +0 -589
  540. package/tests/unit/domains/learning-optimization/transfer-specialist.test.ts +0 -453
  541. package/tests/unit/domains/quality-assessment/coherence-gate.test.ts +0 -1006
  542. package/tests/unit/domains/quality-assessment/deployment-advisor.test.ts +0 -515
  543. package/tests/unit/domains/quality-assessment/quality-analyzer.test.ts +0 -401
  544. package/tests/unit/domains/quality-assessment/quality-gate.test.ts +0 -324
  545. package/tests/unit/domains/requirements-validation/bdd-scenario-writer.test.ts +0 -479
  546. package/tests/unit/domains/requirements-validation/requirements-validator.test.ts +0 -452
  547. package/tests/unit/domains/requirements-validation/testability-scorer.test.ts +0 -505
  548. package/tests/unit/domains/security-compliance/compliance-validator.test.ts +0 -500
  549. package/tests/unit/domains/security-compliance/security-auditor.test.ts +0 -498
  550. package/tests/unit/domains/security-compliance/security-scanner.test.ts +0 -412
  551. package/tests/unit/domains/visual-accessibility/accessibility-tester.test.ts +0 -432
  552. package/tests/unit/domains/visual-accessibility/responsive-tester.test.ts +0 -506
  553. package/tests/unit/domains/visual-accessibility/visual-tester.test.ts +0 -412
  554. package/tests/unit/early-exit/early-exit-controller.test.ts +0 -548
  555. package/tests/unit/early-exit/early-exit-decision.test.ts +0 -617
  556. package/tests/unit/early-exit/index.test.ts +0 -254
  557. package/tests/unit/early-exit/quality-signal.test.ts +0 -589
  558. package/tests/unit/early-exit/speculative-executor.test.ts +0 -453
  559. package/tests/unit/feedback/coverage-learner.test.ts +0 -288
  560. package/tests/unit/feedback/feedback-loop.test.ts +0 -458
  561. package/tests/unit/feedback/pattern-promotion.test.ts +0 -390
  562. package/tests/unit/feedback/quality-score-calculator.test.ts +0 -364
  563. package/tests/unit/feedback/test-outcome-tracker.test.ts +0 -243
  564. package/tests/unit/init/init-wizard.test.ts +0 -881
  565. package/tests/unit/init/project-analyzer.test.ts +0 -807
  566. package/tests/unit/init/self-configurator.test.ts +0 -493
  567. package/tests/unit/integrations/ruvector/ast-complexity.test.ts +0 -240
  568. package/tests/unit/integrations/ruvector/coverage-router.test.ts +0 -366
  569. package/tests/unit/integrations/ruvector/diff-risk-classifier.test.ts +0 -340
  570. package/tests/unit/integrations/ruvector/graph-boundaries.test.ts +0 -355
  571. package/tests/unit/integrations/ruvector/q-learning-router.test.ts +0 -314
  572. package/tests/unit/kernel/agent-coordinator.test.ts +0 -220
  573. package/tests/unit/kernel/event-bus.test.ts +0 -197
  574. package/tests/unit/learning/qe-reasoning-bank.test.ts +0 -666
  575. package/tests/unit/learning/real-qe-reasoning-bank.benchmark.test.ts +0 -415
  576. package/tests/unit/mcp/mcp-server.test.ts +0 -544
  577. package/tests/unit/mcp/metrics/metrics-collector.test.ts +0 -340
  578. package/tests/unit/mcp/security/cve-prevention.test.ts +0 -512
  579. package/tests/unit/mcp/security/oauth21-provider.test.ts +0 -624
  580. package/tests/unit/mcp/security/rate-limiter.test.ts +0 -410
  581. package/tests/unit/mcp/security/sampling-server.test.ts +0 -420
  582. package/tests/unit/mcp/security/schema-validator.test.ts +0 -494
  583. package/tests/unit/mcp/tools/base.test.ts +0 -336
  584. package/tests/unit/mcp/tools/domain-tools.test.ts +0 -759
  585. package/tests/unit/mcp/tools/registry.test.ts +0 -240
  586. package/tests/unit/neural-optimizer/replay-buffer.test.ts +0 -403
  587. package/tests/unit/neural-optimizer/swarm-topology.test.ts +0 -473
  588. package/tests/unit/neural-optimizer/topology-optimizer.test.ts +0 -595
  589. package/tests/unit/neural-optimizer/value-network.test.ts +0 -343
  590. package/tests/unit/optimization/auto-tuner.test.ts +0 -506
  591. package/tests/unit/optimization/metric-collectors.test.ts +0 -352
  592. package/tests/unit/optimization/qe-workers.test.ts +0 -407
  593. package/tests/unit/optimization/tuning-algorithm.test.ts +0 -467
  594. package/tests/unit/routing/qe-agent-registry.test.ts +0 -229
  595. package/tests/unit/routing/qe-task-router.test.ts +0 -390
  596. package/tests/unit/routing/routing-feedback.test.ts +0 -339
  597. package/tests/unit/shared/embeddings/nomic-embedder.test.ts +0 -419
  598. package/tests/unit/shared/http/http-client.test.ts +0 -719
  599. package/tests/unit/shared/io/file-reader.test.ts +0 -511
  600. package/tests/unit/shared/llm/cache.test.ts +0 -391
  601. package/tests/unit/shared/llm/circuit-breaker.test.ts +0 -293
  602. package/tests/unit/shared/llm/cost-tracker.test.ts +0 -431
  603. package/tests/unit/shared/llm/provider-manager.test.ts +0 -550
  604. package/tests/unit/shared/llm/providers.test.ts +0 -532
  605. package/tests/unit/shared/parsers/typescript-parser.test.ts +0 -693
  606. package/tests/unit/shared/value-objects.test.ts +0 -184
  607. package/tests/unit/strange-loop/strange-loop.test.ts +0 -1170
  608. package/tests/unit/workers/base-worker.test.ts +0 -341
  609. package/tests/unit/workers/daemon.test.ts +0 -291
  610. package/tests/unit/workers/worker-manager.test.ts +0 -284
  611. package/tsconfig.json +0 -32
  612. package/vitest.config.ts +0 -27
@@ -1,2750 +0,0 @@
1
- /**
2
- * Agentic QE v3 - Test Generation Service
3
- * Implements ITestGenerationService for AI-powered test generation
4
- *
5
- * Uses @faker-js/faker for realistic test data generation
6
- * Uses TypeScript AST parser for code analysis
7
- */
8
-
9
- import { v4 as uuidv4 } from 'uuid';
10
- import * as fs from 'fs';
11
- import * as path from 'path';
12
- import * as ts from 'typescript';
13
- import { faker } from '@faker-js/faker';
14
- import { Result, ok, err } from '../../../shared/types';
15
- import { MemoryBackend } from '../../../kernel/interfaces';
16
- import {
17
- GenerateTestsRequest,
18
- GeneratedTests,
19
- GeneratedTest,
20
- TDDRequest,
21
- TDDResult,
22
- PropertyTestRequest,
23
- PropertyTests,
24
- TestDataRequest,
25
- TestData,
26
- Pattern,
27
- } from '../interfaces';
28
-
29
- /**
30
- * Interface for the test generation service
31
- */
32
- export interface ITestGenerationService {
33
- generateTests(request: GenerateTestsRequest): Promise<Result<GeneratedTests, Error>>;
34
- generateForCoverageGap(
35
- file: string,
36
- uncoveredLines: number[],
37
- framework: string
38
- ): Promise<Result<GeneratedTest[], Error>>;
39
- generateTDDTests(request: TDDRequest): Promise<Result<TDDResult, Error>>;
40
- generatePropertyTests(request: PropertyTestRequest): Promise<Result<PropertyTests, Error>>;
41
- generateTestData(request: TestDataRequest): Promise<Result<TestData, Error>>;
42
- }
43
-
44
- /**
45
- * Configuration for the test generator
46
- */
47
- export interface TestGeneratorConfig {
48
- defaultFramework: 'jest' | 'vitest' | 'mocha' | 'pytest';
49
- maxTestsPerFile: number;
50
- coverageTargetDefault: number;
51
- enableAIGeneration: boolean;
52
- }
53
-
54
- const DEFAULT_CONFIG: TestGeneratorConfig = {
55
- defaultFramework: 'jest',
56
- maxTestsPerFile: 50,
57
- coverageTargetDefault: 80,
58
- enableAIGeneration: true,
59
- };
60
-
61
- /**
62
- * Information about a function extracted from AST
63
- */
64
- interface FunctionInfo {
65
- name: string;
66
- parameters: ParameterInfo[];
67
- returnType: string | undefined;
68
- isAsync: boolean;
69
- isExported: boolean;
70
- complexity: number;
71
- startLine: number;
72
- endLine: number;
73
- body?: string;
74
- }
75
-
76
- /**
77
- * Information about a class extracted from AST
78
- */
79
- interface ClassInfo {
80
- name: string;
81
- methods: FunctionInfo[];
82
- properties: PropertyInfo[];
83
- isExported: boolean;
84
- hasConstructor: boolean;
85
- constructorParams?: ParameterInfo[];
86
- }
87
-
88
- /**
89
- * Information about a parameter
90
- */
91
- interface ParameterInfo {
92
- name: string;
93
- type: string | undefined;
94
- optional: boolean;
95
- defaultValue: string | undefined;
96
- }
97
-
98
- /**
99
- * Information about a class property
100
- */
101
- interface PropertyInfo {
102
- name: string;
103
- type: string | undefined;
104
- isPrivate: boolean;
105
- isReadonly: boolean;
106
- }
107
-
108
- /**
109
- * Test case definition
110
- */
111
- interface TestCase {
112
- description: string;
113
- type: 'happy-path' | 'edge-case' | 'error-handling' | 'boundary';
114
- setup?: string;
115
- action: string;
116
- assertion: string;
117
- }
118
-
119
- /**
120
- * Data schema field definition
121
- */
122
- interface SchemaField {
123
- type: string;
124
- faker?: string;
125
- min?: number;
126
- max?: number;
127
- enum?: string[];
128
- pattern?: string;
129
- reference?: string;
130
- }
131
-
132
- /**
133
- * Test Generation Service Implementation
134
- * Uses heuristic analysis and AST parsing to generate test cases from source code
135
- * Supports TDD workflow, property-based testing, and pattern-aware generation
136
- */
137
- export class TestGeneratorService implements ITestGenerationService {
138
- private readonly config: TestGeneratorConfig;
139
-
140
- constructor(
141
- private readonly memory: MemoryBackend,
142
- config: Partial<TestGeneratorConfig> = {}
143
- ) {
144
- this.config = { ...DEFAULT_CONFIG, ...config };
145
- }
146
-
147
- /**
148
- * Generate tests for given source files
149
- */
150
- async generateTests(request: GenerateTestsRequest): Promise<Result<GeneratedTests, Error>> {
151
- try {
152
- const {
153
- sourceFiles,
154
- testType,
155
- framework,
156
- coverageTarget = this.config.coverageTargetDefault,
157
- patterns = [],
158
- } = request;
159
-
160
- if (sourceFiles.length === 0) {
161
- return err(new Error('No source files provided'));
162
- }
163
-
164
- const tests: GeneratedTest[] = [];
165
- const patternsUsed: string[] = [];
166
-
167
- // Process each source file
168
- for (const sourceFile of sourceFiles) {
169
- const fileTests = await this.generateTestsForFile(
170
- sourceFile,
171
- testType,
172
- framework,
173
- patterns
174
- );
175
-
176
- if (fileTests.success) {
177
- tests.push(...fileTests.value.tests);
178
- patternsUsed.push(...fileTests.value.patternsUsed);
179
- }
180
- }
181
-
182
- // Calculate coverage estimate based on test count and complexity
183
- const coverageEstimate = this.estimateCoverage(tests, coverageTarget);
184
-
185
- // Store generation metadata in memory
186
- await this.storeGenerationMetadata(tests, patternsUsed);
187
-
188
- return ok({
189
- tests,
190
- coverageEstimate,
191
- patternsUsed: [...new Set(patternsUsed)],
192
- });
193
- } catch (error) {
194
- return err(error instanceof Error ? error : new Error(String(error)));
195
- }
196
- }
197
-
198
- /**
199
- * Generate tests specifically targeting coverage gaps
200
- */
201
- async generateForCoverageGap(
202
- file: string,
203
- uncoveredLines: number[],
204
- framework: string
205
- ): Promise<Result<GeneratedTest[], Error>> {
206
- try {
207
- if (uncoveredLines.length === 0) {
208
- return ok([]);
209
- }
210
-
211
- // Analyze uncovered lines and generate targeted tests
212
- // Groups consecutive lines and generates tests for each block
213
- const tests: GeneratedTest[] = [];
214
-
215
- // Group uncovered lines into logical blocks
216
- const lineGroups = this.groupConsecutiveLines(uncoveredLines);
217
-
218
- for (const group of lineGroups) {
219
- const test = await this.generateTestForLines(file, group, framework);
220
- if (test) {
221
- tests.push(test);
222
- }
223
- }
224
-
225
- return ok(tests);
226
- } catch (error) {
227
- return err(error instanceof Error ? error : new Error(String(error)));
228
- }
229
- }
230
-
231
- /**
232
- * Generate tests following TDD workflow
233
- */
234
- async generateTDDTests(request: TDDRequest): Promise<Result<TDDResult, Error>> {
235
- try {
236
- const { feature, behavior, framework, phase } = request;
237
-
238
- switch (phase) {
239
- case 'red':
240
- // Generate failing test first
241
- return ok(await this.generateRedPhaseTest(feature, behavior, framework));
242
-
243
- case 'green':
244
- // Generate minimal implementation to make test pass
245
- return ok(await this.generateGreenPhaseCode(feature, behavior, framework));
246
-
247
- case 'refactor':
248
- // Suggest refactoring improvements
249
- return ok(await this.generateRefactoringSuggestions(feature, behavior));
250
-
251
- default:
252
- return err(new Error(`Unknown TDD phase: ${phase}`));
253
- }
254
- } catch (error) {
255
- return err(error instanceof Error ? error : new Error(String(error)));
256
- }
257
- }
258
-
259
- /**
260
- * Generate property-based tests
261
- */
262
- async generatePropertyTests(
263
- request: PropertyTestRequest
264
- ): Promise<Result<PropertyTests, Error>> {
265
- try {
266
- const { function: funcName, properties, constraints = {} } = request;
267
-
268
- // Generate property-based tests using fast-check generators
269
- const tests = properties.map((property) => ({
270
- property,
271
- testCode: this.generatePropertyTestCode(funcName, property, constraints),
272
- generators: this.inferGenerators(property, constraints),
273
- }));
274
-
275
- return ok({
276
- tests,
277
- arbitraries: this.collectArbitraries(tests),
278
- });
279
- } catch (error) {
280
- return err(error instanceof Error ? error : new Error(String(error)));
281
- }
282
- }
283
-
284
- /**
285
- * Generate test data based on schema
286
- */
287
- async generateTestData(request: TestDataRequest): Promise<Result<TestData, Error>> {
288
- try {
289
- const { schema, count, locale = 'en', preserveRelationships = false } = request;
290
-
291
- // Generate test data using @faker-js/faker with seeded randomness
292
- const seed = Date.now();
293
- const records: unknown[] = [];
294
-
295
- for (let i = 0; i < count; i++) {
296
- const record = this.generateRecordFromSchema(schema, seed + i, locale);
297
- records.push(record);
298
- }
299
-
300
- // Handle relationships if needed
301
- if (preserveRelationships) {
302
- this.linkRelatedRecords(records, schema);
303
- }
304
-
305
- return ok({
306
- records,
307
- schema,
308
- seed,
309
- });
310
- } catch (error) {
311
- return err(error instanceof Error ? error : new Error(String(error)));
312
- }
313
- }
314
-
315
- // ============================================================================
316
- // Private Helper Methods
317
- // ============================================================================
318
-
319
- private async generateTestsForFile(
320
- sourceFile: string,
321
- testType: 'unit' | 'integration' | 'e2e',
322
- framework: string,
323
- patterns: string[]
324
- ): Promise<Result<{ tests: GeneratedTest[]; patternsUsed: string[] }, Error>> {
325
- const testFile = this.getTestFilePath(sourceFile, framework);
326
- const patternsUsed: string[] = [];
327
-
328
- // Look for applicable patterns from memory
329
- const applicablePatterns = await this.findApplicablePatterns(sourceFile, patterns);
330
- patternsUsed.push(...applicablePatterns.map((p) => p.name));
331
-
332
- // Try to read and parse the source file for real AST analysis
333
- let codeAnalysis: { functions: FunctionInfo[]; classes: ClassInfo[] } | null = null;
334
- try {
335
- const content = fs.readFileSync(sourceFile, 'utf-8');
336
- codeAnalysis = this.analyzeSourceCode(content, sourceFile);
337
- } catch {
338
- // File doesn't exist or can't be read - use stub generation
339
- }
340
-
341
- // Generate test code based on analysis or fall back to stub
342
- let testCode: string;
343
- if (codeAnalysis && (codeAnalysis.functions.length > 0 || codeAnalysis.classes.length > 0)) {
344
- testCode = this.generateRealTestCode(
345
- sourceFile,
346
- testType,
347
- framework,
348
- codeAnalysis,
349
- applicablePatterns
350
- );
351
- } else {
352
- testCode = this.generateStubTestCode(sourceFile, testType, framework, applicablePatterns);
353
- }
354
-
355
- const test: GeneratedTest = {
356
- id: uuidv4(),
357
- name: `${this.extractModuleName(sourceFile)} tests`,
358
- sourceFile,
359
- testFile,
360
- testCode,
361
- type: testType,
362
- assertions: this.countAssertions(testCode),
363
- };
364
-
365
- return ok({ tests: [test], patternsUsed });
366
- }
367
-
368
- /**
369
- * Analyze source code using TypeScript AST
370
- */
371
- private analyzeSourceCode(
372
- content: string,
373
- fileName: string
374
- ): { functions: FunctionInfo[]; classes: ClassInfo[] } {
375
- const sourceFile = ts.createSourceFile(
376
- path.basename(fileName),
377
- content,
378
- ts.ScriptTarget.Latest,
379
- true,
380
- ts.ScriptKind.TS
381
- );
382
-
383
- const functions: FunctionInfo[] = [];
384
- const classes: ClassInfo[] = [];
385
-
386
- const visit = (node: ts.Node): void => {
387
- // Extract function declarations
388
- if (ts.isFunctionDeclaration(node) && node.name) {
389
- functions.push(this.extractFunctionInfo(node, sourceFile));
390
- }
391
- // Extract arrow functions assigned to variables
392
- else if (ts.isVariableStatement(node)) {
393
- for (const declaration of node.declarationList.declarations) {
394
- if (
395
- ts.isVariableDeclaration(declaration) &&
396
- declaration.initializer &&
397
- (ts.isArrowFunction(declaration.initializer) ||
398
- ts.isFunctionExpression(declaration.initializer))
399
- ) {
400
- const name = declaration.name.getText(sourceFile);
401
- functions.push(
402
- this.extractArrowFunctionInfo(name, declaration.initializer, sourceFile, node)
403
- );
404
- }
405
- }
406
- }
407
- // Extract class declarations
408
- else if (ts.isClassDeclaration(node) && node.name) {
409
- classes.push(this.extractClassInfo(node, sourceFile));
410
- }
411
-
412
- ts.forEachChild(node, visit);
413
- };
414
-
415
- ts.forEachChild(sourceFile, visit);
416
- return { functions, classes };
417
- }
418
-
419
- /**
420
- * Extract function information from AST
421
- */
422
- private extractFunctionInfo(
423
- node: ts.FunctionDeclaration,
424
- sourceFile: ts.SourceFile
425
- ): FunctionInfo {
426
- const name = node.name?.getText(sourceFile) || 'anonymous';
427
- const parameters = this.extractParameters(node.parameters, sourceFile);
428
- const returnType = node.type?.getText(sourceFile);
429
- const isAsync = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) ?? false;
430
- const isExported = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
431
-
432
- const { line: startLine } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
433
- const { line: endLine } = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
434
-
435
- return {
436
- name,
437
- parameters,
438
- returnType,
439
- isAsync,
440
- isExported,
441
- complexity: this.calculateComplexity(node),
442
- startLine: startLine + 1,
443
- endLine: endLine + 1,
444
- body: node.body?.getText(sourceFile),
445
- };
446
- }
447
-
448
- /**
449
- * Extract arrow function information from AST
450
- */
451
- private extractArrowFunctionInfo(
452
- name: string,
453
- node: ts.ArrowFunction | ts.FunctionExpression,
454
- sourceFile: ts.SourceFile,
455
- parentNode: ts.Node
456
- ): FunctionInfo {
457
- const parameters = this.extractParameters(node.parameters, sourceFile);
458
- const returnType = node.type?.getText(sourceFile);
459
- const isAsync = node.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) ?? false;
460
- const isExported =
461
- ts.isVariableStatement(parentNode) &&
462
- (parentNode.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false);
463
-
464
- const { line: startLine } = sourceFile.getLineAndCharacterOfPosition(node.getStart(sourceFile));
465
- const { line: endLine } = sourceFile.getLineAndCharacterOfPosition(node.getEnd());
466
-
467
- return {
468
- name,
469
- parameters,
470
- returnType,
471
- isAsync,
472
- isExported,
473
- complexity: this.calculateComplexity(node),
474
- startLine: startLine + 1,
475
- endLine: endLine + 1,
476
- body: node.body?.getText(sourceFile),
477
- };
478
- }
479
-
480
- /**
481
- * Extract class information from AST
482
- */
483
- private extractClassInfo(node: ts.ClassDeclaration, sourceFile: ts.SourceFile): ClassInfo {
484
- const name = node.name?.getText(sourceFile) || 'AnonymousClass';
485
- const methods: FunctionInfo[] = [];
486
- const properties: PropertyInfo[] = [];
487
- let hasConstructor = false;
488
- let constructorParams: ParameterInfo[] | undefined;
489
-
490
- const isExported =
491
- node.modifiers?.some((m) => m.kind === ts.SyntaxKind.ExportKeyword) ?? false;
492
-
493
- for (const member of node.members) {
494
- if (ts.isMethodDeclaration(member)) {
495
- const methodName = member.name.getText(sourceFile);
496
- const parameters = this.extractParameters(member.parameters, sourceFile);
497
- const returnType = member.type?.getText(sourceFile);
498
- const isAsync =
499
- member.modifiers?.some((m) => m.kind === ts.SyntaxKind.AsyncKeyword) ?? false;
500
-
501
- const { line: startLine } = sourceFile.getLineAndCharacterOfPosition(
502
- member.getStart(sourceFile)
503
- );
504
- const { line: endLine } = sourceFile.getLineAndCharacterOfPosition(member.getEnd());
505
-
506
- methods.push({
507
- name: methodName,
508
- parameters,
509
- returnType,
510
- isAsync,
511
- isExported: false,
512
- complexity: this.calculateComplexity(member),
513
- startLine: startLine + 1,
514
- endLine: endLine + 1,
515
- body: member.body?.getText(sourceFile),
516
- });
517
- } else if (ts.isConstructorDeclaration(member)) {
518
- hasConstructor = true;
519
- constructorParams = this.extractParameters(member.parameters, sourceFile);
520
- } else if (ts.isPropertyDeclaration(member)) {
521
- const propName = member.name.getText(sourceFile);
522
- const propType = member.type?.getText(sourceFile);
523
- const isPrivate =
524
- member.modifiers?.some((m) => m.kind === ts.SyntaxKind.PrivateKeyword) ?? false;
525
- const isReadonly =
526
- member.modifiers?.some((m) => m.kind === ts.SyntaxKind.ReadonlyKeyword) ?? false;
527
-
528
- properties.push({
529
- name: propName,
530
- type: propType,
531
- isPrivate,
532
- isReadonly,
533
- });
534
- }
535
- }
536
-
537
- return {
538
- name,
539
- methods,
540
- properties,
541
- isExported,
542
- hasConstructor,
543
- constructorParams,
544
- };
545
- }
546
-
547
- /**
548
- * Extract parameters from a function
549
- */
550
- private extractParameters(
551
- params: ts.NodeArray<ts.ParameterDeclaration>,
552
- sourceFile: ts.SourceFile
553
- ): ParameterInfo[] {
554
- return params.map((param) => ({
555
- name: param.name.getText(sourceFile),
556
- type: param.type?.getText(sourceFile),
557
- optional: param.questionToken !== undefined,
558
- defaultValue: param.initializer?.getText(sourceFile),
559
- }));
560
- }
561
-
562
- /**
563
- * Calculate cyclomatic complexity of a node
564
- */
565
- private calculateComplexity(node: ts.Node): number {
566
- let complexity = 1;
567
-
568
- const visit = (n: ts.Node): void => {
569
- switch (n.kind) {
570
- case ts.SyntaxKind.IfStatement:
571
- case ts.SyntaxKind.ForStatement:
572
- case ts.SyntaxKind.ForInStatement:
573
- case ts.SyntaxKind.ForOfStatement:
574
- case ts.SyntaxKind.WhileStatement:
575
- case ts.SyntaxKind.DoStatement:
576
- case ts.SyntaxKind.CaseClause:
577
- case ts.SyntaxKind.CatchClause:
578
- case ts.SyntaxKind.ConditionalExpression:
579
- complexity++;
580
- break;
581
- case ts.SyntaxKind.BinaryExpression: {
582
- const binary = n as ts.BinaryExpression;
583
- if (
584
- binary.operatorToken.kind === ts.SyntaxKind.AmpersandAmpersandToken ||
585
- binary.operatorToken.kind === ts.SyntaxKind.BarBarToken
586
- ) {
587
- complexity++;
588
- }
589
- break;
590
- }
591
- }
592
- ts.forEachChild(n, visit);
593
- };
594
-
595
- ts.forEachChild(node, visit);
596
- return complexity;
597
- }
598
-
599
- /**
600
- * Generate real test code based on AST analysis
601
- */
602
- private generateRealTestCode(
603
- sourceFile: string,
604
- testType: 'unit' | 'integration' | 'e2e',
605
- framework: string,
606
- analysis: { functions: FunctionInfo[]; classes: ClassInfo[] },
607
- patterns: Pattern[]
608
- ): string {
609
- const moduleName = this.extractModuleName(sourceFile);
610
- const importPath = this.getImportPath(sourceFile);
611
-
612
- switch (framework) {
613
- case 'jest':
614
- case 'vitest':
615
- return this.generateRealJestVitestTest(
616
- moduleName,
617
- importPath,
618
- testType,
619
- analysis,
620
- patterns,
621
- framework
622
- );
623
- case 'mocha':
624
- return this.generateRealMochaTest(moduleName, importPath, testType, analysis, patterns);
625
- case 'pytest':
626
- return this.generateRealPytestTest(moduleName, importPath, testType, analysis, patterns);
627
- default:
628
- return this.generateRealJestVitestTest(
629
- moduleName,
630
- importPath,
631
- testType,
632
- analysis,
633
- patterns,
634
- 'vitest'
635
- );
636
- }
637
- }
638
-
639
- /**
640
- * Generate real Jest/Vitest test code
641
- */
642
- private generateRealJestVitestTest(
643
- moduleName: string,
644
- importPath: string,
645
- testType: string,
646
- analysis: { functions: FunctionInfo[]; classes: ClassInfo[] },
647
- patterns: Pattern[],
648
- framework: string
649
- ): string {
650
- const patternComment =
651
- patterns.length > 0
652
- ? `// Applied patterns: ${patterns.map((p) => p.name).join(', ')}\n`
653
- : '';
654
-
655
- // Collect all exports to import
656
- const exports: string[] = [];
657
- for (const fn of analysis.functions) {
658
- if (fn.isExported) exports.push(fn.name);
659
- }
660
- for (const cls of analysis.classes) {
661
- if (cls.isExported) exports.push(cls.name);
662
- }
663
-
664
- const importStatement =
665
- exports.length > 0
666
- ? `import { ${exports.join(', ')} } from '${importPath}';`
667
- : `import * as ${moduleName} from '${importPath}';`;
668
-
669
- let testCode = `${patternComment}import { describe, it, expect, beforeEach${framework === 'vitest' ? ', vi' : ''} } from '${framework}';
670
- ${importStatement}
671
-
672
- `;
673
-
674
- // Generate tests for each function
675
- for (const fn of analysis.functions) {
676
- testCode += this.generateFunctionTests(fn, testType);
677
- }
678
-
679
- // Generate tests for each class
680
- for (const cls of analysis.classes) {
681
- testCode += this.generateClassTests(cls, testType);
682
- }
683
-
684
- return testCode;
685
- }
686
-
687
- /**
688
- * Generate tests for a function
689
- */
690
- private generateFunctionTests(fn: FunctionInfo, _testType: string): string {
691
- const testCases = this.generateTestCasesForFunction(fn);
692
-
693
- let code = `describe('${fn.name}', () => {\n`;
694
-
695
- for (const testCase of testCases) {
696
- if (testCase.setup) {
697
- code += ` ${testCase.setup}\n\n`;
698
- }
699
-
700
- const asyncPrefix = fn.isAsync ? 'async ' : '';
701
- code += ` it('${testCase.description}', ${asyncPrefix}() => {\n`;
702
- code += ` ${testCase.action}\n`;
703
- code += ` ${testCase.assertion}\n`;
704
- code += ` });\n\n`;
705
- }
706
-
707
- code += `});\n\n`;
708
- return code;
709
- }
710
-
711
- /**
712
- * Generate test cases for a function
713
- */
714
- private generateTestCasesForFunction(fn: FunctionInfo): TestCase[] {
715
- const testCases: TestCase[] = [];
716
-
717
- // Generate valid input test
718
- const validParams = fn.parameters.map((p) => this.generateTestValue(p)).join(', ');
719
- const fnCall = fn.isAsync ? `await ${fn.name}(${validParams})` : `${fn.name}(${validParams})`;
720
-
721
- testCases.push({
722
- description: 'should handle valid input correctly',
723
- type: 'happy-path',
724
- action: `const result = ${fnCall};`,
725
- assertion: 'expect(result).toBeDefined();',
726
- });
727
-
728
- // Generate tests for each parameter
729
- for (const param of fn.parameters) {
730
- if (!param.optional) {
731
- // Test with undefined
732
- const paramsWithUndefined = fn.parameters
733
- .map((p) => (p.name === param.name ? 'undefined' : this.generateTestValue(p)))
734
- .join(', ');
735
-
736
- testCases.push({
737
- description: `should handle undefined ${param.name}`,
738
- type: 'error-handling',
739
- action: fn.isAsync
740
- ? `const action = async () => await ${fn.name}(${paramsWithUndefined});`
741
- : `const action = () => ${fn.name}(${paramsWithUndefined});`,
742
- assertion: 'expect(action).toThrow();',
743
- });
744
- }
745
-
746
- // Type-specific boundary tests
747
- if (param.type?.includes('string')) {
748
- const paramsWithEmpty = fn.parameters
749
- .map((p) => (p.name === param.name ? "''" : this.generateTestValue(p)))
750
- .join(', ');
751
- const emptyCall = fn.isAsync
752
- ? `await ${fn.name}(${paramsWithEmpty})`
753
- : `${fn.name}(${paramsWithEmpty})`;
754
-
755
- testCases.push({
756
- description: `should handle empty string for ${param.name}`,
757
- type: 'boundary',
758
- action: `const result = ${emptyCall};`,
759
- assertion: 'expect(result).toBeDefined();',
760
- });
761
- }
762
-
763
- if (param.type?.includes('number')) {
764
- const paramsWithZero = fn.parameters
765
- .map((p) => (p.name === param.name ? '0' : this.generateTestValue(p)))
766
- .join(', ');
767
- const zeroCall = fn.isAsync
768
- ? `await ${fn.name}(${paramsWithZero})`
769
- : `${fn.name}(${paramsWithZero})`;
770
-
771
- testCases.push({
772
- description: `should handle zero for ${param.name}`,
773
- type: 'boundary',
774
- action: `const result = ${zeroCall};`,
775
- assertion: 'expect(result).toBeDefined();',
776
- });
777
-
778
- const paramsWithNegative = fn.parameters
779
- .map((p) => (p.name === param.name ? '-1' : this.generateTestValue(p)))
780
- .join(', ');
781
- const negativeCall = fn.isAsync
782
- ? `await ${fn.name}(${paramsWithNegative})`
783
- : `${fn.name}(${paramsWithNegative})`;
784
-
785
- testCases.push({
786
- description: `should handle negative value for ${param.name}`,
787
- type: 'edge-case',
788
- action: `const result = ${negativeCall};`,
789
- assertion: 'expect(result).toBeDefined();',
790
- });
791
- }
792
-
793
- if (param.type?.includes('[]') || param.type?.includes('Array')) {
794
- const paramsWithEmpty = fn.parameters
795
- .map((p) => (p.name === param.name ? '[]' : this.generateTestValue(p)))
796
- .join(', ');
797
- const emptyCall = fn.isAsync
798
- ? `await ${fn.name}(${paramsWithEmpty})`
799
- : `${fn.name}(${paramsWithEmpty})`;
800
-
801
- testCases.push({
802
- description: `should handle empty array for ${param.name}`,
803
- type: 'boundary',
804
- action: `const result = ${emptyCall};`,
805
- assertion: 'expect(result).toBeDefined();',
806
- });
807
- }
808
- }
809
-
810
- // Async rejection test
811
- if (fn.isAsync) {
812
- testCases.push({
813
- description: 'should handle async rejection gracefully',
814
- type: 'error-handling',
815
- action: `// Mock or setup to cause rejection`,
816
- assertion: `// await expect(${fn.name}(invalidParams)).rejects.toThrow();`,
817
- });
818
- }
819
-
820
- return testCases;
821
- }
822
-
823
- /**
824
- * Generate tests for a class
825
- */
826
- private generateClassTests(cls: ClassInfo, testType: string): string {
827
- let code = `describe('${cls.name}', () => {\n`;
828
- code += ` let instance: ${cls.name};\n\n`;
829
-
830
- // Setup
831
- if (cls.hasConstructor && cls.constructorParams) {
832
- const constructorArgs = cls.constructorParams
833
- .map((p) => this.generateTestValue(p))
834
- .join(', ');
835
- code += ` beforeEach(() => {\n`;
836
- code += ` instance = new ${cls.name}(${constructorArgs});\n`;
837
- code += ` });\n\n`;
838
- } else {
839
- code += ` beforeEach(() => {\n`;
840
- code += ` instance = new ${cls.name}();\n`;
841
- code += ` });\n\n`;
842
- }
843
-
844
- // Constructor test
845
- code += ` it('should instantiate correctly', () => {\n`;
846
- code += ` expect(instance).toBeInstanceOf(${cls.name});\n`;
847
- code += ` });\n\n`;
848
-
849
- // Generate tests for each public method
850
- for (const method of cls.methods) {
851
- if (!method.name.startsWith('_') && !method.name.startsWith('#')) {
852
- code += this.generateMethodTests(method, cls.name, testType);
853
- }
854
- }
855
-
856
- code += `});\n\n`;
857
- return code;
858
- }
859
-
860
- /**
861
- * Generate tests for a class method
862
- */
863
- private generateMethodTests(method: FunctionInfo, _className: string, _testType: string): string {
864
- let code = ` describe('${method.name}', () => {\n`;
865
-
866
- const validParams = method.parameters.map((p) => this.generateTestValue(p)).join(', ');
867
- const methodCall = method.isAsync
868
- ? `await instance.${method.name}(${validParams})`
869
- : `instance.${method.name}(${validParams})`;
870
-
871
- // Happy path
872
- const asyncPrefix = method.isAsync ? 'async ' : '';
873
- code += ` it('should execute successfully', ${asyncPrefix}() => {\n`;
874
- code += ` const result = ${methodCall};\n`;
875
- code += ` expect(result).toBeDefined();\n`;
876
- code += ` });\n`;
877
-
878
- // Error handling for non-optional params
879
- for (const param of method.parameters) {
880
- if (!param.optional) {
881
- const paramsWithUndefined = method.parameters
882
- .map((p) => (p.name === param.name ? 'undefined as any' : this.generateTestValue(p)))
883
- .join(', ');
884
-
885
- code += `\n it('should handle invalid ${param.name}', () => {\n`;
886
- code += ` expect(() => instance.${method.name}(${paramsWithUndefined})).toThrow();\n`;
887
- code += ` });\n`;
888
- }
889
- }
890
-
891
- code += ` });\n\n`;
892
- return code;
893
- }
894
-
895
- /**
896
- * Generate a test value for a parameter
897
- */
898
- private generateTestValue(param: ParameterInfo): string {
899
- if (param.defaultValue) {
900
- return param.defaultValue;
901
- }
902
-
903
- const type = param.type?.toLowerCase() || 'unknown';
904
- const name = param.name.toLowerCase();
905
-
906
- // Infer from param name first
907
- if (name.includes('id')) return `'${faker.string.uuid()}'`;
908
- if (name.includes('email')) return `'${faker.internet.email()}'`;
909
- if (name.includes('name')) return `'${faker.person.fullName()}'`;
910
- if (name.includes('url')) return `'${faker.internet.url()}'`;
911
- if (name.includes('date')) return `new Date('${faker.date.recent().toISOString()}')`;
912
-
913
- // Then by type
914
- if (type.includes('string')) return `'${faker.lorem.word()}'`;
915
- if (type.includes('number')) return String(faker.number.int({ min: 1, max: 100 }));
916
- if (type.includes('boolean')) return 'true';
917
- if (type.includes('[]') || type.includes('array')) return '[]';
918
- if (type.includes('object') || type.includes('{')) return '{}';
919
- if (type.includes('function')) return '() => {}';
920
- if (type.includes('promise')) return 'Promise.resolve()';
921
- if (type.includes('date')) return 'new Date()';
922
-
923
- // Default
924
- return `mock${param.name.charAt(0).toUpperCase() + param.name.slice(1)}`;
925
- }
926
-
927
- /**
928
- * Generate real Mocha test code
929
- */
930
- private generateRealMochaTest(
931
- moduleName: string,
932
- importPath: string,
933
- testType: string,
934
- analysis: { functions: FunctionInfo[]; classes: ClassInfo[] },
935
- patterns: Pattern[]
936
- ): string {
937
- const patternComment =
938
- patterns.length > 0
939
- ? `// Applied patterns: ${patterns.map((p) => p.name).join(', ')}\n`
940
- : '';
941
-
942
- const exports: string[] = [];
943
- for (const fn of analysis.functions) {
944
- if (fn.isExported) exports.push(fn.name);
945
- }
946
- for (const cls of analysis.classes) {
947
- if (cls.isExported) exports.push(cls.name);
948
- }
949
-
950
- const importStatement =
951
- exports.length > 0
952
- ? `import { ${exports.join(', ')} } from '${importPath}';`
953
- : `import * as ${moduleName} from '${importPath}';`;
954
-
955
- let code = `${patternComment}import { expect } from 'chai';
956
- ${importStatement}
957
-
958
- describe('${moduleName} - ${testType} tests', function() {
959
- `;
960
-
961
- for (const fn of analysis.functions) {
962
- code += this.generateMochaFunctionTests(fn);
963
- }
964
-
965
- for (const cls of analysis.classes) {
966
- code += this.generateMochaClassTests(cls);
967
- }
968
-
969
- code += `});\n`;
970
- return code;
971
- }
972
-
973
- /**
974
- * Generate Mocha tests for a function
975
- */
976
- private generateMochaFunctionTests(fn: FunctionInfo): string {
977
- const validParams = fn.parameters.map((p) => this.generateTestValue(p)).join(', ');
978
- const fnCall = fn.isAsync ? `await ${fn.name}(${validParams})` : `${fn.name}(${validParams})`;
979
-
980
- let code = ` describe('${fn.name}', function() {\n`;
981
- code += ` it('should handle valid input', ${fn.isAsync ? 'async ' : ''}function() {\n`;
982
- code += ` const result = ${fnCall};\n`;
983
- code += ` expect(result).to.not.be.undefined;\n`;
984
- code += ` });\n`;
985
- code += ` });\n\n`;
986
-
987
- return code;
988
- }
989
-
990
- /**
991
- * Generate Mocha tests for a class
992
- */
993
- private generateMochaClassTests(cls: ClassInfo): string {
994
- const constructorArgs =
995
- cls.constructorParams?.map((p) => this.generateTestValue(p)).join(', ') || '';
996
-
997
- let code = ` describe('${cls.name}', function() {\n`;
998
- code += ` let instance;\n\n`;
999
- code += ` beforeEach(function() {\n`;
1000
- code += ` instance = new ${cls.name}(${constructorArgs});\n`;
1001
- code += ` });\n\n`;
1002
- code += ` it('should instantiate correctly', function() {\n`;
1003
- code += ` expect(instance).to.be.instanceOf(${cls.name});\n`;
1004
- code += ` });\n`;
1005
-
1006
- for (const method of cls.methods) {
1007
- if (!method.name.startsWith('_')) {
1008
- const methodParams = method.parameters.map((p) => this.generateTestValue(p)).join(', ');
1009
- code += `\n it('${method.name} should work', ${method.isAsync ? 'async ' : ''}function() {\n`;
1010
- code += ` const result = ${method.isAsync ? 'await ' : ''}instance.${method.name}(${methodParams});\n`;
1011
- code += ` expect(result).to.not.be.undefined;\n`;
1012
- code += ` });\n`;
1013
- }
1014
- }
1015
-
1016
- code += ` });\n\n`;
1017
- return code;
1018
- }
1019
-
1020
- /**
1021
- * Generate real Pytest test code
1022
- */
1023
- private generateRealPytestTest(
1024
- moduleName: string,
1025
- importPath: string,
1026
- testType: string,
1027
- analysis: { functions: FunctionInfo[]; classes: ClassInfo[] },
1028
- patterns: Pattern[]
1029
- ): string {
1030
- const patternComment =
1031
- patterns.length > 0
1032
- ? `# Applied patterns: ${patterns.map((p) => p.name).join(', ')}\n`
1033
- : '';
1034
-
1035
- const exports: string[] = [];
1036
- for (const fn of analysis.functions) {
1037
- if (fn.isExported) exports.push(fn.name);
1038
- }
1039
- for (const cls of analysis.classes) {
1040
- if (cls.isExported) exports.push(cls.name);
1041
- }
1042
-
1043
- const pythonImport = importPath.replace(/\//g, '.').replace(/\.(ts|js)$/, '');
1044
- const importStatement =
1045
- exports.length > 0
1046
- ? `from ${pythonImport} import ${exports.join(', ')}`
1047
- : `import ${pythonImport} as ${moduleName}`;
1048
-
1049
- let code = `${patternComment}import pytest
1050
- ${importStatement}
1051
-
1052
-
1053
- class Test${moduleName.charAt(0).toUpperCase() + moduleName.slice(1)}:
1054
- """${testType} tests for ${moduleName}"""
1055
-
1056
- `;
1057
-
1058
- for (const fn of analysis.functions) {
1059
- code += this.generatePytestFunctionTests(fn);
1060
- }
1061
-
1062
- for (const cls of analysis.classes) {
1063
- code += this.generatePytestClassTests(cls);
1064
- }
1065
-
1066
- return code;
1067
- }
1068
-
1069
- /**
1070
- * Generate Pytest tests for a function
1071
- */
1072
- private generatePytestFunctionTests(fn: FunctionInfo): string {
1073
- const validParams = fn.parameters.map((p) => this.generatePythonTestValue(p)).join(', ');
1074
-
1075
- let code = ` def test_${fn.name}_valid_input(self):\n`;
1076
- code += ` """Test ${fn.name} with valid input"""\n`;
1077
- code += ` result = ${fn.name}(${validParams})\n`;
1078
- code += ` assert result is not None\n\n`;
1079
-
1080
- return code;
1081
- }
1082
-
1083
- /**
1084
- * Generate Pytest tests for a class
1085
- */
1086
- private generatePytestClassTests(cls: ClassInfo): string {
1087
- const constructorArgs =
1088
- cls.constructorParams?.map((p) => this.generatePythonTestValue(p)).join(', ') || '';
1089
-
1090
- let code = `\nclass Test${cls.name}:\n`;
1091
- code += ` """Tests for ${cls.name}"""\n\n`;
1092
- code += ` @pytest.fixture\n`;
1093
- code += ` def instance(self):\n`;
1094
- code += ` return ${cls.name}(${constructorArgs})\n\n`;
1095
- code += ` def test_instantiation(self, instance):\n`;
1096
- code += ` assert isinstance(instance, ${cls.name})\n\n`;
1097
-
1098
- for (const method of cls.methods) {
1099
- if (!method.name.startsWith('_')) {
1100
- const methodParams = method.parameters.map((p) => this.generatePythonTestValue(p)).join(', ');
1101
- code += ` def test_${method.name}(self, instance):\n`;
1102
- code += ` result = instance.${method.name}(${methodParams})\n`;
1103
- code += ` assert result is not None\n\n`;
1104
- }
1105
- }
1106
-
1107
- return code;
1108
- }
1109
-
1110
- /**
1111
- * Generate a Python test value for a parameter
1112
- */
1113
- private generatePythonTestValue(param: ParameterInfo): string {
1114
- const type = param.type?.toLowerCase() || 'unknown';
1115
- const name = param.name.toLowerCase();
1116
-
1117
- if (name.includes('id')) return `"${faker.string.uuid()}"`;
1118
- if (name.includes('name')) return `"${faker.person.fullName()}"`;
1119
- if (name.includes('email')) return `"${faker.internet.email()}"`;
1120
-
1121
- if (type.includes('str')) return `"${faker.lorem.word()}"`;
1122
- if (type.includes('int') || type.includes('number')) {
1123
- return String(faker.number.int({ min: 1, max: 100 }));
1124
- }
1125
- if (type.includes('bool')) return 'True';
1126
- if (type.includes('list') || type.includes('[]')) return '[]';
1127
- if (type.includes('dict') || type.includes('{}')) return '{}';
1128
-
1129
- return 'None';
1130
- }
1131
-
1132
- private async findApplicablePatterns(
1133
- sourceFile: string,
1134
- requestedPatterns: string[]
1135
- ): Promise<Pattern[]> {
1136
- const patterns: Pattern[] = [];
1137
-
1138
- // Check memory for stored patterns
1139
- for (const patternName of requestedPatterns) {
1140
- const stored = await this.memory.get<Pattern>(`pattern:${patternName}`);
1141
- if (stored) {
1142
- patterns.push(stored);
1143
- }
1144
- }
1145
-
1146
- // Also search for patterns by file type
1147
- const extension = sourceFile.split('.').pop() || '';
1148
- const searchResults = await this.memory.search(`pattern:*:${extension}`, 5);
1149
- for (const key of searchResults) {
1150
- const pattern = await this.memory.get<Pattern>(key);
1151
- if (pattern && !patterns.some((p) => p.id === pattern.id)) {
1152
- patterns.push(pattern);
1153
- }
1154
- }
1155
-
1156
- return patterns;
1157
- }
1158
-
1159
- private generateStubTestCode(
1160
- sourceFile: string,
1161
- testType: 'unit' | 'integration' | 'e2e',
1162
- framework: string,
1163
- patterns: Pattern[]
1164
- ): string {
1165
- const moduleName = this.extractModuleName(sourceFile);
1166
- const importPath = this.getImportPath(sourceFile);
1167
-
1168
- // Generate framework-specific test template
1169
- switch (framework) {
1170
- case 'jest':
1171
- case 'vitest':
1172
- return this.generateJestVitestTest(moduleName, importPath, testType, patterns);
1173
- case 'mocha':
1174
- return this.generateMochaTest(moduleName, importPath, testType, patterns);
1175
- case 'pytest':
1176
- return this.generatePytestTest(moduleName, importPath, testType, patterns);
1177
- default:
1178
- return this.generateJestVitestTest(moduleName, importPath, testType, patterns);
1179
- }
1180
- }
1181
-
1182
- private generateJestVitestTest(
1183
- moduleName: string,
1184
- importPath: string,
1185
- testType: string,
1186
- patterns: Pattern[]
1187
- ): string {
1188
- const patternComment =
1189
- patterns.length > 0
1190
- ? `// Applied patterns: ${patterns.map((p) => p.name).join(', ')}\n`
1191
- : '';
1192
-
1193
- // Generate pattern-aware test implementations
1194
- const basicOpsTest = this.generateBasicOpsTest(moduleName, patterns);
1195
- const edgeCaseTest = this.generateEdgeCaseTest(moduleName, patterns);
1196
- const errorHandlingTest = this.generateErrorHandlingTest(moduleName, patterns);
1197
-
1198
- return `${patternComment}import { ${moduleName} } from '${importPath}';
1199
-
1200
- describe('${moduleName}', () => {
1201
- describe('${testType} tests', () => {
1202
- it('should be defined', () => {
1203
- expect(${moduleName}).toBeDefined();
1204
- });
1205
-
1206
- ${basicOpsTest}
1207
- ${edgeCaseTest}
1208
- ${errorHandlingTest}
1209
- });
1210
- });
1211
- `;
1212
- }
1213
-
1214
- /**
1215
- * Generate basic operations test based on patterns
1216
- */
1217
- private generateBasicOpsTest(moduleName: string, patterns: Pattern[]): string {
1218
- // Check for service pattern
1219
- const isService = patterns.some((p) =>
1220
- p.name.toLowerCase().includes('service') || p.name.toLowerCase().includes('repository')
1221
- );
1222
-
1223
- // Check for factory pattern
1224
- const isFactory = patterns.some((p) => p.name.toLowerCase().includes('factory'));
1225
-
1226
- // Check for async patterns
1227
- const hasAsyncPattern = patterns.some((p) =>
1228
- p.name.toLowerCase().includes('async') || p.name.toLowerCase().includes('promise')
1229
- );
1230
-
1231
- if (isService) {
1232
- return ` it('should handle basic operations', async () => {
1233
- // Service pattern: test core functionality
1234
- const instance = new ${moduleName}();
1235
- expect(instance).toBeInstanceOf(${moduleName});
1236
-
1237
- // Verify service is properly initialized
1238
- const methods = Object.getOwnPropertyNames(Object.getPrototypeOf(instance))
1239
- .filter(m => m !== 'constructor');
1240
- expect(methods.length).toBeGreaterThan(0);
1241
- });`;
1242
- }
1243
-
1244
- if (isFactory) {
1245
- return ` it('should handle basic operations', () => {
1246
- // Factory pattern: test object creation
1247
- const result = ${moduleName}.create ? ${moduleName}.create() : new ${moduleName}();
1248
- expect(result).toBeDefined();
1249
- expect(typeof result).not.toBe('undefined');
1250
- });`;
1251
- }
1252
-
1253
- if (hasAsyncPattern) {
1254
- return ` it('should handle basic operations', async () => {
1255
- // Async pattern: test promise resolution
1256
- const instance = typeof ${moduleName} === 'function'
1257
- ? new ${moduleName}()
1258
- : ${moduleName};
1259
-
1260
- // Verify async methods resolve properly
1261
- if (typeof instance.execute === 'function') {
1262
- await expect(instance.execute()).resolves.toBeDefined();
1263
- }
1264
- });`;
1265
- }
1266
-
1267
- // Default implementation
1268
- return ` it('should handle basic operations', () => {
1269
- // Verify module exports expected interface
1270
- const moduleType = typeof ${moduleName};
1271
- expect(['function', 'object']).toContain(moduleType);
1272
-
1273
- if (moduleType === 'function') {
1274
- // Class or function: verify instantiation
1275
- const instance = new ${moduleName}();
1276
- expect(instance).toBeDefined();
1277
- } else {
1278
- // Object module: verify properties exist
1279
- expect(Object.keys(${moduleName}).length).toBeGreaterThan(0);
1280
- }
1281
- });`;
1282
- }
1283
-
1284
- /**
1285
- * Generate edge case test based on patterns
1286
- */
1287
- private generateEdgeCaseTest(moduleName: string, patterns: Pattern[]): string {
1288
- const hasValidation = patterns.some((p) =>
1289
- p.name.toLowerCase().includes('validation') || p.name.toLowerCase().includes('validator')
1290
- );
1291
-
1292
- const hasCollection = patterns.some((p) =>
1293
- p.name.toLowerCase().includes('collection') || p.name.toLowerCase().includes('list')
1294
- );
1295
-
1296
- if (hasValidation) {
1297
- return ` it('should handle edge cases', () => {
1298
- // Validation pattern: test boundary conditions
1299
- const instance = new ${moduleName}();
1300
-
1301
- // Test with empty values
1302
- if (typeof instance.validate === 'function') {
1303
- expect(() => instance.validate('')).toBeDefined();
1304
- expect(() => instance.validate(null)).toBeDefined();
1305
- }
1306
- });`;
1307
- }
1308
-
1309
- if (hasCollection) {
1310
- return ` it('should handle edge cases', () => {
1311
- // Collection pattern: test empty and large datasets
1312
- const instance = new ${moduleName}();
1313
-
1314
- // Empty collection should be handled gracefully
1315
- if (typeof instance.add === 'function') {
1316
- expect(() => instance.add(undefined)).toBeDefined();
1317
- }
1318
- if (typeof instance.get === 'function') {
1319
- expect(instance.get('nonexistent')).toBeUndefined();
1320
- }
1321
- });`;
1322
- }
1323
-
1324
- // Default edge case test
1325
- return ` it('should handle edge cases', () => {
1326
- // Test null/undefined handling
1327
- const instance = typeof ${moduleName} === 'function'
1328
- ? new ${moduleName}()
1329
- : ${moduleName};
1330
-
1331
- // Module should handle edge case inputs gracefully
1332
- expect(instance).toBeDefined();
1333
- expect(() => JSON.stringify(instance)).not.toThrow();
1334
- });`;
1335
- }
1336
-
1337
- /**
1338
- * Generate error handling test based on patterns
1339
- */
1340
- private generateErrorHandlingTest(moduleName: string, patterns: Pattern[]): string {
1341
- const hasErrorPattern = patterns.some((p) =>
1342
- p.name.toLowerCase().includes('error') || p.name.toLowerCase().includes('exception')
1343
- );
1344
-
1345
- const hasAsyncPattern = patterns.some((p) =>
1346
- p.name.toLowerCase().includes('async') || p.name.toLowerCase().includes('promise')
1347
- );
1348
-
1349
- if (hasAsyncPattern) {
1350
- return ` it('should handle error conditions', async () => {
1351
- // Async error handling: verify rejections are caught
1352
- const instance = typeof ${moduleName} === 'function'
1353
- ? new ${moduleName}()
1354
- : ${moduleName};
1355
-
1356
- // Async operations should reject gracefully on invalid input
1357
- const asyncMethods = Object.getOwnPropertyNames(Object.getPrototypeOf(instance) || {})
1358
- .filter(m => m !== 'constructor');
1359
-
1360
- // At minimum, module should be stable
1361
- expect(instance).toBeDefined();
1362
- });`;
1363
- }
1364
-
1365
- if (hasErrorPattern) {
1366
- return ` it('should handle error conditions', () => {
1367
- // Error pattern: verify custom error types
1368
- try {
1369
- const instance = new ${moduleName}();
1370
- // Trigger error condition if possible
1371
- if (typeof instance.throwError === 'function') {
1372
- expect(() => instance.throwError()).toThrow();
1373
- }
1374
- } catch (error) {
1375
- expect(error).toBeInstanceOf(Error);
1376
- }
1377
- });`;
1378
- }
1379
-
1380
- // Default error handling test
1381
- return ` it('should handle error conditions', () => {
1382
- // Verify error resilience
1383
- expect(() => {
1384
- const instance = typeof ${moduleName} === 'function'
1385
- ? new ${moduleName}()
1386
- : ${moduleName};
1387
- return instance;
1388
- }).not.toThrow();
1389
-
1390
- // Module should not throw on inspection
1391
- expect(() => Object.keys(${moduleName})).not.toThrow();
1392
- });`;
1393
- }
1394
-
1395
- private generateMochaTest(
1396
- moduleName: string,
1397
- importPath: string,
1398
- testType: string,
1399
- patterns: Pattern[]
1400
- ): string {
1401
- const patternComment =
1402
- patterns.length > 0
1403
- ? `// Applied patterns: ${patterns.map((p) => p.name).join(', ')}\n`
1404
- : '';
1405
-
1406
- // Determine if async tests needed based on patterns
1407
- const isAsync = patterns.some((p) =>
1408
- p.name.toLowerCase().includes('async') || p.name.toLowerCase().includes('promise')
1409
- );
1410
- const asyncSetup = isAsync ? 'async ' : '';
1411
-
1412
- return `${patternComment}import { expect } from 'chai';
1413
- import { ${moduleName} } from '${importPath}';
1414
-
1415
- describe('${moduleName}', function() {
1416
- describe('${testType} tests', function() {
1417
- it('should be defined', function() {
1418
- expect(${moduleName}).to.not.be.undefined;
1419
- });
1420
-
1421
- it('should handle basic operations', ${asyncSetup}function() {
1422
- // Verify module exports expected interface
1423
- const moduleType = typeof ${moduleName};
1424
- expect(['function', 'object']).to.include(moduleType);
1425
-
1426
- if (moduleType === 'function') {
1427
- const instance = new ${moduleName}();
1428
- expect(instance).to.exist;
1429
- } else {
1430
- expect(Object.keys(${moduleName})).to.have.length.greaterThan(0);
1431
- }
1432
- });
1433
-
1434
- it('should handle edge cases', function() {
1435
- // Verify resilience to edge inputs
1436
- const instance = typeof ${moduleName} === 'function'
1437
- ? new ${moduleName}()
1438
- : ${moduleName};
1439
- expect(instance).to.exist;
1440
- expect(() => JSON.stringify(instance)).to.not.throw();
1441
- });
1442
-
1443
- it('should handle error conditions', function() {
1444
- // Verify error resilience
1445
- expect(() => {
1446
- const instance = typeof ${moduleName} === 'function'
1447
- ? new ${moduleName}()
1448
- : ${moduleName};
1449
- return instance;
1450
- }).to.not.throw();
1451
- });
1452
- });
1453
- });
1454
- `;
1455
- }
1456
-
1457
- private generatePytestTest(
1458
- moduleName: string,
1459
- importPath: string,
1460
- testType: string,
1461
- patterns: Pattern[]
1462
- ): string {
1463
- const patternComment =
1464
- patterns.length > 0
1465
- ? `# Applied patterns: ${patterns.map((p) => p.name).join(', ')}\n`
1466
- : '';
1467
-
1468
- // Determine if async tests needed based on patterns
1469
- const isAsync = patterns.some((p) =>
1470
- p.name.toLowerCase().includes('async') || p.name.toLowerCase().includes('promise')
1471
- );
1472
- const asyncDecorator = isAsync ? '@pytest.mark.asyncio\n ' : '';
1473
- const asyncDef = isAsync ? 'async def' : 'def';
1474
-
1475
- return `${patternComment}import pytest
1476
- from ${importPath} import ${moduleName}
1477
-
1478
-
1479
- class Test${moduleName}:
1480
- """${testType} tests for ${moduleName}"""
1481
-
1482
- def test_is_defined(self):
1483
- """Verify the module is properly exported and defined."""
1484
- assert ${moduleName} is not None
1485
-
1486
- ${asyncDecorator}${asyncDef} test_basic_operations(self):
1487
- """Test core functionality with valid inputs."""
1488
- # Verify module can be instantiated or accessed
1489
- if callable(${moduleName}):
1490
- instance = ${moduleName}()
1491
- assert instance is not None
1492
- else:
1493
- assert len(dir(${moduleName})) > 0
1494
-
1495
- def test_edge_cases(self):
1496
- """Test handling of edge case inputs."""
1497
- # Verify module handles edge cases gracefully
1498
- instance = ${moduleName}() if callable(${moduleName}) else ${moduleName}
1499
- assert instance is not None
1500
- # Module should be serializable
1501
- import json
1502
- try:
1503
- json.dumps(str(instance))
1504
- except (TypeError, ValueError):
1505
- pass # Complex objects may not serialize, but shouldn't crash
1506
-
1507
- def test_error_conditions(self):
1508
- """Test error handling and recovery."""
1509
- # Module instantiation should not raise unexpected errors
1510
- try:
1511
- instance = ${moduleName}() if callable(${moduleName}) else ${moduleName}
1512
- assert instance is not None
1513
- except TypeError:
1514
- # Expected if constructor requires arguments
1515
- pass
1516
- `;
1517
- }
1518
-
1519
- private async generateRedPhaseTest(
1520
- feature: string,
1521
- behavior: string,
1522
- _framework: string
1523
- ): Promise<TDDResult> {
1524
- // Generate TDD RED phase: failing test that defines expected behavior
1525
- const funcName = this.camelCase(feature);
1526
- const assertions = this.generateAssertionsFromBehavior(behavior, funcName);
1527
-
1528
- const testCode = `describe('${feature}', () => {
1529
- it('${behavior}', () => {
1530
- // Red phase: This test should fail initially
1531
- ${assertions}
1532
- });
1533
- });`;
1534
-
1535
- return {
1536
- phase: 'red',
1537
- testCode,
1538
- nextStep: 'Write the minimal implementation to make this test pass',
1539
- };
1540
- }
1541
-
1542
- /**
1543
- * Generate specific assertions from behavior description
1544
- * Uses NLP-style extraction to infer test values and assertions
1545
- */
1546
- private generateAssertionsFromBehavior(behavior: string, funcName: string): string {
1547
- const behaviorLower = behavior.toLowerCase();
1548
- const assertions: string[] = [];
1549
-
1550
- // Extract context from behavior description
1551
- const context = this.extractBehaviorContext(behavior);
1552
-
1553
- // Build function call with appropriate arguments
1554
- const funcCall = this.buildFunctionCall(funcName, context, behaviorLower);
1555
-
1556
- // Add setup if needed
1557
- if (context.setupCode) {
1558
- assertions.push(context.setupCode);
1559
- }
1560
-
1561
- // Generate assertions based on expected outcome
1562
- if (behaviorLower.includes('return') && behaviorLower.includes('true')) {
1563
- assertions.push(` const result = ${funcCall};`);
1564
- assertions.push(` expect(result).toBe(true);`);
1565
- } else if (behaviorLower.includes('return') && behaviorLower.includes('false')) {
1566
- assertions.push(` const result = ${funcCall};`);
1567
- assertions.push(` expect(result).toBe(false);`);
1568
- } else if (behaviorLower.includes('throw') || behaviorLower.includes('error')) {
1569
- const errorMsg = context.extractedString || 'Error';
1570
- assertions.push(` expect(() => ${funcCall}).toThrow(${context.extractedString ? `'${errorMsg}'` : ''});`);
1571
- } else if (behaviorLower.includes('empty') || behaviorLower.includes('nothing')) {
1572
- assertions.push(` const result = ${funcCall};`);
1573
- if (behaviorLower.includes('string')) {
1574
- assertions.push(` expect(result).toBe('');`);
1575
- } else if (behaviorLower.includes('object')) {
1576
- assertions.push(` expect(result).toEqual({});`);
1577
- } else {
1578
- assertions.push(` expect(result).toEqual([]);`);
1579
- }
1580
- } else if (behaviorLower.includes('null')) {
1581
- assertions.push(` const result = ${funcCall};`);
1582
- assertions.push(` expect(result).toBeNull();`);
1583
- } else if (behaviorLower.includes('undefined')) {
1584
- assertions.push(` const result = ${funcCall};`);
1585
- assertions.push(` expect(result).toBeUndefined();`);
1586
- } else if (behaviorLower.includes('contain') || behaviorLower.includes('include')) {
1587
- assertions.push(` const result = ${funcCall};`);
1588
- const expectedValue = context.extractedString || context.extractedNumber?.toString() || 'expectedItem';
1589
- if (context.extractedString) {
1590
- assertions.push(` expect(result).toContain('${expectedValue}');`);
1591
- } else if (context.extractedNumber !== undefined) {
1592
- assertions.push(` expect(result).toContain(${expectedValue});`);
1593
- } else {
1594
- assertions.push(` expect(result).toContain(testInput); // Contains the input`);
1595
- }
1596
- } else if (behaviorLower.includes('length') || behaviorLower.includes('count')) {
1597
- assertions.push(` const result = ${funcCall};`);
1598
- const expectedLength = context.extractedNumber ?? 3;
1599
- assertions.push(` expect(result).toHaveLength(${expectedLength});`);
1600
- } else if (behaviorLower.includes('equal') || behaviorLower.includes('match')) {
1601
- assertions.push(` const result = ${funcCall};`);
1602
- if (context.extractedString) {
1603
- assertions.push(` expect(result).toEqual('${context.extractedString}');`);
1604
- } else if (context.extractedNumber !== undefined) {
1605
- assertions.push(` expect(result).toEqual(${context.extractedNumber});`);
1606
- } else {
1607
- assertions.push(` expect(result).toEqual(expectedOutput);`);
1608
- }
1609
- } else if (behaviorLower.includes('greater') || behaviorLower.includes('more than')) {
1610
- assertions.push(` const result = ${funcCall};`);
1611
- const threshold = context.extractedNumber ?? 0;
1612
- assertions.push(` expect(result).toBeGreaterThan(${threshold});`);
1613
- } else if (behaviorLower.includes('less') || behaviorLower.includes('fewer')) {
1614
- assertions.push(` const result = ${funcCall};`);
1615
- const threshold = context.extractedNumber ?? 100;
1616
- assertions.push(` expect(result).toBeLessThan(${threshold});`);
1617
- } else if (behaviorLower.includes('valid') || behaviorLower.includes('success')) {
1618
- assertions.push(` const result = ${funcCall};`);
1619
- assertions.push(` expect(result).toBeDefined();`);
1620
- if (behaviorLower.includes('object') || behaviorLower.includes('response')) {
1621
- assertions.push(` expect(result.success ?? result.valid ?? result.ok).toBeTruthy();`);
1622
- } else {
1623
- assertions.push(` expect(result).toBeTruthy();`);
1624
- }
1625
- } else if (behaviorLower.includes('array') || behaviorLower.includes('list')) {
1626
- assertions.push(` const result = ${funcCall};`);
1627
- assertions.push(` expect(Array.isArray(result)).toBe(true);`);
1628
- if (context.extractedNumber !== undefined) {
1629
- assertions.push(` expect(result.length).toBeGreaterThanOrEqual(${context.extractedNumber});`);
1630
- }
1631
- } else if (behaviorLower.includes('object')) {
1632
- assertions.push(` const result = ${funcCall};`);
1633
- assertions.push(` expect(typeof result).toBe('object');`);
1634
- assertions.push(` expect(result).not.toBeNull();`);
1635
- } else if (behaviorLower.includes('string')) {
1636
- assertions.push(` const result = ${funcCall};`);
1637
- assertions.push(` expect(typeof result).toBe('string');`);
1638
- if (context.extractedString) {
1639
- assertions.push(` expect(result).toContain('${context.extractedString}');`);
1640
- }
1641
- } else if (behaviorLower.includes('number')) {
1642
- assertions.push(` const result = ${funcCall};`);
1643
- assertions.push(` expect(typeof result).toBe('number');`);
1644
- assertions.push(` expect(Number.isNaN(result)).toBe(false);`);
1645
- } else {
1646
- // Default: check it's defined
1647
- assertions.push(` const result = ${funcCall};`);
1648
- assertions.push(` expect(result).toBeDefined();`);
1649
- }
1650
-
1651
- return assertions.join('\n');
1652
- }
1653
-
1654
- /**
1655
- * Extract contextual information from behavior description
1656
- */
1657
- private extractBehaviorContext(behavior: string): {
1658
- extractedString?: string;
1659
- extractedNumber?: number;
1660
- inputType?: string;
1661
- setupCode?: string;
1662
- } {
1663
- const context: {
1664
- extractedString?: string;
1665
- extractedNumber?: number;
1666
- inputType?: string;
1667
- setupCode?: string;
1668
- } = {};
1669
-
1670
- // Extract quoted strings from behavior
1671
- const stringMatch = behavior.match(/["']([^"']+)["']/);
1672
- if (stringMatch) {
1673
- context.extractedString = stringMatch[1];
1674
- }
1675
-
1676
- // Extract numbers from behavior
1677
- const numberMatch = behavior.match(/\b(\d+)\b/);
1678
- if (numberMatch) {
1679
- context.extractedNumber = parseInt(numberMatch[1], 10);
1680
- }
1681
-
1682
- // Detect input type
1683
- if (/\b(email|e-mail)\b/i.test(behavior)) {
1684
- context.inputType = 'email';
1685
- context.setupCode = ` const testInput = 'test@example.com';`;
1686
- } else if (/\b(url|link|href)\b/i.test(behavior)) {
1687
- context.inputType = 'url';
1688
- context.setupCode = ` const testInput = 'https://example.com';`;
1689
- } else if (/\b(date|time|timestamp)\b/i.test(behavior)) {
1690
- context.inputType = 'date';
1691
- context.setupCode = ` const testInput = new Date('2024-01-15');`;
1692
- } else if (/\b(id|uuid|identifier)\b/i.test(behavior)) {
1693
- context.inputType = 'id';
1694
- context.setupCode = ` const testInput = 'abc-123-def';`;
1695
- } else if (/\b(user|person|customer)\b/i.test(behavior)) {
1696
- context.inputType = 'user';
1697
- context.setupCode = ` const testInput = { id: '1', name: 'Test User', email: 'test@example.com' };`;
1698
- } else if (/\b(config|options|settings)\b/i.test(behavior)) {
1699
- context.inputType = 'config';
1700
- context.setupCode = ` const testInput = { enabled: true, timeout: 5000 };`;
1701
- }
1702
-
1703
- return context;
1704
- }
1705
-
1706
- /**
1707
- * Build function call with appropriate arguments based on context
1708
- */
1709
- private buildFunctionCall(
1710
- funcName: string,
1711
- context: { inputType?: string; extractedString?: string; extractedNumber?: number },
1712
- behaviorLower: string
1713
- ): string {
1714
- // If context has setup code, use testInput variable
1715
- if (context.inputType) {
1716
- return `${funcName}(testInput)`;
1717
- }
1718
-
1719
- // No input needed
1720
- if (!behaviorLower.includes('with') && !behaviorLower.includes('given') && !behaviorLower.includes('for')) {
1721
- return `${funcName}()`;
1722
- }
1723
-
1724
- // Infer input type from behavior
1725
- if (behaviorLower.includes('string') || behaviorLower.includes('text') || behaviorLower.includes('name')) {
1726
- const value = context.extractedString || 'test input';
1727
- return `${funcName}('${value}')`;
1728
- }
1729
- if (behaviorLower.includes('number') || behaviorLower.includes('count') || behaviorLower.includes('amount')) {
1730
- const value = context.extractedNumber ?? 42;
1731
- return `${funcName}(${value})`;
1732
- }
1733
- if (behaviorLower.includes('array') || behaviorLower.includes('list') || behaviorLower.includes('items')) {
1734
- return `${funcName}([1, 2, 3])`;
1735
- }
1736
- if (behaviorLower.includes('object') || behaviorLower.includes('data') || behaviorLower.includes('payload')) {
1737
- return `${funcName}({ key: 'value' })`;
1738
- }
1739
- if (behaviorLower.includes('boolean') || behaviorLower.includes('flag')) {
1740
- return `${funcName}(true)`;
1741
- }
1742
-
1743
- // Default: use extracted value or generic input
1744
- if (context.extractedString) {
1745
- return `${funcName}('${context.extractedString}')`;
1746
- }
1747
- if (context.extractedNumber !== undefined) {
1748
- return `${funcName}(${context.extractedNumber})`;
1749
- }
1750
-
1751
- return `${funcName}(input)`;
1752
- }
1753
-
1754
- private async generateGreenPhaseCode(
1755
- feature: string,
1756
- behavior: string,
1757
- _framework: string
1758
- ): Promise<TDDResult> {
1759
- // Generate TDD GREEN phase: minimal implementation to pass the test
1760
- // Analyze the behavior description to generate appropriate implementation
1761
- const behaviorLower = behavior.toLowerCase();
1762
- const funcName = this.camelCase(feature);
1763
-
1764
- // Infer return type and implementation from behavior
1765
- const { returnType, implementation, params } = this.inferImplementationFromBehavior(behaviorLower);
1766
-
1767
- const implementationCode = `/**
1768
- * ${feature}
1769
- * Behavior: ${behavior}
1770
- */
1771
- export function ${funcName}(${params}): ${returnType} {
1772
- ${implementation}
1773
- }`;
1774
-
1775
- return {
1776
- phase: 'green',
1777
- implementationCode,
1778
- nextStep: 'Refactor the code while keeping tests green',
1779
- };
1780
- }
1781
-
1782
- /**
1783
- * Infer implementation details from behavior description using heuristics
1784
- * Analyzes the behavior text to determine return type, parameters, and minimal implementation
1785
- */
1786
- private inferImplementationFromBehavior(behavior: string): {
1787
- returnType: string;
1788
- implementation: string;
1789
- params: string;
1790
- } {
1791
- // Default values
1792
- let returnType = 'unknown';
1793
- let implementation = ' return undefined;';
1794
- let params = '';
1795
-
1796
- // Detect return type from behavior description
1797
- if (behavior.includes('return') || behavior.includes('returns')) {
1798
- if (behavior.includes('boolean') || behavior.includes('true') || behavior.includes('false') ||
1799
- behavior.includes('valid') || behavior.includes('is ') || behavior.includes('has ') ||
1800
- behavior.includes('can ') || behavior.includes('should ')) {
1801
- returnType = 'boolean';
1802
- implementation = ' // Validate and return boolean result\n return true;';
1803
- } else if (behavior.includes('number') || behavior.includes('count') || behavior.includes('sum') ||
1804
- behavior.includes('total') || behavior.includes('calculate') || behavior.includes('average')) {
1805
- returnType = 'number';
1806
- implementation = ' // Perform calculation and return result\n return 0;';
1807
- } else if (behavior.includes('string') || behavior.includes('text') || behavior.includes('message') ||
1808
- behavior.includes('name') || behavior.includes('format')) {
1809
- returnType = 'string';
1810
- implementation = " // Process and return string result\n return '';";
1811
- } else if (behavior.includes('array') || behavior.includes('list') || behavior.includes('items') ||
1812
- behavior.includes('collection') || behavior.includes('filter') || behavior.includes('map')) {
1813
- returnType = 'unknown[]';
1814
- implementation = ' // Process and return array\n return [];';
1815
- } else if (behavior.includes('object') || behavior.includes('data') || behavior.includes('result') ||
1816
- behavior.includes('response')) {
1817
- returnType = 'Record<string, unknown>';
1818
- implementation = ' // Build and return object\n return {};';
1819
- }
1820
- }
1821
-
1822
- // Detect if async is needed
1823
- if (behavior.includes('async') || behavior.includes('await') || behavior.includes('promise') ||
1824
- behavior.includes('fetch') || behavior.includes('load') || behavior.includes('save') ||
1825
- behavior.includes('api') || behavior.includes('request')) {
1826
- returnType = `Promise<${returnType}>`;
1827
- implementation = implementation.replace('return ', 'return await Promise.resolve(').replace(';', ');');
1828
- }
1829
-
1830
- // Detect parameters from behavior
1831
- const paramPatterns: Array<{ pattern: RegExp; param: string }> = [
1832
- { pattern: /(?:with|given|for|using)\s+(?:a\s+)?(?:string|text|name)/i, param: 'input: string' },
1833
- { pattern: /(?:with|given|for|using)\s+(?:a\s+)?(?:number|count|amount)/i, param: 'value: number' },
1834
- { pattern: /(?:with|given|for|using)\s+(?:an?\s+)?(?:array|list|items)/i, param: 'items: unknown[]' },
1835
- { pattern: /(?:with|given|for|using)\s+(?:an?\s+)?(?:object|data)/i, param: 'data: Record<string, unknown>' },
1836
- { pattern: /(?:with|given|for|using)\s+(?:an?\s+)?id/i, param: 'id: string' },
1837
- { pattern: /(?:with|given|for|using)\s+(?:valid|invalid)\s+input/i, param: 'input: unknown' },
1838
- { pattern: /(?:when|if)\s+(?:called\s+)?(?:with|without)/i, param: 'input?: unknown' },
1839
- ];
1840
-
1841
- const detectedParams: string[] = [];
1842
- for (const { pattern, param } of paramPatterns) {
1843
- if (pattern.test(behavior) && !detectedParams.includes(param)) {
1844
- detectedParams.push(param);
1845
- }
1846
- }
1847
- params = detectedParams.join(', ');
1848
-
1849
- // Detect validation logic from behavior
1850
- if (behavior.includes('validate') || behavior.includes('check') || behavior.includes('verify')) {
1851
- if (params.includes('input')) {
1852
- implementation = ` // Validate the input
1853
- if (input === undefined || input === null) {
1854
- throw new Error('Invalid input');
1855
- }
1856
- ${implementation}`;
1857
- }
1858
- }
1859
-
1860
- // Detect error throwing from behavior
1861
- if (behavior.includes('throw') || behavior.includes('error') || behavior.includes('exception') ||
1862
- behavior.includes('invalid') || behavior.includes('fail')) {
1863
- if (behavior.includes('when') || behavior.includes('if')) {
1864
- implementation = ` // Check for error conditions
1865
- if (!input) {
1866
- throw new Error('Validation failed');
1867
- }
1868
- ${implementation}`;
1869
- }
1870
- }
1871
-
1872
- return { returnType, implementation, params };
1873
- }
1874
-
1875
- private async generateRefactoringSuggestions(
1876
- _feature: string,
1877
- _behavior: string
1878
- ): Promise<TDDResult> {
1879
- return {
1880
- phase: 'refactor',
1881
- refactoringChanges: [
1882
- 'Extract common logic into helper functions',
1883
- 'Apply single responsibility principle',
1884
- 'Consider adding type safety improvements',
1885
- 'Review naming conventions',
1886
- 'Optimize performance if needed',
1887
- ],
1888
- nextStep: 'Apply refactoring changes and ensure all tests still pass',
1889
- };
1890
- }
1891
-
1892
- private generatePropertyTestCode(
1893
- funcName: string,
1894
- property: string,
1895
- constraints: Record<string, unknown>
1896
- ): string {
1897
- // Analyze property description to generate appropriate generators and assertions
1898
- const propertyLower = property.toLowerCase();
1899
- const { generators, assertion, setupCode } = this.analyzePropertyForTestGeneration(
1900
- propertyLower,
1901
- funcName,
1902
- constraints
1903
- );
1904
-
1905
- return `import * as fc from 'fast-check';
1906
-
1907
- describe('${funcName} property tests', () => {
1908
- it('${property}', () => {
1909
- ${setupCode}
1910
- fc.assert(
1911
- fc.property(${generators.join(', ')}, (${this.generatePropertyParams(generators)}) => {
1912
- const result = ${funcName}(${this.generatePropertyArgs(generators)});
1913
- ${assertion}
1914
- })
1915
- );
1916
- });
1917
- });`;
1918
- }
1919
-
1920
- /**
1921
- * Analyze property description to determine generators and assertions
1922
- */
1923
- private analyzePropertyForTestGeneration(
1924
- propertyLower: string,
1925
- funcName: string,
1926
- constraints: Record<string, unknown>
1927
- ): { generators: string[]; assertion: string; setupCode: string } {
1928
- const generators: string[] = [];
1929
- let assertion = 'return result !== undefined;';
1930
- let setupCode = '';
1931
-
1932
- // Idempotent property: f(f(x)) === f(x)
1933
- if (propertyLower.includes('idempotent') || propertyLower.includes('same result')) {
1934
- generators.push(this.inferGeneratorFromConstraints(constraints, 'input'));
1935
- assertion = `// Idempotent: applying twice gives same result
1936
- const firstResult = ${funcName}(input);
1937
- const secondResult = ${funcName}(firstResult);
1938
- return JSON.stringify(firstResult) === JSON.stringify(secondResult);`;
1939
- }
1940
- // Commutative property: f(a, b) === f(b, a)
1941
- else if (propertyLower.includes('commutative') || propertyLower.includes('order independent')) {
1942
- const gen = this.inferGeneratorFromConstraints(constraints, 'value');
1943
- generators.push(gen, gen);
1944
- assertion = `// Commutative: order doesn't matter
1945
- const result1 = ${funcName}(a, b);
1946
- const result2 = ${funcName}(b, a);
1947
- return JSON.stringify(result1) === JSON.stringify(result2);`;
1948
- }
1949
- // Associative property: f(f(a, b), c) === f(a, f(b, c))
1950
- else if (propertyLower.includes('associative')) {
1951
- const gen = this.inferGeneratorFromConstraints(constraints, 'value');
1952
- generators.push(gen, gen, gen);
1953
- assertion = `// Associative: grouping doesn't matter
1954
- const left = ${funcName}(${funcName}(a, b), c);
1955
- const right = ${funcName}(a, ${funcName}(b, c));
1956
- return JSON.stringify(left) === JSON.stringify(right);`;
1957
- }
1958
- // Identity property: f(x, identity) === x
1959
- else if (propertyLower.includes('identity') || propertyLower.includes('neutral element')) {
1960
- generators.push(this.inferGeneratorFromConstraints(constraints, 'input'));
1961
- const identity = constraints.identity !== undefined ? String(constraints.identity) : '0';
1962
- setupCode = ` const identity = ${identity};`;
1963
- assertion = `// Identity: operation with identity returns original
1964
- const result = ${funcName}(input, identity);
1965
- return JSON.stringify(result) === JSON.stringify(input);`;
1966
- }
1967
- // Inverse property: f(f(x)) === x (e.g., encode/decode)
1968
- else if (propertyLower.includes('inverse') || propertyLower.includes('reversible') ||
1969
- propertyLower.includes('round-trip') || propertyLower.includes('encode') ||
1970
- propertyLower.includes('decode')) {
1971
- generators.push(this.inferGeneratorFromConstraints(constraints, 'input'));
1972
- const inverseFn = constraints.inverse as string || `${funcName}Inverse`;
1973
- assertion = `// Inverse: applying function and its inverse returns original
1974
- const encoded = ${funcName}(input);
1975
- const decoded = ${inverseFn}(encoded);
1976
- return JSON.stringify(decoded) === JSON.stringify(input);`;
1977
- }
1978
- // Distributive property: f(a, b + c) === f(a, b) + f(a, c)
1979
- else if (propertyLower.includes('distributive')) {
1980
- const gen = this.inferGeneratorFromConstraints(constraints, 'number');
1981
- generators.push(gen, gen, gen);
1982
- assertion = `// Distributive: f(a, b + c) === f(a, b) + f(a, c)
1983
- const left = ${funcName}(a, b + c);
1984
- const right = ${funcName}(a, b) + ${funcName}(a, c);
1985
- return Math.abs(left - right) < 0.0001;`;
1986
- }
1987
- // Monotonic property: a <= b implies f(a) <= f(b)
1988
- else if (propertyLower.includes('monotonic') || propertyLower.includes('preserves order') ||
1989
- propertyLower.includes('non-decreasing') || propertyLower.includes('sorted')) {
1990
- generators.push('fc.integer()', 'fc.integer()');
1991
- assertion = `// Monotonic: preserves order
1992
- const [small, large] = a <= b ? [a, b] : [b, a];
1993
- const resultSmall = ${funcName}(small);
1994
- const resultLarge = ${funcName}(large);
1995
- return resultSmall <= resultLarge;`;
1996
- }
1997
- // Bounds/range property: output is within expected bounds
1998
- else if (propertyLower.includes('bound') || propertyLower.includes('range') ||
1999
- propertyLower.includes('between') || propertyLower.includes('clamp')) {
2000
- generators.push(this.inferGeneratorFromConstraints(constraints, 'input'));
2001
- const min = constraints.min !== undefined ? constraints.min : 0;
2002
- const max = constraints.max !== undefined ? constraints.max : 100;
2003
- assertion = `// Bounded: result is within expected range
2004
- const result = ${funcName}(input);
2005
- return result >= ${min} && result <= ${max};`;
2006
- }
2007
- // Length preservation
2008
- else if (propertyLower.includes('length') || propertyLower.includes('size')) {
2009
- generators.push('fc.array(fc.anything())');
2010
- if (propertyLower.includes('preserve')) {
2011
- assertion = `// Length preserved: output has same length as input
2012
- const result = ${funcName}(input);
2013
- return Array.isArray(result) && result.length === input.length;`;
2014
- } else {
2015
- assertion = `// Length invariant
2016
- const result = ${funcName}(input);
2017
- return typeof result.length === 'number' || typeof result.size === 'number';`;
2018
- }
2019
- }
2020
- // Type preservation
2021
- else if (propertyLower.includes('type') && propertyLower.includes('preserve')) {
2022
- generators.push('fc.anything()');
2023
- assertion = `// Type preserved: output has same type as input
2024
- const result = ${funcName}(input);
2025
- return typeof result === typeof input;`;
2026
- }
2027
- // Non-null/defined output
2028
- else if (propertyLower.includes('never null') || propertyLower.includes('always defined') ||
2029
- propertyLower.includes('non-null')) {
2030
- generators.push(this.inferGeneratorFromConstraints(constraints, 'input'));
2031
- assertion = `// Never null: always returns defined value
2032
- const result = ${funcName}(input);
2033
- return result !== null && result !== undefined;`;
2034
- }
2035
- // Deterministic property: same input always produces same output
2036
- else if (propertyLower.includes('deterministic') || propertyLower.includes('pure') ||
2037
- propertyLower.includes('consistent')) {
2038
- generators.push(this.inferGeneratorFromConstraints(constraints, 'input'));
2039
- assertion = `// Deterministic: same input always gives same output
2040
- const result1 = ${funcName}(input);
2041
- const result2 = ${funcName}(input);
2042
- return JSON.stringify(result1) === JSON.stringify(result2);`;
2043
- }
2044
- // Default case: basic existence check with type-appropriate generator
2045
- else {
2046
- generators.push(this.inferGeneratorFromConstraints(constraints, 'input'));
2047
- assertion = `// Basic property: function returns a value
2048
- return result !== undefined;`;
2049
- }
2050
-
2051
- return { generators, assertion, setupCode };
2052
- }
2053
-
2054
- /**
2055
- * Infer the appropriate fast-check generator from constraints
2056
- */
2057
- private inferGeneratorFromConstraints(
2058
- constraints: Record<string, unknown>,
2059
- hint: string
2060
- ): string {
2061
- // Check explicit type constraint
2062
- const type = (constraints.type as string)?.toLowerCase() || hint.toLowerCase();
2063
-
2064
- if (type.includes('string') || type.includes('text')) {
2065
- const minLength = constraints.minLength as number | undefined;
2066
- const maxLength = constraints.maxLength as number | undefined;
2067
- if (minLength !== undefined || maxLength !== undefined) {
2068
- return `fc.string({ minLength: ${minLength ?? 0}, maxLength: ${maxLength ?? 100} })`;
2069
- }
2070
- return 'fc.string()';
2071
- }
2072
-
2073
- if (type.includes('number') || type.includes('int') || type.includes('value')) {
2074
- const min = constraints.min as number | undefined;
2075
- const max = constraints.max as number | undefined;
2076
- if (min !== undefined || max !== undefined) {
2077
- return `fc.integer({ min: ${min ?? Number.MIN_SAFE_INTEGER}, max: ${max ?? Number.MAX_SAFE_INTEGER} })`;
2078
- }
2079
- return 'fc.integer()';
2080
- }
2081
-
2082
- if (type.includes('float') || type.includes('decimal')) {
2083
- return 'fc.float()';
2084
- }
2085
-
2086
- if (type.includes('boolean') || type.includes('bool')) {
2087
- return 'fc.boolean()';
2088
- }
2089
-
2090
- if (type.includes('array') || type.includes('list')) {
2091
- const itemType = constraints.itemType as string || 'anything';
2092
- const itemGen = this.getSimpleGenerator(itemType);
2093
- return `fc.array(${itemGen})`;
2094
- }
2095
-
2096
- if (type.includes('object') || type.includes('record')) {
2097
- return 'fc.object()';
2098
- }
2099
-
2100
- if (type.includes('date')) {
2101
- return 'fc.date()';
2102
- }
2103
-
2104
- if (type.includes('uuid') || type.includes('id')) {
2105
- return 'fc.uuid()';
2106
- }
2107
-
2108
- if (type.includes('email')) {
2109
- return 'fc.emailAddress()';
2110
- }
2111
-
2112
- // Default to anything
2113
- return 'fc.anything()';
2114
- }
2115
-
2116
- /**
2117
- * Get a simple generator for a type name
2118
- */
2119
- private getSimpleGenerator(typeName: string): string {
2120
- const typeMap: Record<string, string> = {
2121
- string: 'fc.string()',
2122
- number: 'fc.integer()',
2123
- integer: 'fc.integer()',
2124
- float: 'fc.float()',
2125
- boolean: 'fc.boolean()',
2126
- date: 'fc.date()',
2127
- uuid: 'fc.uuid()',
2128
- anything: 'fc.anything()',
2129
- };
2130
- return typeMap[typeName.toLowerCase()] || 'fc.anything()';
2131
- }
2132
-
2133
- /**
2134
- * Generate parameter names from generator list
2135
- */
2136
- private generatePropertyParams(generators: string[]): string {
2137
- if (generators.length === 1) {
2138
- return 'input';
2139
- }
2140
- return generators.map((_, i) => String.fromCharCode(97 + i)).join(', '); // a, b, c...
2141
- }
2142
-
2143
- /**
2144
- * Generate argument list for function call
2145
- */
2146
- private generatePropertyArgs(generators: string[]): string {
2147
- if (generators.length === 1) {
2148
- return 'input';
2149
- }
2150
- return generators.map((_, i) => String.fromCharCode(97 + i)).join(', ');
2151
- }
2152
-
2153
- private inferGenerators(
2154
- property: string,
2155
- constraints: Record<string, unknown>
2156
- ): string[] {
2157
- const generators: string[] = [];
2158
- const propertyLower = property.toLowerCase();
2159
-
2160
- // Analyze property description to infer appropriate generators
2161
- // String-related properties
2162
- if (
2163
- propertyLower.includes('string') ||
2164
- propertyLower.includes('text') ||
2165
- propertyLower.includes('name') ||
2166
- propertyLower.includes('email')
2167
- ) {
2168
- if (constraints.minLength || constraints.maxLength) {
2169
- const min = constraints.minLength ?? 0;
2170
- const max = constraints.maxLength ?? 100;
2171
- generators.push(`fc.string({ minLength: ${min}, maxLength: ${max} })`);
2172
- } else {
2173
- generators.push('fc.string()');
2174
- }
2175
- if (propertyLower.includes('email')) {
2176
- generators.push('fc.emailAddress()');
2177
- }
2178
- }
2179
-
2180
- // Number-related properties
2181
- if (
2182
- propertyLower.includes('number') ||
2183
- propertyLower.includes('count') ||
2184
- propertyLower.includes('amount') ||
2185
- propertyLower.includes('integer') ||
2186
- propertyLower.includes('positive') ||
2187
- propertyLower.includes('negative')
2188
- ) {
2189
- if (propertyLower.includes('positive')) {
2190
- generators.push('fc.nat()');
2191
- } else if (propertyLower.includes('negative')) {
2192
- generators.push('fc.integer({ max: -1 })');
2193
- } else if (constraints.min !== undefined || constraints.max !== undefined) {
2194
- const min = constraints.min ?? Number.MIN_SAFE_INTEGER;
2195
- const max = constraints.max ?? Number.MAX_SAFE_INTEGER;
2196
- generators.push(`fc.integer({ min: ${min}, max: ${max} })`);
2197
- } else {
2198
- generators.push('fc.integer()');
2199
- }
2200
- if (propertyLower.includes('float') || propertyLower.includes('decimal')) {
2201
- generators.push('fc.float()');
2202
- }
2203
- }
2204
-
2205
- // Boolean properties
2206
- if (propertyLower.includes('boolean') || propertyLower.includes('flag')) {
2207
- generators.push('fc.boolean()');
2208
- }
2209
-
2210
- // Array-related properties
2211
- if (
2212
- propertyLower.includes('array') ||
2213
- propertyLower.includes('list') ||
2214
- propertyLower.includes('collection')
2215
- ) {
2216
- const itemType = constraints.itemType as string || 'anything';
2217
- const itemGen = this.getGeneratorForType(itemType);
2218
- if (constraints.minItems || constraints.maxItems) {
2219
- const min = constraints.minItems ?? 0;
2220
- const max = constraints.maxItems ?? 10;
2221
- generators.push(`fc.array(${itemGen}, { minLength: ${min}, maxLength: ${max} })`);
2222
- } else {
2223
- generators.push(`fc.array(${itemGen})`);
2224
- }
2225
- }
2226
-
2227
- // Object-related properties
2228
- if (propertyLower.includes('object') || propertyLower.includes('record')) {
2229
- generators.push('fc.object()');
2230
- generators.push('fc.dictionary(fc.string(), fc.anything())');
2231
- }
2232
-
2233
- // Date-related properties
2234
- if (propertyLower.includes('date') || propertyLower.includes('time')) {
2235
- generators.push('fc.date()');
2236
- }
2237
-
2238
- // UUID properties
2239
- if (propertyLower.includes('uuid') || propertyLower.includes('id')) {
2240
- generators.push('fc.uuid()');
2241
- }
2242
-
2243
- // Default fallback if no specific type detected
2244
- if (generators.length === 0) {
2245
- generators.push('fc.anything()');
2246
- }
2247
-
2248
- return generators;
2249
- }
2250
-
2251
- private getGeneratorForType(type: string): string {
2252
- const typeGenerators: Record<string, string> = {
2253
- string: 'fc.string()',
2254
- number: 'fc.integer()',
2255
- integer: 'fc.integer()',
2256
- float: 'fc.float()',
2257
- boolean: 'fc.boolean()',
2258
- date: 'fc.date()',
2259
- uuid: 'fc.uuid()',
2260
- anything: 'fc.anything()',
2261
- };
2262
- return typeGenerators[type.toLowerCase()] || 'fc.anything()';
2263
- }
2264
-
2265
- private collectArbitraries(tests: { generators: string[] }[]): string[] {
2266
- const arbitraries = new Set<string>();
2267
- for (const test of tests) {
2268
- test.generators.forEach((g) => arbitraries.add(g));
2269
- }
2270
- return Array.from(arbitraries);
2271
- }
2272
-
2273
- private generateRecordFromSchema(
2274
- schema: Record<string, unknown>,
2275
- seed: number,
2276
- locale: string
2277
- ): Record<string, unknown> {
2278
- // Set faker locale and seed for reproducibility
2279
- faker.seed(seed);
2280
- if (locale && locale !== 'en') {
2281
- // Note: faker v8+ uses different locale handling
2282
- // For now, we use the default locale
2283
- }
2284
-
2285
- const record: Record<string, unknown> = {};
2286
-
2287
- for (const [key, fieldDef] of Object.entries(schema)) {
2288
- record[key] = this.generateValueForField(key, fieldDef, seed);
2289
- }
2290
-
2291
- return record;
2292
- }
2293
-
2294
- private generateValueForField(
2295
- fieldName: string,
2296
- fieldDef: unknown,
2297
- _seed: number
2298
- ): unknown {
2299
- // Handle simple type strings
2300
- if (typeof fieldDef === 'string') {
2301
- return this.generateValueForType(fieldDef, fieldName);
2302
- }
2303
-
2304
- // Handle complex field definitions
2305
- if (typeof fieldDef === 'object' && fieldDef !== null) {
2306
- const field = fieldDef as SchemaField;
2307
-
2308
- // Use explicit faker method if specified
2309
- if (field.faker) {
2310
- return this.callFakerMethod(field.faker);
2311
- }
2312
-
2313
- return this.generateValueForType(field.type, fieldName, field);
2314
- }
2315
-
2316
- return null;
2317
- }
2318
-
2319
- private generateValueForType(
2320
- type: string,
2321
- fieldName: string,
2322
- options?: SchemaField
2323
- ): unknown {
2324
- const normalizedType = type.toLowerCase();
2325
-
2326
- // Try to infer the best faker method based on field name and type
2327
- switch (normalizedType) {
2328
- case 'string':
2329
- return this.generateStringValue(fieldName, options);
2330
- case 'number':
2331
- case 'int':
2332
- case 'integer':
2333
- return this.generateNumberValue(options);
2334
- case 'float':
2335
- case 'decimal':
2336
- return faker.number.float({ min: options?.min ?? 0, max: options?.max ?? 1000, fractionDigits: 2 });
2337
- case 'boolean':
2338
- case 'bool':
2339
- return faker.datatype.boolean();
2340
- case 'date':
2341
- case 'datetime':
2342
- return faker.date.recent().toISOString();
2343
- case 'email':
2344
- return faker.internet.email();
2345
- case 'uuid':
2346
- case 'id':
2347
- return faker.string.uuid();
2348
- case 'url':
2349
- return faker.internet.url();
2350
- case 'phone':
2351
- return faker.phone.number();
2352
- case 'address':
2353
- return this.generateAddress();
2354
- case 'name':
2355
- case 'fullname':
2356
- return faker.person.fullName();
2357
- case 'firstname':
2358
- return faker.person.firstName();
2359
- case 'lastname':
2360
- return faker.person.lastName();
2361
- case 'username':
2362
- return faker.internet.username();
2363
- case 'password':
2364
- return faker.internet.password();
2365
- case 'company':
2366
- return faker.company.name();
2367
- case 'jobtitle':
2368
- return faker.person.jobTitle();
2369
- case 'text':
2370
- case 'paragraph':
2371
- return faker.lorem.paragraph();
2372
- case 'sentence':
2373
- return faker.lorem.sentence();
2374
- case 'word':
2375
- case 'words':
2376
- return faker.lorem.word();
2377
- case 'avatar':
2378
- case 'image':
2379
- return faker.image.avatar();
2380
- case 'color':
2381
- return faker.color.rgb();
2382
- case 'ipaddress':
2383
- case 'ip':
2384
- return faker.internet.ipv4();
2385
- case 'mac':
2386
- return faker.internet.mac();
2387
- case 'latitude':
2388
- return faker.location.latitude();
2389
- case 'longitude':
2390
- return faker.location.longitude();
2391
- case 'country':
2392
- return faker.location.country();
2393
- case 'city':
2394
- return faker.location.city();
2395
- case 'zipcode':
2396
- case 'postalcode':
2397
- return faker.location.zipCode();
2398
- case 'creditcard':
2399
- return faker.finance.creditCardNumber();
2400
- case 'currency':
2401
- return faker.finance.currencyCode();
2402
- case 'amount':
2403
- case 'price':
2404
- return faker.finance.amount();
2405
- case 'json':
2406
- case 'object':
2407
- return { key: faker.lorem.word(), value: faker.lorem.sentence() };
2408
- case 'array':
2409
- return [faker.lorem.word(), faker.lorem.word(), faker.lorem.word()];
2410
- case 'enum':
2411
- if (options?.enum && options.enum.length > 0) {
2412
- return faker.helpers.arrayElement(options.enum);
2413
- }
2414
- return faker.lorem.word();
2415
- default:
2416
- // Try to infer from field name
2417
- return this.inferValueFromFieldName(fieldName);
2418
- }
2419
- }
2420
-
2421
- private generateStringValue(fieldName: string, options?: SchemaField): string {
2422
- const lowerName = fieldName.toLowerCase();
2423
-
2424
- // Infer type from field name
2425
- if (lowerName.includes('email')) return faker.internet.email();
2426
- if (lowerName.includes('name') && lowerName.includes('first')) return faker.person.firstName();
2427
- if (lowerName.includes('name') && lowerName.includes('last')) return faker.person.lastName();
2428
- if (lowerName.includes('name')) return faker.person.fullName();
2429
- if (lowerName.includes('phone')) return faker.phone.number();
2430
- if (lowerName.includes('address')) return faker.location.streetAddress();
2431
- if (lowerName.includes('city')) return faker.location.city();
2432
- if (lowerName.includes('country')) return faker.location.country();
2433
- if (lowerName.includes('zip') || lowerName.includes('postal')) return faker.location.zipCode();
2434
- if (lowerName.includes('url') || lowerName.includes('website')) return faker.internet.url();
2435
- if (lowerName.includes('username') || lowerName.includes('user')) return faker.internet.username();
2436
- if (lowerName.includes('password')) return faker.internet.password();
2437
- if (lowerName.includes('description') || lowerName.includes('bio')) return faker.lorem.paragraph();
2438
- if (lowerName.includes('title')) return faker.lorem.sentence();
2439
- if (lowerName.includes('company')) return faker.company.name();
2440
- if (lowerName.includes('job')) return faker.person.jobTitle();
2441
- if (lowerName.includes('avatar') || lowerName.includes('image')) return faker.image.avatar();
2442
-
2443
- // Apply pattern if provided
2444
- if (options?.pattern) {
2445
- return faker.helpers.fromRegExp(options.pattern);
2446
- }
2447
-
2448
- // Default string generation
2449
- return faker.lorem.words(3);
2450
- }
2451
-
2452
- private generateNumberValue(options?: SchemaField): number {
2453
- const min = options?.min ?? 0;
2454
- const max = options?.max ?? 10000;
2455
- return faker.number.int({ min, max });
2456
- }
2457
-
2458
- private generateAddress(): Record<string, string> {
2459
- return {
2460
- street: faker.location.streetAddress(),
2461
- city: faker.location.city(),
2462
- state: faker.location.state(),
2463
- zipCode: faker.location.zipCode(),
2464
- country: faker.location.country(),
2465
- };
2466
- }
2467
-
2468
- private inferValueFromFieldName(fieldName: string): unknown {
2469
- const lowerName = fieldName.toLowerCase();
2470
-
2471
- if (lowerName.includes('id')) return faker.string.uuid();
2472
- if (lowerName.includes('email')) return faker.internet.email();
2473
- if (lowerName.includes('name')) return faker.person.fullName();
2474
- if (lowerName.includes('phone')) return faker.phone.number();
2475
- if (lowerName.includes('date') || lowerName.includes('time')) return faker.date.recent().toISOString();
2476
- if (lowerName.includes('url')) return faker.internet.url();
2477
- if (lowerName.includes('count') || lowerName.includes('amount')) return faker.number.int({ min: 0, max: 100 });
2478
- if (lowerName.includes('price')) return faker.finance.amount();
2479
- if (lowerName.includes('active') || lowerName.includes('enabled') || lowerName.includes('is')) {
2480
- return faker.datatype.boolean();
2481
- }
2482
-
2483
- // Default to a random string
2484
- return faker.lorem.word();
2485
- }
2486
-
2487
- private callFakerMethod(methodPath: string): unknown {
2488
- try {
2489
- const parts = methodPath.split('.');
2490
- let result: unknown = faker;
2491
-
2492
- for (const part of parts) {
2493
- if (result && typeof result === 'object' && part in result) {
2494
- const next = (result as Record<string, unknown>)[part];
2495
- if (typeof next === 'function') {
2496
- result = (next as () => unknown)();
2497
- } else {
2498
- result = next;
2499
- }
2500
- } else {
2501
- return faker.lorem.word();
2502
- }
2503
- }
2504
-
2505
- return result;
2506
- } catch {
2507
- return faker.lorem.word();
2508
- }
2509
- }
2510
-
2511
- private linkRelatedRecords(
2512
- records: unknown[],
2513
- schema: Record<string, unknown>
2514
- ): void {
2515
- // Find fields with references and link them
2516
- const referenceFields: Array<{ field: string; reference: string }> = [];
2517
-
2518
- for (const [key, fieldDef] of Object.entries(schema)) {
2519
- if (typeof fieldDef === 'object' && fieldDef !== null) {
2520
- const field = fieldDef as SchemaField;
2521
- if (field.reference) {
2522
- referenceFields.push({ field: key, reference: field.reference });
2523
- }
2524
- }
2525
- }
2526
-
2527
- // If we have reference fields, link records
2528
- if (referenceFields.length > 0) {
2529
- for (let i = 0; i < records.length; i++) {
2530
- const record = records[i] as Record<string, unknown>;
2531
- for (const { field, reference } of referenceFields) {
2532
- // Link to a random previous record's ID or create a new one
2533
- if (i > 0 && reference === 'id') {
2534
- const prevRecord = records[Math.floor(Math.random() * i)] as Record<string, unknown>;
2535
- record[field] = prevRecord['id'] ?? faker.string.uuid();
2536
- } else {
2537
- record[field] = faker.string.uuid();
2538
- }
2539
- }
2540
- }
2541
- }
2542
- }
2543
-
2544
- private async generateTestForLines(
2545
- file: string,
2546
- lines: number[],
2547
- framework: string
2548
- ): Promise<GeneratedTest | null> {
2549
- if (lines.length === 0) return null;
2550
-
2551
- const testId = uuidv4();
2552
- const testFile = this.getTestFilePath(file, framework);
2553
- const moduleName = this.extractModuleName(file);
2554
- const importPath = this.getImportPath(file);
2555
-
2556
- // Generate meaningful coverage test code
2557
- const testCode = this.generateCoverageTestCode(moduleName, importPath, lines, framework);
2558
-
2559
- return {
2560
- id: testId,
2561
- name: `Coverage test for lines ${lines[0]}-${lines[lines.length - 1]}`,
2562
- sourceFile: file,
2563
- testFile,
2564
- testCode,
2565
- type: 'unit',
2566
- assertions: this.countAssertions(testCode),
2567
- };
2568
- }
2569
-
2570
- /**
2571
- * Generate actual coverage test code for specific lines
2572
- */
2573
- private generateCoverageTestCode(
2574
- moduleName: string,
2575
- importPath: string,
2576
- lines: number[],
2577
- framework: string
2578
- ): string {
2579
- const funcName = this.camelCase(moduleName);
2580
- const lineRange = lines.length === 1
2581
- ? `line ${lines[0]}`
2582
- : `lines ${lines[0]}-${lines[lines.length - 1]}`;
2583
-
2584
- if (framework === 'pytest') {
2585
- return `# Coverage test for ${lineRange} in ${moduleName}
2586
- import pytest
2587
- from ${importPath.replace(/\//g, '.')} import ${funcName}
2588
-
2589
- class Test${this.pascalCase(moduleName)}Coverage:
2590
- """Tests to cover ${lineRange}"""
2591
-
2592
- def test_cover_${lines[0]}_${lines[lines.length - 1]}(self):
2593
- """Exercise code path covering ${lineRange}"""
2594
- # Arrange: Set up test inputs to reach uncovered lines
2595
- test_input = None # Replace with appropriate input
2596
-
2597
- # Act: Execute the code path
2598
- try:
2599
- result = ${funcName}(test_input)
2600
-
2601
- # Assert: Verify expected behavior
2602
- assert result is not None
2603
- except Exception as e:
2604
- # If exception is expected for this path, verify it
2605
- pytest.fail(f"Unexpected exception: {e}")
2606
- `;
2607
- }
2608
-
2609
- // Default: Jest/Vitest format
2610
- return `// Coverage test for ${lineRange} in ${moduleName}
2611
- import { ${funcName} } from '${importPath}';
2612
-
2613
- describe('${moduleName} coverage', () => {
2614
- describe('${lineRange}', () => {
2615
- it('should execute code path covering ${lineRange}', () => {
2616
- // Arrange: Set up test inputs to reach uncovered lines
2617
- const testInput = undefined; // Replace with appropriate input
2618
-
2619
- // Act: Execute the code path
2620
- const result = ${funcName}(testInput);
2621
-
2622
- // Assert: Verify the code was reached and behaves correctly
2623
- expect(result).toBeDefined();
2624
- });
2625
-
2626
- it('should handle edge case for ${lineRange}', () => {
2627
- // Arrange: Set up edge case input
2628
- const edgeCaseInput = null;
2629
-
2630
- // Act & Assert: Verify edge case handling
2631
- expect(() => ${funcName}(edgeCaseInput)).not.toThrow();
2632
- });
2633
- });
2634
- });
2635
- `;
2636
- }
2637
-
2638
- private groupConsecutiveLines(lines: number[]): number[][] {
2639
- if (lines.length === 0) return [];
2640
-
2641
- const sorted = [...lines].sort((a, b) => a - b);
2642
- const groups: number[][] = [[sorted[0]]];
2643
-
2644
- for (let i = 1; i < sorted.length; i++) {
2645
- const currentGroup = groups[groups.length - 1];
2646
- if (sorted[i] - currentGroup[currentGroup.length - 1] <= 3) {
2647
- currentGroup.push(sorted[i]);
2648
- } else {
2649
- groups.push([sorted[i]]);
2650
- }
2651
- }
2652
-
2653
- return groups;
2654
- }
2655
-
2656
- private getTestFilePath(sourceFile: string, framework: string): string {
2657
- const ext = sourceFile.split('.').pop() || 'ts';
2658
- const base = sourceFile.replace(`.${ext}`, '');
2659
-
2660
- if (framework === 'pytest') {
2661
- return `test_${base.split('/').pop()}.py`;
2662
- }
2663
-
2664
- return `${base}.test.${ext}`;
2665
- }
2666
-
2667
- private extractModuleName(sourceFile: string): string {
2668
- const filename = sourceFile.split('/').pop() || sourceFile;
2669
- return filename.replace(/\.(ts|js|tsx|jsx|py)$/, '');
2670
- }
2671
-
2672
- private getImportPath(sourceFile: string): string {
2673
- return sourceFile.replace(/\.(ts|js|tsx|jsx)$/, '');
2674
- }
2675
-
2676
- private countAssertions(testCode: string): number {
2677
- const assertPatterns = [
2678
- /expect\(/g,
2679
- /assert/g,
2680
- /\.to\./g,
2681
- /\.toBe/g,
2682
- /\.toEqual/g,
2683
- ];
2684
-
2685
- let count = 0;
2686
- for (const pattern of assertPatterns) {
2687
- const matches = testCode.match(pattern);
2688
- count += matches ? matches.length : 0;
2689
- }
2690
-
2691
- return Math.max(1, count);
2692
- }
2693
-
2694
- private estimateCoverage(tests: GeneratedTest[], target: number): number {
2695
- // Estimate coverage based on test characteristics
2696
- const totalAssertions = tests.reduce((sum, t) => sum + t.assertions, 0);
2697
- const totalTests = tests.length;
2698
-
2699
- // Base coverage from test count (each test covers ~3-5% typically)
2700
- const testBasedCoverage = totalTests * 4;
2701
-
2702
- // Additional coverage from assertions (each assertion ~1-2%)
2703
- const assertionCoverage = totalAssertions * 1.5;
2704
-
2705
- // Test type multipliers (integration tests cover more)
2706
- const typeMultiplier = tests.reduce((mult, t) => {
2707
- if (t.type === 'integration') return mult + 0.1;
2708
- if (t.type === 'e2e') return mult + 0.15;
2709
- return mult;
2710
- }, 1);
2711
-
2712
- // Calculate estimated coverage with diminishing returns
2713
- const rawEstimate = (testBasedCoverage + assertionCoverage) * typeMultiplier;
2714
- const diminishedEstimate = rawEstimate * (1 - rawEstimate / 200); // Diminishing returns above 100%
2715
-
2716
- // Cap at target and round
2717
- const estimatedCoverage = Math.min(target, Math.max(0, diminishedEstimate));
2718
- return Math.round(estimatedCoverage * 10) / 10;
2719
- }
2720
-
2721
- private camelCase(str: string): string {
2722
- return str
2723
- .replace(/[^a-zA-Z0-9]+(.)/g, (_, chr) => chr.toUpperCase())
2724
- .replace(/^./, (chr) => chr.toLowerCase());
2725
- }
2726
-
2727
- private pascalCase(str: string): string {
2728
- return str
2729
- .replace(/[^a-zA-Z0-9]+(.)/g, (_, chr) => chr.toUpperCase())
2730
- .replace(/^./, (chr) => chr.toUpperCase());
2731
- }
2732
-
2733
- private async storeGenerationMetadata(
2734
- tests: GeneratedTest[],
2735
- patterns: string[]
2736
- ): Promise<void> {
2737
- const metadata = {
2738
- generatedAt: new Date().toISOString(),
2739
- testCount: tests.length,
2740
- patterns,
2741
- testIds: tests.map((t) => t.id),
2742
- };
2743
-
2744
- await this.memory.set(
2745
- `test-generation:metadata:${Date.now()}`,
2746
- metadata,
2747
- { namespace: 'test-generation', ttl: 86400 * 7 } // 7 days
2748
- );
2749
- }
2750
- }