@agentic-qe/v3 3.0.0-alpha.5 → 3.0.0-alpha.7

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 (609) 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 +8 -8
  149. package/dist/init/agents-installer.js +4 -4
  150. package/dist/init/agents-installer.js.map +1 -1
  151. package/dist/init/skills-installer.js +4 -4
  152. package/dist/init/skills-installer.js.map +1 -1
  153. package/package.json +8 -2
  154. package/docs/analysis/V3-INIT-REQUIREMENTS-ANALYSIS.md +0 -352
  155. package/implementation/README.md +0 -90
  156. package/implementation/adrs/ADR-030-coherence-gated-quality-gates.md +0 -312
  157. package/implementation/adrs/ADR-031-strange-loop-self-awareness.md +0 -484
  158. package/implementation/adrs/ADR-032-time-crystal-scheduling.md +0 -530
  159. package/implementation/adrs/ADR-033-early-exit-testing.md +0 -634
  160. package/implementation/adrs/ADR-034-neural-topology-optimizer.md +0 -589
  161. package/implementation/adrs/ADR-035-causal-discovery.md +0 -610
  162. package/implementation/adrs/ADR-036-result-persistence.md +0 -326
  163. package/implementation/adrs/ADR-037-v3-qe-agent-naming.md +0 -105
  164. package/implementation/adrs/ADR-038-v3-qe-memory-unification.md +0 -154
  165. package/implementation/adrs/ADR-039-v3-qe-mcp-optimization.md +0 -179
  166. package/implementation/adrs/ADR-040-v3-qe-agentic-flow-integration.md +0 -240
  167. package/implementation/adrs/ADR-041-v3-qe-cli-enhancement.md +0 -296
  168. package/implementation/adrs/ADR-042-v3-qe-token-tracking-integration.md +0 -517
  169. package/implementation/adrs/v3-adrs.md +0 -2783
  170. package/implementation/planning/AQE-V3-MASTER-PLAN.md +0 -815
  171. package/security-scan-report-2026-01-11.md +0 -410
  172. package/security-verification-report-2026-01-11.md +0 -278
  173. package/src/benchmarks/performance-benchmarks.ts +0 -646
  174. package/src/benchmarks/run-benchmarks.ts +0 -324
  175. package/src/causal-discovery/causal-graph.ts +0 -450
  176. package/src/causal-discovery/discovery-engine.ts +0 -438
  177. package/src/causal-discovery/index.ts +0 -117
  178. package/src/causal-discovery/types.ts +0 -456
  179. package/src/causal-discovery/weight-matrix.ts +0 -453
  180. package/src/cli/commands/qe-tools.ts +0 -634
  181. package/src/cli/index.ts +0 -1976
  182. package/src/compatibility/agent-mapper.ts +0 -291
  183. package/src/compatibility/cli-adapter.ts +0 -277
  184. package/src/compatibility/config-migrator.ts +0 -334
  185. package/src/compatibility/index.ts +0 -112
  186. package/src/compatibility/mcp-adapter.ts +0 -248
  187. package/src/compatibility/types.ts +0 -156
  188. package/src/coordination/claims/claim-repository.ts +0 -636
  189. package/src/coordination/claims/claim-service.ts +0 -675
  190. package/src/coordination/claims/handoff-manager.ts +0 -535
  191. package/src/coordination/claims/index.ts +0 -276
  192. package/src/coordination/claims/interfaces.ts +0 -687
  193. package/src/coordination/claims/work-stealing.ts +0 -436
  194. package/src/coordination/cross-domain-router.ts +0 -492
  195. package/src/coordination/index.ts +0 -127
  196. package/src/coordination/interfaces.ts +0 -691
  197. package/src/coordination/protocol-executor.ts +0 -760
  198. package/src/coordination/protocols/code-intelligence-index.ts +0 -855
  199. package/src/coordination/protocols/defect-investigation.ts +0 -1184
  200. package/src/coordination/protocols/index.ts +0 -11
  201. package/src/coordination/protocols/learning-consolidation.ts +0 -1181
  202. package/src/coordination/protocols/morning-sync.ts +0 -1055
  203. package/src/coordination/protocols/quality-gate.ts +0 -1566
  204. package/src/coordination/protocols/security-audit.ts +0 -1587
  205. package/src/coordination/queen-coordinator.ts +0 -1176
  206. package/src/coordination/result-saver.ts +0 -780
  207. package/src/coordination/task-executor.ts +0 -1146
  208. package/src/coordination/workflow-orchestrator.ts +0 -1917
  209. package/src/domains/chaos-resilience/coordinator.ts +0 -1032
  210. package/src/domains/chaos-resilience/index.ts +0 -143
  211. package/src/domains/chaos-resilience/interfaces.ts +0 -659
  212. package/src/domains/chaos-resilience/plugin.ts +0 -691
  213. package/src/domains/chaos-resilience/services/chaos-engineer.ts +0 -1097
  214. package/src/domains/chaos-resilience/services/index.ts +0 -19
  215. package/src/domains/chaos-resilience/services/load-tester.ts +0 -799
  216. package/src/domains/chaos-resilience/services/performance-profiler.ts +0 -792
  217. package/src/domains/code-intelligence/coordinator.ts +0 -631
  218. package/src/domains/code-intelligence/index.ts +0 -86
  219. package/src/domains/code-intelligence/interfaces.ts +0 -162
  220. package/src/domains/code-intelligence/plugin.ts +0 -451
  221. package/src/domains/code-intelligence/services/impact-analyzer.ts +0 -567
  222. package/src/domains/code-intelligence/services/index.ts +0 -26
  223. package/src/domains/code-intelligence/services/knowledge-graph.ts +0 -1067
  224. package/src/domains/code-intelligence/services/semantic-analyzer.ts +0 -901
  225. package/src/domains/contract-testing/coordinator.ts +0 -1038
  226. package/src/domains/contract-testing/index.ts +0 -122
  227. package/src/domains/contract-testing/interfaces.ts +0 -458
  228. package/src/domains/contract-testing/plugin.ts +0 -746
  229. package/src/domains/contract-testing/services/api-compatibility.ts +0 -748
  230. package/src/domains/contract-testing/services/contract-validator.ts +0 -1700
  231. package/src/domains/contract-testing/services/index.ts +0 -19
  232. package/src/domains/contract-testing/services/schema-validator.ts +0 -1102
  233. package/src/domains/coverage-analysis/coordinator.ts +0 -485
  234. package/src/domains/coverage-analysis/index.ts +0 -114
  235. package/src/domains/coverage-analysis/interfaces.ts +0 -142
  236. package/src/domains/coverage-analysis/plugin.ts +0 -172
  237. package/src/domains/coverage-analysis/services/coverage-analyzer.ts +0 -449
  238. package/src/domains/coverage-analysis/services/coverage-embedder.ts +0 -733
  239. package/src/domains/coverage-analysis/services/coverage-parser.ts +0 -753
  240. package/src/domains/coverage-analysis/services/gap-detector.ts +0 -592
  241. package/src/domains/coverage-analysis/services/hnsw-index.ts +0 -728
  242. package/src/domains/coverage-analysis/services/index.ts +0 -61
  243. package/src/domains/coverage-analysis/services/risk-scorer.ts +0 -540
  244. package/src/domains/coverage-analysis/services/sublinear-analyzer.ts +0 -747
  245. package/src/domains/defect-intelligence/coordinator.ts +0 -635
  246. package/src/domains/defect-intelligence/index.ts +0 -83
  247. package/src/domains/defect-intelligence/interfaces.ts +0 -152
  248. package/src/domains/defect-intelligence/plugin.ts +0 -483
  249. package/src/domains/defect-intelligence/services/causal-root-cause-analyzer.ts +0 -494
  250. package/src/domains/defect-intelligence/services/defect-predictor.ts +0 -852
  251. package/src/domains/defect-intelligence/services/index.ts +0 -37
  252. package/src/domains/defect-intelligence/services/pattern-learner.ts +0 -738
  253. package/src/domains/defect-intelligence/services/root-cause-analyzer.ts +0 -637
  254. package/src/domains/domain-interface.ts +0 -77
  255. package/src/domains/index.ts +0 -23
  256. package/src/domains/learning-optimization/coordinator.ts +0 -1215
  257. package/src/domains/learning-optimization/index.ts +0 -127
  258. package/src/domains/learning-optimization/interfaces.ts +0 -570
  259. package/src/domains/learning-optimization/plugin.ts +0 -851
  260. package/src/domains/learning-optimization/services/index.ts +0 -29
  261. package/src/domains/learning-optimization/services/learning-coordinator.ts +0 -972
  262. package/src/domains/learning-optimization/services/metrics-optimizer.ts +0 -915
  263. package/src/domains/learning-optimization/services/production-intel.ts +0 -971
  264. package/src/domains/learning-optimization/services/transfer-specialist.ts +0 -723
  265. package/src/domains/quality-assessment/coherence/gate-controller.ts +0 -549
  266. package/src/domains/quality-assessment/coherence/index.ts +0 -211
  267. package/src/domains/quality-assessment/coherence/lambda-calculator.ts +0 -384
  268. package/src/domains/quality-assessment/coherence/partition-detector.ts +0 -469
  269. package/src/domains/quality-assessment/coherence/types.ts +0 -384
  270. package/src/domains/quality-assessment/coordinator.ts +0 -605
  271. package/src/domains/quality-assessment/index.ts +0 -97
  272. package/src/domains/quality-assessment/interfaces.ts +0 -152
  273. package/src/domains/quality-assessment/plugin.ts +0 -496
  274. package/src/domains/quality-assessment/services/coherence-gate.ts +0 -358
  275. package/src/domains/quality-assessment/services/deployment-advisor.ts +0 -571
  276. package/src/domains/quality-assessment/services/index.ts +0 -34
  277. package/src/domains/quality-assessment/services/quality-analyzer.ts +0 -670
  278. package/src/domains/quality-assessment/services/quality-gate.ts +0 -384
  279. package/src/domains/requirements-validation/coordinator.ts +0 -812
  280. package/src/domains/requirements-validation/index.ts +0 -92
  281. package/src/domains/requirements-validation/interfaces.ts +0 -303
  282. package/src/domains/requirements-validation/plugin.ts +0 -576
  283. package/src/domains/requirements-validation/services/bdd-scenario-writer.ts +0 -676
  284. package/src/domains/requirements-validation/services/index.ts +0 -20
  285. package/src/domains/requirements-validation/services/requirements-validator.ts +0 -559
  286. package/src/domains/requirements-validation/services/testability-scorer.ts +0 -639
  287. package/src/domains/security-compliance/coordinator.ts +0 -757
  288. package/src/domains/security-compliance/index.ts +0 -120
  289. package/src/domains/security-compliance/interfaces.ts +0 -434
  290. package/src/domains/security-compliance/plugin.ts +0 -509
  291. package/src/domains/security-compliance/services/compliance-validator.ts +0 -1226
  292. package/src/domains/security-compliance/services/index.ts +0 -31
  293. package/src/domains/security-compliance/services/security-auditor.ts +0 -2227
  294. package/src/domains/security-compliance/services/security-scanner.ts +0 -2354
  295. package/src/domains/security-compliance/services/semgrep-integration.ts +0 -289
  296. package/src/domains/test-execution/coordinator.ts +0 -426
  297. package/src/domains/test-execution/index.ts +0 -76
  298. package/src/domains/test-execution/interfaces.ts +0 -119
  299. package/src/domains/test-execution/plugin.ts +0 -208
  300. package/src/domains/test-execution/services/flaky-detector.ts +0 -1240
  301. package/src/domains/test-execution/services/index.ts +0 -8
  302. package/src/domains/test-execution/services/retry-handler.ts +0 -820
  303. package/src/domains/test-execution/services/test-executor.ts +0 -885
  304. package/src/domains/test-generation/coordinator.ts +0 -656
  305. package/src/domains/test-generation/index.ts +0 -77
  306. package/src/domains/test-generation/interfaces.ts +0 -118
  307. package/src/domains/test-generation/plugin.ts +0 -397
  308. package/src/domains/test-generation/services/index.ts +0 -23
  309. package/src/domains/test-generation/services/pattern-matcher.ts +0 -1725
  310. package/src/domains/test-generation/services/test-generator.ts +0 -2750
  311. package/src/domains/visual-accessibility/coordinator.ts +0 -860
  312. package/src/domains/visual-accessibility/index.ts +0 -116
  313. package/src/domains/visual-accessibility/interfaces.ts +0 -435
  314. package/src/domains/visual-accessibility/plugin.ts +0 -568
  315. package/src/domains/visual-accessibility/services/accessibility-tester.ts +0 -982
  316. package/src/domains/visual-accessibility/services/axe-core-audit.ts +0 -630
  317. package/src/domains/visual-accessibility/services/index.ts +0 -28
  318. package/src/domains/visual-accessibility/services/responsive-tester.ts +0 -934
  319. package/src/domains/visual-accessibility/services/visual-tester.ts +0 -458
  320. package/src/early-exit/early-exit-controller.ts +0 -490
  321. package/src/early-exit/early-exit-decision.ts +0 -391
  322. package/src/early-exit/index.ts +0 -115
  323. package/src/early-exit/quality-signal.ts +0 -389
  324. package/src/early-exit/speculative-executor.ts +0 -505
  325. package/src/early-exit/types.ts +0 -407
  326. package/src/feedback/coverage-learner.ts +0 -456
  327. package/src/feedback/feedback-loop.ts +0 -426
  328. package/src/feedback/index.ts +0 -72
  329. package/src/feedback/pattern-promotion.ts +0 -373
  330. package/src/feedback/quality-score-calculator.ts +0 -334
  331. package/src/feedback/test-outcome-tracker.ts +0 -450
  332. package/src/feedback/types.ts +0 -497
  333. package/src/index.ts +0 -224
  334. package/src/init/agents-installer.ts +0 -536
  335. package/src/init/index.ts +0 -80
  336. package/src/init/init-wizard.ts +0 -1061
  337. package/src/init/project-analyzer.ts +0 -696
  338. package/src/init/self-configurator.ts +0 -488
  339. package/src/init/skills-installer.ts +0 -467
  340. package/src/init/types.ts +0 -432
  341. package/src/integrations/ruvector/ast-complexity.ts +0 -470
  342. package/src/integrations/ruvector/coverage-router.ts +0 -594
  343. package/src/integrations/ruvector/diff-risk-classifier.ts +0 -759
  344. package/src/integrations/ruvector/fallback.ts +0 -942
  345. package/src/integrations/ruvector/graph-boundaries.ts +0 -809
  346. package/src/integrations/ruvector/index.ts +0 -363
  347. package/src/integrations/ruvector/interfaces.ts +0 -609
  348. package/src/integrations/ruvector/q-learning-router.ts +0 -550
  349. package/src/kernel/agent-coordinator.ts +0 -165
  350. package/src/kernel/agentdb-backend.ts +0 -504
  351. package/src/kernel/event-bus.ts +0 -129
  352. package/src/kernel/hybrid-backend.ts +0 -538
  353. package/src/kernel/index.ts +0 -28
  354. package/src/kernel/interfaces.ts +0 -257
  355. package/src/kernel/kernel.ts +0 -285
  356. package/src/kernel/memory-backend.ts +0 -169
  357. package/src/kernel/memory-factory.ts +0 -293
  358. package/src/kernel/plugin-loader.ts +0 -179
  359. package/src/learning/index.ts +0 -219
  360. package/src/learning/pattern-store.ts +0 -990
  361. package/src/learning/qe-guidance.ts +0 -832
  362. package/src/learning/qe-hooks.ts +0 -644
  363. package/src/learning/qe-patterns.ts +0 -449
  364. package/src/learning/qe-reasoning-bank.ts +0 -951
  365. package/src/learning/real-embeddings.ts +0 -277
  366. package/src/learning/real-qe-reasoning-bank.ts +0 -833
  367. package/src/learning/sqlite-persistence.ts +0 -554
  368. package/src/mcp/entry.ts +0 -59
  369. package/src/mcp/handlers/agent-handlers.ts +0 -285
  370. package/src/mcp/handlers/core-handlers.ts +0 -317
  371. package/src/mcp/handlers/domain-handlers.ts +0 -1444
  372. package/src/mcp/handlers/index.ts +0 -57
  373. package/src/mcp/handlers/memory-handlers.ts +0 -338
  374. package/src/mcp/handlers/task-handlers.ts +0 -363
  375. package/src/mcp/index.ts +0 -30
  376. package/src/mcp/metrics/index.ts +0 -14
  377. package/src/mcp/metrics/metrics-collector.ts +0 -503
  378. package/src/mcp/protocol-server.ts +0 -752
  379. package/src/mcp/security/cve-prevention.ts +0 -742
  380. package/src/mcp/security/index.ts +0 -356
  381. package/src/mcp/security/oauth21-provider.ts +0 -821
  382. package/src/mcp/security/rate-limiter.ts +0 -615
  383. package/src/mcp/security/sampling-server.ts +0 -662
  384. package/src/mcp/security/schema-validator.ts +0 -855
  385. package/src/mcp/server.ts +0 -657
  386. package/src/mcp/tool-registry.ts +0 -391
  387. package/src/mcp/tools/base.ts +0 -399
  388. package/src/mcp/tools/chaos-resilience/inject.ts +0 -699
  389. package/src/mcp/tools/code-intelligence/analyze.ts +0 -745
  390. package/src/mcp/tools/contract-testing/validate.ts +0 -708
  391. package/src/mcp/tools/coverage-analysis/index.ts +0 -770
  392. package/src/mcp/tools/defect-intelligence/predict.ts +0 -466
  393. package/src/mcp/tools/index.ts +0 -214
  394. package/src/mcp/tools/learning-optimization/optimize.ts +0 -772
  395. package/src/mcp/tools/quality-assessment/evaluate.ts +0 -385
  396. package/src/mcp/tools/registry.ts +0 -248
  397. package/src/mcp/tools/requirements-validation/validate.ts +0 -394
  398. package/src/mcp/tools/security-compliance/scan.ts +0 -365
  399. package/src/mcp/tools/test-execution/execute.ts +0 -291
  400. package/src/mcp/tools/test-generation/generate.ts +0 -544
  401. package/src/mcp/tools/visual-accessibility/index.ts +0 -791
  402. package/src/mcp/transport/index.ts +0 -31
  403. package/src/mcp/transport/stdio.ts +0 -318
  404. package/src/mcp/types.ts +0 -543
  405. package/src/neural-optimizer/index.ts +0 -111
  406. package/src/neural-optimizer/replay-buffer.ts +0 -455
  407. package/src/neural-optimizer/swarm-topology.ts +0 -508
  408. package/src/neural-optimizer/topology-optimizer.ts +0 -828
  409. package/src/neural-optimizer/types.ts +0 -481
  410. package/src/neural-optimizer/value-network.ts +0 -351
  411. package/src/optimization/auto-tuner.ts +0 -817
  412. package/src/optimization/index.ts +0 -77
  413. package/src/optimization/metric-collectors.ts +0 -474
  414. package/src/optimization/qe-workers.ts +0 -704
  415. package/src/optimization/tuning-algorithm.ts +0 -401
  416. package/src/optimization/types.ts +0 -314
  417. package/src/routing/index.ts +0 -51
  418. package/src/routing/qe-agent-registry.ts +0 -963
  419. package/src/routing/qe-task-router.ts +0 -564
  420. package/src/routing/routing-feedback.ts +0 -365
  421. package/src/routing/types.ts +0 -406
  422. package/src/shared/embeddings/embedding-cache.ts +0 -157
  423. package/src/shared/embeddings/index.ts +0 -50
  424. package/src/shared/embeddings/nomic-embedder.ts +0 -404
  425. package/src/shared/embeddings/ollama-client.ts +0 -195
  426. package/src/shared/embeddings/types.ts +0 -147
  427. package/src/shared/entities/agent.ts +0 -141
  428. package/src/shared/entities/base-entity.ts +0 -79
  429. package/src/shared/entities/index.ts +0 -6
  430. package/src/shared/events/domain-events.ts +0 -259
  431. package/src/shared/events/index.ts +0 -5
  432. package/src/shared/git/git-analyzer.ts +0 -656
  433. package/src/shared/git/index.ts +0 -11
  434. package/src/shared/http/http-client.ts +0 -420
  435. package/src/shared/http/index.ts +0 -13
  436. package/src/shared/index.ts +0 -41
  437. package/src/shared/io/file-reader.ts +0 -525
  438. package/src/shared/io/index.ts +0 -25
  439. package/src/shared/llm/cache.ts +0 -473
  440. package/src/shared/llm/circuit-breaker.ts +0 -369
  441. package/src/shared/llm/cost-tracker.ts +0 -460
  442. package/src/shared/llm/index.ts +0 -140
  443. package/src/shared/llm/interfaces.ts +0 -629
  444. package/src/shared/llm/provider-manager.ts +0 -685
  445. package/src/shared/llm/providers/claude.ts +0 -524
  446. package/src/shared/llm/providers/index.ts +0 -8
  447. package/src/shared/llm/providers/ollama.ts +0 -575
  448. package/src/shared/llm/providers/openai.ts +0 -609
  449. package/src/shared/metrics/code-metrics.ts +0 -520
  450. package/src/shared/metrics/index.ts +0 -23
  451. package/src/shared/metrics/system-metrics.ts +0 -353
  452. package/src/shared/parsers/index.ts +0 -6
  453. package/src/shared/parsers/typescript-parser.ts +0 -841
  454. package/src/shared/security/compliance-patterns.ts +0 -666
  455. package/src/shared/security/index.ts +0 -30
  456. package/src/shared/security/osv-client.ts +0 -468
  457. package/src/shared/types/index.ts +0 -150
  458. package/src/shared/value-objects/index.ts +0 -273
  459. package/src/strange-loop/healing-controller.ts +0 -833
  460. package/src/strange-loop/index.ts +0 -104
  461. package/src/strange-loop/self-model.ts +0 -494
  462. package/src/strange-loop/strange-loop.ts +0 -446
  463. package/src/strange-loop/swarm-observer.ts +0 -448
  464. package/src/strange-loop/topology-analyzer.ts +0 -565
  465. package/src/strange-loop/types.ts +0 -640
  466. package/src/time-crystal/default-phases.ts +0 -520
  467. package/src/time-crystal/index.ts +0 -164
  468. package/src/time-crystal/oscillator.ts +0 -425
  469. package/src/time-crystal/phase-executor.ts +0 -521
  470. package/src/time-crystal/scheduler.ts +0 -1025
  471. package/src/time-crystal/test-runner.ts +0 -787
  472. package/src/time-crystal/types.ts +0 -421
  473. package/src/workers/base-worker.ts +0 -304
  474. package/src/workers/daemon.ts +0 -264
  475. package/src/workers/index.ts +0 -119
  476. package/src/workers/interfaces.ts +0 -393
  477. package/src/workers/worker-manager.ts +0 -424
  478. package/src/workers/workers/compliance-checker.ts +0 -445
  479. package/src/workers/workers/coverage-tracker.ts +0 -344
  480. package/src/workers/workers/defect-predictor.ts +0 -375
  481. package/src/workers/workers/flaky-detector.ts +0 -390
  482. package/src/workers/workers/index.ts +0 -17
  483. package/src/workers/workers/learning-consolidation.ts +0 -442
  484. package/src/workers/workers/performance-baseline.ts +0 -434
  485. package/src/workers/workers/quality-gate.ts +0 -419
  486. package/src/workers/workers/regression-monitor.ts +0 -357
  487. package/src/workers/workers/security-scan.ts +0 -349
  488. package/src/workers/workers/test-health.ts +0 -359
  489. package/tests/integration/code-intelligence/knowledge-graph-real.test.ts +0 -540
  490. package/tests/integration/coordination/cross-domain-router.test.ts +0 -403
  491. package/tests/integration/coordination/protocol-executor.test.ts +0 -454
  492. package/tests/integration/coordination/workflow-orchestrator.test.ts +0 -418
  493. package/tests/integration/feedback/feedback-loop-integration.test.ts +0 -560
  494. package/tests/integration/migration/v2-to-v3-migration.test.ts +0 -471
  495. package/tests/integration/parsers/typescript-parser.test.ts +0 -463
  496. package/tests/integration/security/vulnerability-detection.test.ts +0 -628
  497. package/tests/integration/test-execution/coordinator.test.ts +0 -410
  498. package/tests/integration/test-generation/coordinator.test.ts +0 -361
  499. package/tests/mocks/index.ts +0 -228
  500. package/tests/time-crystal/default-phases.test.ts +0 -476
  501. package/tests/time-crystal/oscillator.test.ts +0 -541
  502. package/tests/time-crystal/phase-executor.test.ts +0 -653
  503. package/tests/time-crystal/scheduler.test.ts +0 -626
  504. package/tests/time-crystal/test-runner.test.ts +0 -594
  505. package/tests/unit/causal-discovery/causal-graph.test.ts +0 -504
  506. package/tests/unit/causal-discovery/causal-root-cause-analyzer.test.ts +0 -347
  507. package/tests/unit/causal-discovery/discovery-engine.test.ts +0 -435
  508. package/tests/unit/causal-discovery/weight-matrix.test.ts +0 -328
  509. package/tests/unit/cli/cli.test.ts +0 -341
  510. package/tests/unit/cli/commands.test.ts +0 -414
  511. package/tests/unit/cli/init-command.test.ts +0 -274
  512. package/tests/unit/cli/migrate-command.test.ts +0 -396
  513. package/tests/unit/coordination/claims/claim-service.test.ts +0 -949
  514. package/tests/unit/coordination/claims/handoff-manager.test.ts +0 -773
  515. package/tests/unit/coordination/claims/work-stealing.test.ts +0 -492
  516. package/tests/unit/coordination/queen-coordinator.test.ts +0 -966
  517. package/tests/unit/coordination/result-saver.test.ts +0 -653
  518. package/tests/unit/coordination/task-executor.test.ts +0 -810
  519. package/tests/unit/domains/chaos-resilience/chaos-engineer.test.ts +0 -484
  520. package/tests/unit/domains/chaos-resilience/load-tester.test.ts +0 -559
  521. package/tests/unit/domains/chaos-resilience/performance-profiler.test.ts +0 -490
  522. package/tests/unit/domains/code-intelligence/impact-analyzer.test.ts +0 -560
  523. package/tests/unit/domains/code-intelligence/knowledge-graph.test.ts +0 -460
  524. package/tests/unit/domains/code-intelligence/semantic-analyzer.test.ts +0 -584
  525. package/tests/unit/domains/contract-testing/api-compatibility.test.ts +0 -483
  526. package/tests/unit/domains/contract-testing/contract-validator.test.ts +0 -370
  527. package/tests/unit/domains/contract-testing/schema-validator.test.ts +0 -610
  528. package/tests/unit/domains/coverage-analysis/coverage-embedder.test.ts +0 -298
  529. package/tests/unit/domains/coverage-analysis/hnsw-index.test.ts +0 -292
  530. package/tests/unit/domains/coverage-analysis/sublinear-analyzer.test.ts +0 -506
  531. package/tests/unit/domains/defect-intelligence/defect-predictor.test.ts +0 -370
  532. package/tests/unit/domains/defect-intelligence/pattern-learner.test.ts +0 -546
  533. package/tests/unit/domains/defect-intelligence/root-cause-analyzer.test.ts +0 -534
  534. package/tests/unit/domains/learning-optimization/learning-coordinator.test.ts +0 -541
  535. package/tests/unit/domains/learning-optimization/metrics-optimizer.test.ts +0 -552
  536. package/tests/unit/domains/learning-optimization/production-intel.test.ts +0 -589
  537. package/tests/unit/domains/learning-optimization/transfer-specialist.test.ts +0 -453
  538. package/tests/unit/domains/quality-assessment/coherence-gate.test.ts +0 -1006
  539. package/tests/unit/domains/quality-assessment/deployment-advisor.test.ts +0 -515
  540. package/tests/unit/domains/quality-assessment/quality-analyzer.test.ts +0 -401
  541. package/tests/unit/domains/quality-assessment/quality-gate.test.ts +0 -324
  542. package/tests/unit/domains/requirements-validation/bdd-scenario-writer.test.ts +0 -479
  543. package/tests/unit/domains/requirements-validation/requirements-validator.test.ts +0 -452
  544. package/tests/unit/domains/requirements-validation/testability-scorer.test.ts +0 -505
  545. package/tests/unit/domains/security-compliance/compliance-validator.test.ts +0 -500
  546. package/tests/unit/domains/security-compliance/security-auditor.test.ts +0 -498
  547. package/tests/unit/domains/security-compliance/security-scanner.test.ts +0 -412
  548. package/tests/unit/domains/visual-accessibility/accessibility-tester.test.ts +0 -432
  549. package/tests/unit/domains/visual-accessibility/responsive-tester.test.ts +0 -506
  550. package/tests/unit/domains/visual-accessibility/visual-tester.test.ts +0 -412
  551. package/tests/unit/early-exit/early-exit-controller.test.ts +0 -548
  552. package/tests/unit/early-exit/early-exit-decision.test.ts +0 -617
  553. package/tests/unit/early-exit/index.test.ts +0 -254
  554. package/tests/unit/early-exit/quality-signal.test.ts +0 -589
  555. package/tests/unit/early-exit/speculative-executor.test.ts +0 -453
  556. package/tests/unit/feedback/coverage-learner.test.ts +0 -288
  557. package/tests/unit/feedback/feedback-loop.test.ts +0 -458
  558. package/tests/unit/feedback/pattern-promotion.test.ts +0 -390
  559. package/tests/unit/feedback/quality-score-calculator.test.ts +0 -364
  560. package/tests/unit/feedback/test-outcome-tracker.test.ts +0 -243
  561. package/tests/unit/init/init-wizard.test.ts +0 -881
  562. package/tests/unit/init/project-analyzer.test.ts +0 -807
  563. package/tests/unit/init/self-configurator.test.ts +0 -493
  564. package/tests/unit/integrations/ruvector/ast-complexity.test.ts +0 -240
  565. package/tests/unit/integrations/ruvector/coverage-router.test.ts +0 -366
  566. package/tests/unit/integrations/ruvector/diff-risk-classifier.test.ts +0 -340
  567. package/tests/unit/integrations/ruvector/graph-boundaries.test.ts +0 -355
  568. package/tests/unit/integrations/ruvector/q-learning-router.test.ts +0 -314
  569. package/tests/unit/kernel/agent-coordinator.test.ts +0 -220
  570. package/tests/unit/kernel/event-bus.test.ts +0 -197
  571. package/tests/unit/learning/qe-reasoning-bank.test.ts +0 -666
  572. package/tests/unit/learning/real-qe-reasoning-bank.benchmark.test.ts +0 -415
  573. package/tests/unit/mcp/mcp-server.test.ts +0 -544
  574. package/tests/unit/mcp/metrics/metrics-collector.test.ts +0 -340
  575. package/tests/unit/mcp/security/cve-prevention.test.ts +0 -512
  576. package/tests/unit/mcp/security/oauth21-provider.test.ts +0 -624
  577. package/tests/unit/mcp/security/rate-limiter.test.ts +0 -410
  578. package/tests/unit/mcp/security/sampling-server.test.ts +0 -420
  579. package/tests/unit/mcp/security/schema-validator.test.ts +0 -494
  580. package/tests/unit/mcp/tools/base.test.ts +0 -336
  581. package/tests/unit/mcp/tools/domain-tools.test.ts +0 -759
  582. package/tests/unit/mcp/tools/registry.test.ts +0 -240
  583. package/tests/unit/neural-optimizer/replay-buffer.test.ts +0 -403
  584. package/tests/unit/neural-optimizer/swarm-topology.test.ts +0 -473
  585. package/tests/unit/neural-optimizer/topology-optimizer.test.ts +0 -595
  586. package/tests/unit/neural-optimizer/value-network.test.ts +0 -343
  587. package/tests/unit/optimization/auto-tuner.test.ts +0 -506
  588. package/tests/unit/optimization/metric-collectors.test.ts +0 -352
  589. package/tests/unit/optimization/qe-workers.test.ts +0 -407
  590. package/tests/unit/optimization/tuning-algorithm.test.ts +0 -467
  591. package/tests/unit/routing/qe-agent-registry.test.ts +0 -229
  592. package/tests/unit/routing/qe-task-router.test.ts +0 -390
  593. package/tests/unit/routing/routing-feedback.test.ts +0 -339
  594. package/tests/unit/shared/embeddings/nomic-embedder.test.ts +0 -419
  595. package/tests/unit/shared/http/http-client.test.ts +0 -719
  596. package/tests/unit/shared/io/file-reader.test.ts +0 -511
  597. package/tests/unit/shared/llm/cache.test.ts +0 -391
  598. package/tests/unit/shared/llm/circuit-breaker.test.ts +0 -293
  599. package/tests/unit/shared/llm/cost-tracker.test.ts +0 -431
  600. package/tests/unit/shared/llm/provider-manager.test.ts +0 -550
  601. package/tests/unit/shared/llm/providers.test.ts +0 -532
  602. package/tests/unit/shared/parsers/typescript-parser.test.ts +0 -693
  603. package/tests/unit/shared/value-objects.test.ts +0 -184
  604. package/tests/unit/strange-loop/strange-loop.test.ts +0 -1170
  605. package/tests/unit/workers/base-worker.test.ts +0 -341
  606. package/tests/unit/workers/daemon.test.ts +0 -291
  607. package/tests/unit/workers/worker-manager.test.ts +0 -284
  608. package/tsconfig.json +0 -32
  609. 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
- }