@substrate-ai/factory 0.19.54 → 0.20.2

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 (491) hide show
  1. package/README.md +39 -0
  2. package/dist/config.d.ts +4 -4
  3. package/package.json +12 -3
  4. package/dist/__tests__/config.test.d.ts +0 -11
  5. package/dist/__tests__/config.test.d.ts.map +0 -1
  6. package/dist/__tests__/config.test.js +0 -215
  7. package/dist/__tests__/config.test.js.map +0 -1
  8. package/dist/__tests__/factory-run-command.test.d.ts +0 -12
  9. package/dist/__tests__/factory-run-command.test.d.ts.map +0 -1
  10. package/dist/__tests__/factory-run-command.test.js +0 -454
  11. package/dist/__tests__/factory-run-command.test.js.map +0 -1
  12. package/dist/__tests__/factory-validate-command.test.d.ts +0 -15
  13. package/dist/__tests__/factory-validate-command.test.d.ts.map +0 -1
  14. package/dist/__tests__/factory-validate-command.test.js +0 -339
  15. package/dist/__tests__/factory-validate-command.test.js.map +0 -1
  16. package/dist/__tests__/fixtures/advanced-cross-project-validation.dot.d.ts +0 -72
  17. package/dist/__tests__/fixtures/advanced-cross-project-validation.dot.d.ts.map +0 -1
  18. package/dist/__tests__/fixtures/advanced-cross-project-validation.dot.js +0 -121
  19. package/dist/__tests__/fixtures/advanced-cross-project-validation.dot.js.map +0 -1
  20. package/dist/__tests__/fixtures/llm-edge-routing.dot.d.ts +0 -28
  21. package/dist/__tests__/fixtures/llm-edge-routing.dot.d.ts.map +0 -1
  22. package/dist/__tests__/fixtures/llm-edge-routing.dot.js +0 -55
  23. package/dist/__tests__/fixtures/llm-edge-routing.dot.js.map +0 -1
  24. package/dist/__tests__/fixtures/manager-loop.dot.d.ts +0 -34
  25. package/dist/__tests__/fixtures/manager-loop.dot.d.ts.map +0 -1
  26. package/dist/__tests__/fixtures/manager-loop.dot.js +0 -61
  27. package/dist/__tests__/fixtures/manager-loop.dot.js.map +0 -1
  28. package/dist/__tests__/fixtures/parallel-fan-out-fan-in.dot.d.ts +0 -42
  29. package/dist/__tests__/fixtures/parallel-fan-out-fan-in.dot.d.ts.map +0 -1
  30. package/dist/__tests__/fixtures/parallel-fan-out-fan-in.dot.js +0 -118
  31. package/dist/__tests__/fixtures/parallel-fan-out-fan-in.dot.js.map +0 -1
  32. package/dist/__tests__/fixtures/subgraph-parent.dot.d.ts +0 -35
  33. package/dist/__tests__/fixtures/subgraph-parent.dot.d.ts.map +0 -1
  34. package/dist/__tests__/fixtures/subgraph-parent.dot.js +0 -69
  35. package/dist/__tests__/fixtures/subgraph-parent.dot.js.map +0 -1
  36. package/dist/__tests__/integration/advanced-graph-events.test.d.ts +0 -19
  37. package/dist/__tests__/integration/advanced-graph-events.test.d.ts.map +0 -1
  38. package/dist/__tests__/integration/advanced-graph-events.test.js +0 -288
  39. package/dist/__tests__/integration/advanced-graph-events.test.js.map +0 -1
  40. package/dist/__tests__/integration/checkpoint-resume.test.d.ts +0 -10
  41. package/dist/__tests__/integration/checkpoint-resume.test.d.ts.map +0 -1
  42. package/dist/__tests__/integration/checkpoint-resume.test.js +0 -125
  43. package/dist/__tests__/integration/checkpoint-resume.test.js.map +0 -1
  44. package/dist/__tests__/integration/conditional-pipeline.test.d.ts +0 -10
  45. package/dist/__tests__/integration/conditional-pipeline.test.d.ts.map +0 -1
  46. package/dist/__tests__/integration/conditional-pipeline.test.js +0 -106
  47. package/dist/__tests__/integration/conditional-pipeline.test.js.map +0 -1
  48. package/dist/__tests__/integration/convergence-validation.test.d.ts +0 -14
  49. package/dist/__tests__/integration/convergence-validation.test.d.ts.map +0 -1
  50. package/dist/__tests__/integration/convergence-validation.test.js +0 -449
  51. package/dist/__tests__/integration/convergence-validation.test.js.map +0 -1
  52. package/dist/__tests__/integration/epic44-coverage-gate.test.d.ts +0 -12
  53. package/dist/__tests__/integration/epic44-coverage-gate.test.d.ts.map +0 -1
  54. package/dist/__tests__/integration/epic44-coverage-gate.test.js +0 -58
  55. package/dist/__tests__/integration/epic44-coverage-gate.test.js.map +0 -1
  56. package/dist/__tests__/integration/epic45-coverage-gate.test.d.ts +0 -11
  57. package/dist/__tests__/integration/epic45-coverage-gate.test.d.ts.map +0 -1
  58. package/dist/__tests__/integration/epic45-coverage-gate.test.js +0 -64
  59. package/dist/__tests__/integration/epic45-coverage-gate.test.js.map +0 -1
  60. package/dist/__tests__/integration/epic46-scenario-primary-executor.test.d.ts +0 -2
  61. package/dist/__tests__/integration/epic46-scenario-primary-executor.test.d.ts.map +0 -1
  62. package/dist/__tests__/integration/epic46-scenario-primary-executor.test.js +0 -285
  63. package/dist/__tests__/integration/epic46-scenario-primary-executor.test.js.map +0 -1
  64. package/dist/__tests__/integration/events.test.d.ts +0 -8
  65. package/dist/__tests__/integration/events.test.d.ts.map +0 -1
  66. package/dist/__tests__/integration/events.test.js +0 -194
  67. package/dist/__tests__/integration/events.test.js.map +0 -1
  68. package/dist/__tests__/integration/graphs.d.ts +0 -59
  69. package/dist/__tests__/integration/graphs.d.ts.map +0 -1
  70. package/dist/__tests__/integration/graphs.js +0 -164
  71. package/dist/__tests__/integration/graphs.js.map +0 -1
  72. package/dist/__tests__/integration/helpers.d.ts +0 -127
  73. package/dist/__tests__/integration/helpers.d.ts.map +0 -1
  74. package/dist/__tests__/integration/helpers.js +0 -167
  75. package/dist/__tests__/integration/helpers.js.map +0 -1
  76. package/dist/__tests__/integration/integrity.test.d.ts +0 -8
  77. package/dist/__tests__/integration/integrity.test.d.ts.map +0 -1
  78. package/dist/__tests__/integration/integrity.test.js +0 -198
  79. package/dist/__tests__/integration/integrity.test.js.map +0 -1
  80. package/dist/__tests__/integration/llm-edge-routing.test.d.ts +0 -21
  81. package/dist/__tests__/integration/llm-edge-routing.test.d.ts.map +0 -1
  82. package/dist/__tests__/integration/llm-edge-routing.test.js +0 -341
  83. package/dist/__tests__/integration/llm-edge-routing.test.js.map +0 -1
  84. package/dist/__tests__/integration/manager-loop.test.d.ts +0 -24
  85. package/dist/__tests__/integration/manager-loop.test.d.ts.map +0 -1
  86. package/dist/__tests__/integration/manager-loop.test.js +0 -276
  87. package/dist/__tests__/integration/manager-loop.test.js.map +0 -1
  88. package/dist/__tests__/integration/multi-type-graph.test.d.ts +0 -10
  89. package/dist/__tests__/integration/multi-type-graph.test.d.ts.map +0 -1
  90. package/dist/__tests__/integration/multi-type-graph.test.js +0 -100
  91. package/dist/__tests__/integration/multi-type-graph.test.js.map +0 -1
  92. package/dist/__tests__/integration/parallel-fan-out-fan-in.test.d.ts +0 -22
  93. package/dist/__tests__/integration/parallel-fan-out-fan-in.test.d.ts.map +0 -1
  94. package/dist/__tests__/integration/parallel-fan-out-fan-in.test.js +0 -515
  95. package/dist/__tests__/integration/parallel-fan-out-fan-in.test.js.map +0 -1
  96. package/dist/__tests__/integration/persistence.test.d.ts +0 -8
  97. package/dist/__tests__/integration/persistence.test.d.ts.map +0 -1
  98. package/dist/__tests__/integration/persistence.test.js +0 -129
  99. package/dist/__tests__/integration/persistence.test.js.map +0 -1
  100. package/dist/__tests__/integration/pipeline-templates-integration.test.d.ts +0 -16
  101. package/dist/__tests__/integration/pipeline-templates-integration.test.d.ts.map +0 -1
  102. package/dist/__tests__/integration/pipeline-templates-integration.test.js +0 -171
  103. package/dist/__tests__/integration/pipeline-templates-integration.test.js.map +0 -1
  104. package/dist/__tests__/integration/scenario-pipeline.test.d.ts +0 -11
  105. package/dist/__tests__/integration/scenario-pipeline.test.d.ts.map +0 -1
  106. package/dist/__tests__/integration/scenario-pipeline.test.js +0 -243
  107. package/dist/__tests__/integration/scenario-pipeline.test.js.map +0 -1
  108. package/dist/__tests__/integration/stylesheet-application.test.d.ts +0 -12
  109. package/dist/__tests__/integration/stylesheet-application.test.d.ts.map +0 -1
  110. package/dist/__tests__/integration/stylesheet-application.test.js +0 -119
  111. package/dist/__tests__/integration/stylesheet-application.test.js.map +0 -1
  112. package/dist/__tests__/integration/subgraph-execution.test.d.ts +0 -24
  113. package/dist/__tests__/integration/subgraph-execution.test.d.ts.map +0 -1
  114. package/dist/__tests__/integration/subgraph-execution.test.js +0 -291
  115. package/dist/__tests__/integration/subgraph-execution.test.js.map +0 -1
  116. package/dist/__tests__/integration/validation-errors.test.d.ts +0 -8
  117. package/dist/__tests__/integration/validation-errors.test.d.ts.map +0 -1
  118. package/dist/__tests__/integration/validation-errors.test.js +0 -150
  119. package/dist/__tests__/integration/validation-errors.test.js.map +0 -1
  120. package/dist/agent/__tests__/loop-detection.test.d.ts +0 -2
  121. package/dist/agent/__tests__/loop-detection.test.d.ts.map +0 -1
  122. package/dist/agent/__tests__/loop-detection.test.js +0 -236
  123. package/dist/agent/__tests__/loop-detection.test.js.map +0 -1
  124. package/dist/agent/__tests__/loop.test.d.ts +0 -2
  125. package/dist/agent/__tests__/loop.test.d.ts.map +0 -1
  126. package/dist/agent/__tests__/loop.test.js +0 -868
  127. package/dist/agent/__tests__/loop.test.js.map +0 -1
  128. package/dist/agent/__tests__/truncation.test.d.ts +0 -2
  129. package/dist/agent/__tests__/truncation.test.d.ts.map +0 -1
  130. package/dist/agent/__tests__/truncation.test.js +0 -276
  131. package/dist/agent/__tests__/truncation.test.js.map +0 -1
  132. package/dist/agent/tools/__tests__/anthropic-tools.test.d.ts +0 -6
  133. package/dist/agent/tools/__tests__/anthropic-tools.test.d.ts.map +0 -1
  134. package/dist/agent/tools/__tests__/anthropic-tools.test.js +0 -49
  135. package/dist/agent/tools/__tests__/anthropic-tools.test.js.map +0 -1
  136. package/dist/agent/tools/__tests__/environment.test.d.ts +0 -6
  137. package/dist/agent/tools/__tests__/environment.test.d.ts.map +0 -1
  138. package/dist/agent/tools/__tests__/environment.test.js +0 -33
  139. package/dist/agent/tools/__tests__/environment.test.js.map +0 -1
  140. package/dist/agent/tools/__tests__/gemini-tools.test.d.ts +0 -6
  141. package/dist/agent/tools/__tests__/gemini-tools.test.d.ts.map +0 -1
  142. package/dist/agent/tools/__tests__/gemini-tools.test.js +0 -98
  143. package/dist/agent/tools/__tests__/gemini-tools.test.js.map +0 -1
  144. package/dist/agent/tools/__tests__/openai-tools.test.d.ts +0 -6
  145. package/dist/agent/tools/__tests__/openai-tools.test.d.ts.map +0 -1
  146. package/dist/agent/tools/__tests__/openai-tools.test.js +0 -53
  147. package/dist/agent/tools/__tests__/openai-tools.test.js.map +0 -1
  148. package/dist/agent/tools/__tests__/patch.test.d.ts +0 -6
  149. package/dist/agent/tools/__tests__/patch.test.d.ts.map +0 -1
  150. package/dist/agent/tools/__tests__/patch.test.js +0 -116
  151. package/dist/agent/tools/__tests__/patch.test.js.map +0 -1
  152. package/dist/agent/tools/__tests__/profiles.test.d.ts +0 -6
  153. package/dist/agent/tools/__tests__/profiles.test.d.ts.map +0 -1
  154. package/dist/agent/tools/__tests__/profiles.test.js +0 -125
  155. package/dist/agent/tools/__tests__/profiles.test.js.map +0 -1
  156. package/dist/agent/tools/__tests__/registry.test.d.ts +0 -6
  157. package/dist/agent/tools/__tests__/registry.test.d.ts.map +0 -1
  158. package/dist/agent/tools/__tests__/registry.test.js +0 -94
  159. package/dist/agent/tools/__tests__/registry.test.js.map +0 -1
  160. package/dist/agent/tools/__tests__/shared.test.d.ts +0 -6
  161. package/dist/agent/tools/__tests__/shared.test.d.ts.map +0 -1
  162. package/dist/agent/tools/__tests__/shared.test.js +0 -131
  163. package/dist/agent/tools/__tests__/shared.test.js.map +0 -1
  164. package/dist/backend/__tests__/direct-backend.test.d.ts +0 -14
  165. package/dist/backend/__tests__/direct-backend.test.d.ts.map +0 -1
  166. package/dist/backend/__tests__/direct-backend.test.js +0 -393
  167. package/dist/backend/__tests__/direct-backend.test.js.map +0 -1
  168. package/dist/backend/__tests__/direct-bootstrap.test.d.ts +0 -7
  169. package/dist/backend/__tests__/direct-bootstrap.test.d.ts.map +0 -1
  170. package/dist/backend/__tests__/direct-bootstrap.test.js +0 -177
  171. package/dist/backend/__tests__/direct-bootstrap.test.js.map +0 -1
  172. package/dist/backend/__tests__/mock-backend.test.d.ts +0 -7
  173. package/dist/backend/__tests__/mock-backend.test.d.ts.map +0 -1
  174. package/dist/backend/__tests__/mock-backend.test.js +0 -273
  175. package/dist/backend/__tests__/mock-backend.test.js.map +0 -1
  176. package/dist/backend/__tests__/parity.test.d.ts +0 -17
  177. package/dist/backend/__tests__/parity.test.d.ts.map +0 -1
  178. package/dist/backend/__tests__/parity.test.js +0 -411
  179. package/dist/backend/__tests__/parity.test.js.map +0 -1
  180. package/dist/context/__tests__/auto-summarizer.test.d.ts +0 -14
  181. package/dist/context/__tests__/auto-summarizer.test.d.ts.map +0 -1
  182. package/dist/context/__tests__/auto-summarizer.test.js +0 -189
  183. package/dist/context/__tests__/auto-summarizer.test.js.map +0 -1
  184. package/dist/context/__tests__/context-cli-command.test.d.ts +0 -7
  185. package/dist/context/__tests__/context-cli-command.test.d.ts.map +0 -1
  186. package/dist/context/__tests__/context-cli-command.test.js +0 -331
  187. package/dist/context/__tests__/context-cli-command.test.js.map +0 -1
  188. package/dist/context/__tests__/pyramid-summary-integration.test.d.ts +0 -2
  189. package/dist/context/__tests__/pyramid-summary-integration.test.d.ts.map +0 -1
  190. package/dist/context/__tests__/pyramid-summary-integration.test.js +0 -533
  191. package/dist/context/__tests__/pyramid-summary-integration.test.js.map +0 -1
  192. package/dist/context/__tests__/summarizer.test.d.ts +0 -2
  193. package/dist/context/__tests__/summarizer.test.d.ts.map +0 -1
  194. package/dist/context/__tests__/summarizer.test.js +0 -189
  195. package/dist/context/__tests__/summarizer.test.js.map +0 -1
  196. package/dist/context/__tests__/summary-cache.test.d.ts +0 -2
  197. package/dist/context/__tests__/summary-cache.test.d.ts.map +0 -1
  198. package/dist/context/__tests__/summary-cache.test.js +0 -214
  199. package/dist/context/__tests__/summary-cache.test.js.map +0 -1
  200. package/dist/context/__tests__/summary-metrics.test.d.ts +0 -2
  201. package/dist/context/__tests__/summary-metrics.test.d.ts.map +0 -1
  202. package/dist/context/__tests__/summary-metrics.test.js +0 -172
  203. package/dist/context/__tests__/summary-metrics.test.js.map +0 -1
  204. package/dist/context/__tests__/summary-types.test.d.ts +0 -2
  205. package/dist/context/__tests__/summary-types.test.d.ts.map +0 -1
  206. package/dist/context/__tests__/summary-types.test.js +0 -130
  207. package/dist/context/__tests__/summary-types.test.js.map +0 -1
  208. package/dist/convergence/__tests__/budget.test.d.ts +0 -6
  209. package/dist/convergence/__tests__/budget.test.d.ts.map +0 -1
  210. package/dist/convergence/__tests__/budget.test.js +0 -187
  211. package/dist/convergence/__tests__/budget.test.js.map +0 -1
  212. package/dist/convergence/__tests__/controller.test.d.ts +0 -9
  213. package/dist/convergence/__tests__/controller.test.d.ts.map +0 -1
  214. package/dist/convergence/__tests__/controller.test.js +0 -585
  215. package/dist/convergence/__tests__/controller.test.js.map +0 -1
  216. package/dist/convergence/__tests__/dual-signal.test.d.ts +0 -14
  217. package/dist/convergence/__tests__/dual-signal.test.d.ts.map +0 -1
  218. package/dist/convergence/__tests__/dual-signal.test.js +0 -123
  219. package/dist/convergence/__tests__/dual-signal.test.js.map +0 -1
  220. package/dist/convergence/__tests__/epic46-integration.test.d.ts +0 -15
  221. package/dist/convergence/__tests__/epic46-integration.test.d.ts.map +0 -1
  222. package/dist/convergence/__tests__/epic46-integration.test.js +0 -522
  223. package/dist/convergence/__tests__/epic46-integration.test.js.map +0 -1
  224. package/dist/convergence/__tests__/plateau.test.d.ts +0 -6
  225. package/dist/convergence/__tests__/plateau.test.d.ts.map +0 -1
  226. package/dist/convergence/__tests__/plateau.test.js +0 -163
  227. package/dist/convergence/__tests__/plateau.test.js.map +0 -1
  228. package/dist/convergence/__tests__/remediation.test.d.ts +0 -11
  229. package/dist/convergence/__tests__/remediation.test.d.ts.map +0 -1
  230. package/dist/convergence/__tests__/remediation.test.js +0 -209
  231. package/dist/convergence/__tests__/remediation.test.js.map +0 -1
  232. package/dist/convergence/__tests__/scenario-primary.test.d.ts +0 -13
  233. package/dist/convergence/__tests__/scenario-primary.test.d.ts.map +0 -1
  234. package/dist/convergence/__tests__/scenario-primary.test.js +0 -183
  235. package/dist/convergence/__tests__/scenario-primary.test.js.map +0 -1
  236. package/dist/factory-command.test.d.ts +0 -8
  237. package/dist/factory-command.test.d.ts.map +0 -1
  238. package/dist/factory-command.test.js +0 -304
  239. package/dist/factory-command.test.js.map +0 -1
  240. package/dist/graph/__tests__/attractor-compliance.test.d.ts +0 -10
  241. package/dist/graph/__tests__/attractor-compliance.test.d.ts.map +0 -1
  242. package/dist/graph/__tests__/attractor-compliance.test.js +0 -766
  243. package/dist/graph/__tests__/attractor-compliance.test.js.map +0 -1
  244. package/dist/graph/__tests__/checkpoint.test.d.ts +0 -8
  245. package/dist/graph/__tests__/checkpoint.test.d.ts.map +0 -1
  246. package/dist/graph/__tests__/checkpoint.test.js +0 -329
  247. package/dist/graph/__tests__/checkpoint.test.js.map +0 -1
  248. package/dist/graph/__tests__/condition-parser.test.d.ts +0 -14
  249. package/dist/graph/__tests__/condition-parser.test.d.ts.map +0 -1
  250. package/dist/graph/__tests__/condition-parser.test.js +0 -406
  251. package/dist/graph/__tests__/condition-parser.test.js.map +0 -1
  252. package/dist/graph/__tests__/context.test.d.ts +0 -14
  253. package/dist/graph/__tests__/context.test.d.ts.map +0 -1
  254. package/dist/graph/__tests__/context.test.js +0 -276
  255. package/dist/graph/__tests__/context.test.js.map +0 -1
  256. package/dist/graph/__tests__/edge-selector-events.test.d.ts +0 -11
  257. package/dist/graph/__tests__/edge-selector-events.test.d.ts.map +0 -1
  258. package/dist/graph/__tests__/edge-selector-events.test.js +0 -184
  259. package/dist/graph/__tests__/edge-selector-events.test.js.map +0 -1
  260. package/dist/graph/__tests__/edge-selector.test.d.ts +0 -6
  261. package/dist/graph/__tests__/edge-selector.test.d.ts.map +0 -1
  262. package/dist/graph/__tests__/edge-selector.test.js +0 -452
  263. package/dist/graph/__tests__/edge-selector.test.js.map +0 -1
  264. package/dist/graph/__tests__/executor-convergence.test.d.ts +0 -12
  265. package/dist/graph/__tests__/executor-convergence.test.d.ts.map +0 -1
  266. package/dist/graph/__tests__/executor-convergence.test.js +0 -432
  267. package/dist/graph/__tests__/executor-convergence.test.js.map +0 -1
  268. package/dist/graph/__tests__/executor-fidelity.test.d.ts +0 -13
  269. package/dist/graph/__tests__/executor-fidelity.test.d.ts.map +0 -1
  270. package/dist/graph/__tests__/executor-fidelity.test.js +0 -335
  271. package/dist/graph/__tests__/executor-fidelity.test.js.map +0 -1
  272. package/dist/graph/__tests__/executor.test.d.ts +0 -14
  273. package/dist/graph/__tests__/executor.test.d.ts.map +0 -1
  274. package/dist/graph/__tests__/executor.test.js +0 -901
  275. package/dist/graph/__tests__/executor.test.js.map +0 -1
  276. package/dist/graph/__tests__/fidelity.test.d.ts +0 -8
  277. package/dist/graph/__tests__/fidelity.test.d.ts.map +0 -1
  278. package/dist/graph/__tests__/fidelity.test.js +0 -135
  279. package/dist/graph/__tests__/fidelity.test.js.map +0 -1
  280. package/dist/graph/__tests__/llm-evaluator.test.d.ts +0 -7
  281. package/dist/graph/__tests__/llm-evaluator.test.d.ts.map +0 -1
  282. package/dist/graph/__tests__/llm-evaluator.test.js +0 -106
  283. package/dist/graph/__tests__/llm-evaluator.test.js.map +0 -1
  284. package/dist/graph/__tests__/parser-chaining.test.d.ts +0 -13
  285. package/dist/graph/__tests__/parser-chaining.test.d.ts.map +0 -1
  286. package/dist/graph/__tests__/parser-chaining.test.js +0 -215
  287. package/dist/graph/__tests__/parser-chaining.test.js.map +0 -1
  288. package/dist/graph/__tests__/parser.test.d.ts +0 -22
  289. package/dist/graph/__tests__/parser.test.d.ts.map +0 -1
  290. package/dist/graph/__tests__/parser.test.js +0 -452
  291. package/dist/graph/__tests__/parser.test.js.map +0 -1
  292. package/dist/graph/__tests__/run-state.test.d.ts +0 -13
  293. package/dist/graph/__tests__/run-state.test.d.ts.map +0 -1
  294. package/dist/graph/__tests__/run-state.test.js +0 -189
  295. package/dist/graph/__tests__/run-state.test.js.map +0 -1
  296. package/dist/graph/__tests__/transformer.test.d.ts +0 -16
  297. package/dist/graph/__tests__/transformer.test.d.ts.map +0 -1
  298. package/dist/graph/__tests__/transformer.test.js +0 -350
  299. package/dist/graph/__tests__/transformer.test.js.map +0 -1
  300. package/dist/graph/__tests__/validator-errors.test.d.ts +0 -15
  301. package/dist/graph/__tests__/validator-errors.test.d.ts.map +0 -1
  302. package/dist/graph/__tests__/validator-errors.test.js +0 -572
  303. package/dist/graph/__tests__/validator-errors.test.js.map +0 -1
  304. package/dist/graph/__tests__/validator-warnings.test.d.ts +0 -15
  305. package/dist/graph/__tests__/validator-warnings.test.d.ts.map +0 -1
  306. package/dist/graph/__tests__/validator-warnings.test.js +0 -363
  307. package/dist/graph/__tests__/validator-warnings.test.js.map +0 -1
  308. package/dist/handlers/__tests__/codergen-handler.test.d.ts +0 -14
  309. package/dist/handlers/__tests__/codergen-handler.test.d.ts.map +0 -1
  310. package/dist/handlers/__tests__/codergen-handler.test.js +0 -442
  311. package/dist/handlers/__tests__/codergen-handler.test.js.map +0 -1
  312. package/dist/handlers/__tests__/fan-in.test.d.ts +0 -14
  313. package/dist/handlers/__tests__/fan-in.test.d.ts.map +0 -1
  314. package/dist/handlers/__tests__/fan-in.test.js +0 -399
  315. package/dist/handlers/__tests__/fan-in.test.js.map +0 -1
  316. package/dist/handlers/__tests__/join-policy.test.d.ts +0 -9
  317. package/dist/handlers/__tests__/join-policy.test.d.ts.map +0 -1
  318. package/dist/handlers/__tests__/join-policy.test.js +0 -201
  319. package/dist/handlers/__tests__/join-policy.test.js.map +0 -1
  320. package/dist/handlers/__tests__/manager-loop.test.d.ts +0 -14
  321. package/dist/handlers/__tests__/manager-loop.test.d.ts.map +0 -1
  322. package/dist/handlers/__tests__/manager-loop.test.js +0 -322
  323. package/dist/handlers/__tests__/manager-loop.test.js.map +0 -1
  324. package/dist/handlers/__tests__/parallel-events.test.d.ts +0 -12
  325. package/dist/handlers/__tests__/parallel-events.test.d.ts.map +0 -1
  326. package/dist/handlers/__tests__/parallel-events.test.js +0 -252
  327. package/dist/handlers/__tests__/parallel-events.test.js.map +0 -1
  328. package/dist/handlers/__tests__/parallel-handler.test.d.ts +0 -14
  329. package/dist/handlers/__tests__/parallel-handler.test.d.ts.map +0 -1
  330. package/dist/handlers/__tests__/parallel-handler.test.js +0 -337
  331. package/dist/handlers/__tests__/parallel-handler.test.js.map +0 -1
  332. package/dist/handlers/__tests__/parallel-join.test.d.ts +0 -9
  333. package/dist/handlers/__tests__/parallel-join.test.d.ts.map +0 -1
  334. package/dist/handlers/__tests__/parallel-join.test.js +0 -267
  335. package/dist/handlers/__tests__/parallel-join.test.js.map +0 -1
  336. package/dist/handlers/__tests__/registry.test.d.ts +0 -14
  337. package/dist/handlers/__tests__/registry.test.d.ts.map +0 -1
  338. package/dist/handlers/__tests__/registry.test.js +0 -315
  339. package/dist/handlers/__tests__/registry.test.js.map +0 -1
  340. package/dist/handlers/__tests__/subgraph-events.test.d.ts +0 -10
  341. package/dist/handlers/__tests__/subgraph-events.test.d.ts.map +0 -1
  342. package/dist/handlers/__tests__/subgraph-events.test.js +0 -189
  343. package/dist/handlers/__tests__/subgraph-events.test.js.map +0 -1
  344. package/dist/handlers/__tests__/subgraph-inheritance.test.d.ts +0 -14
  345. package/dist/handlers/__tests__/subgraph-inheritance.test.d.ts.map +0 -1
  346. package/dist/handlers/__tests__/subgraph-inheritance.test.js +0 -267
  347. package/dist/handlers/__tests__/subgraph-inheritance.test.js.map +0 -1
  348. package/dist/handlers/__tests__/subgraph.test.d.ts +0 -14
  349. package/dist/handlers/__tests__/subgraph.test.d.ts.map +0 -1
  350. package/dist/handlers/__tests__/subgraph.test.js +0 -369
  351. package/dist/handlers/__tests__/subgraph.test.js.map +0 -1
  352. package/dist/handlers/__tests__/tool-handler.test.d.ts +0 -11
  353. package/dist/handlers/__tests__/tool-handler.test.d.ts.map +0 -1
  354. package/dist/handlers/__tests__/tool-handler.test.js +0 -184
  355. package/dist/handlers/__tests__/tool-handler.test.js.map +0 -1
  356. package/dist/handlers/__tests__/tool-scenario.test.d.ts +0 -12
  357. package/dist/handlers/__tests__/tool-scenario.test.d.ts.map +0 -1
  358. package/dist/handlers/__tests__/tool-scenario.test.js +0 -222
  359. package/dist/handlers/__tests__/tool-scenario.test.js.map +0 -1
  360. package/dist/handlers/__tests__/wait-human-handler.test.d.ts +0 -11
  361. package/dist/handlers/__tests__/wait-human-handler.test.d.ts.map +0 -1
  362. package/dist/handlers/__tests__/wait-human-handler.test.js +0 -251
  363. package/dist/handlers/__tests__/wait-human-handler.test.js.map +0 -1
  364. package/dist/llm/__tests__/client.test.d.ts +0 -2
  365. package/dist/llm/__tests__/client.test.d.ts.map +0 -1
  366. package/dist/llm/__tests__/client.test.js +0 -198
  367. package/dist/llm/__tests__/client.test.js.map +0 -1
  368. package/dist/llm/__tests__/types.test.d.ts +0 -2
  369. package/dist/llm/__tests__/types.test.d.ts.map +0 -1
  370. package/dist/llm/__tests__/types.test.js +0 -289
  371. package/dist/llm/__tests__/types.test.js.map +0 -1
  372. package/dist/llm/middleware/__tests__/cost-tracking.test.d.ts +0 -2
  373. package/dist/llm/middleware/__tests__/cost-tracking.test.d.ts.map +0 -1
  374. package/dist/llm/middleware/__tests__/cost-tracking.test.js +0 -73
  375. package/dist/llm/middleware/__tests__/cost-tracking.test.js.map +0 -1
  376. package/dist/llm/middleware/__tests__/logging.test.d.ts +0 -2
  377. package/dist/llm/middleware/__tests__/logging.test.d.ts.map +0 -1
  378. package/dist/llm/middleware/__tests__/logging.test.js +0 -127
  379. package/dist/llm/middleware/__tests__/logging.test.js.map +0 -1
  380. package/dist/llm/middleware/__tests__/retry.test.d.ts +0 -2
  381. package/dist/llm/middleware/__tests__/retry.test.d.ts.map +0 -1
  382. package/dist/llm/middleware/__tests__/retry.test.js +0 -126
  383. package/dist/llm/middleware/__tests__/retry.test.js.map +0 -1
  384. package/dist/llm/providers/__tests__/anthropic.test.d.ts +0 -2
  385. package/dist/llm/providers/__tests__/anthropic.test.d.ts.map +0 -1
  386. package/dist/llm/providers/__tests__/anthropic.test.js +0 -412
  387. package/dist/llm/providers/__tests__/anthropic.test.js.map +0 -1
  388. package/dist/llm/providers/__tests__/gemini.test.d.ts +0 -2
  389. package/dist/llm/providers/__tests__/gemini.test.d.ts.map +0 -1
  390. package/dist/llm/providers/__tests__/gemini.test.js +0 -591
  391. package/dist/llm/providers/__tests__/gemini.test.js.map +0 -1
  392. package/dist/llm/providers/__tests__/openai.test.d.ts +0 -2
  393. package/dist/llm/providers/__tests__/openai.test.d.ts.map +0 -1
  394. package/dist/llm/providers/__tests__/openai.test.js +0 -546
  395. package/dist/llm/providers/__tests__/openai.test.js.map +0 -1
  396. package/dist/persistence/__tests__/factory-queries.test.d.ts +0 -9
  397. package/dist/persistence/__tests__/factory-queries.test.d.ts.map +0 -1
  398. package/dist/persistence/__tests__/factory-queries.test.js +0 -372
  399. package/dist/persistence/__tests__/factory-queries.test.js.map +0 -1
  400. package/dist/persistence/__tests__/factory-schema.test.d.ts +0 -6
  401. package/dist/persistence/__tests__/factory-schema.test.d.ts.map +0 -1
  402. package/dist/persistence/__tests__/factory-schema.test.js +0 -105
  403. package/dist/persistence/__tests__/factory-schema.test.js.map +0 -1
  404. package/dist/scenarios/__tests__/cli-command-list.test.d.ts +0 -7
  405. package/dist/scenarios/__tests__/cli-command-list.test.d.ts.map +0 -1
  406. package/dist/scenarios/__tests__/cli-command-list.test.js +0 -237
  407. package/dist/scenarios/__tests__/cli-command-list.test.js.map +0 -1
  408. package/dist/scenarios/__tests__/cli-command.test.d.ts +0 -11
  409. package/dist/scenarios/__tests__/cli-command.test.d.ts.map +0 -1
  410. package/dist/scenarios/__tests__/cli-command.test.js +0 -275
  411. package/dist/scenarios/__tests__/cli-command.test.js.map +0 -1
  412. package/dist/scenarios/__tests__/integrity-pipeline.test.d.ts +0 -15
  413. package/dist/scenarios/__tests__/integrity-pipeline.test.d.ts.map +0 -1
  414. package/dist/scenarios/__tests__/integrity-pipeline.test.js +0 -318
  415. package/dist/scenarios/__tests__/integrity-pipeline.test.js.map +0 -1
  416. package/dist/scenarios/__tests__/runner-twins.test.d.ts +0 -13
  417. package/dist/scenarios/__tests__/runner-twins.test.d.ts.map +0 -1
  418. package/dist/scenarios/__tests__/runner-twins.test.js +0 -205
  419. package/dist/scenarios/__tests__/runner-twins.test.js.map +0 -1
  420. package/dist/scenarios/__tests__/scorer.test.d.ts +0 -11
  421. package/dist/scenarios/__tests__/scorer.test.d.ts.map +0 -1
  422. package/dist/scenarios/__tests__/scorer.test.js +0 -225
  423. package/dist/scenarios/__tests__/scorer.test.js.map +0 -1
  424. package/dist/scenarios/__tests__/scoring-integration.test.d.ts +0 -8
  425. package/dist/scenarios/__tests__/scoring-integration.test.d.ts.map +0 -1
  426. package/dist/scenarios/__tests__/scoring-integration.test.js +0 -178
  427. package/dist/scenarios/__tests__/scoring-integration.test.js.map +0 -1
  428. package/dist/scenarios/__tests__/store.test.d.ts +0 -5
  429. package/dist/scenarios/__tests__/store.test.d.ts.map +0 -1
  430. package/dist/scenarios/__tests__/store.test.js +0 -169
  431. package/dist/scenarios/__tests__/store.test.js.map +0 -1
  432. package/dist/stylesheet/__tests__/stylesheet.test.d.ts +0 -17
  433. package/dist/stylesheet/__tests__/stylesheet.test.d.ts.map +0 -1
  434. package/dist/stylesheet/__tests__/stylesheet.test.js +0 -368
  435. package/dist/stylesheet/__tests__/stylesheet.test.js.map +0 -1
  436. package/dist/templates/__tests__/templates.test.d.ts +0 -7
  437. package/dist/templates/__tests__/templates.test.d.ts.map +0 -1
  438. package/dist/templates/__tests__/templates.test.js +0 -92
  439. package/dist/templates/__tests__/templates.test.js.map +0 -1
  440. package/dist/twins/__tests__/docker-compose.test.d.ts +0 -13
  441. package/dist/twins/__tests__/docker-compose.test.d.ts.map +0 -1
  442. package/dist/twins/__tests__/docker-compose.test.js +0 -247
  443. package/dist/twins/__tests__/docker-compose.test.js.map +0 -1
  444. package/dist/twins/__tests__/health-monitor.test.d.ts +0 -19
  445. package/dist/twins/__tests__/health-monitor.test.d.ts.map +0 -1
  446. package/dist/twins/__tests__/health-monitor.test.js +0 -301
  447. package/dist/twins/__tests__/health-monitor.test.js.map +0 -1
  448. package/dist/twins/__tests__/integration/e2e.test.d.ts +0 -18
  449. package/dist/twins/__tests__/integration/e2e.test.d.ts.map +0 -1
  450. package/dist/twins/__tests__/integration/e2e.test.js +0 -146
  451. package/dist/twins/__tests__/integration/e2e.test.js.map +0 -1
  452. package/dist/twins/__tests__/integration/health-monitor-integration.test.d.ts +0 -16
  453. package/dist/twins/__tests__/integration/health-monitor-integration.test.d.ts.map +0 -1
  454. package/dist/twins/__tests__/integration/health-monitor-integration.test.js +0 -183
  455. package/dist/twins/__tests__/integration/health-monitor-integration.test.js.map +0 -1
  456. package/dist/twins/__tests__/integration/helpers.d.ts +0 -32
  457. package/dist/twins/__tests__/integration/helpers.d.ts.map +0 -1
  458. package/dist/twins/__tests__/integration/helpers.js +0 -67
  459. package/dist/twins/__tests__/integration/helpers.js.map +0 -1
  460. package/dist/twins/__tests__/integration/lifecycle.test.d.ts +0 -14
  461. package/dist/twins/__tests__/integration/lifecycle.test.d.ts.map +0 -1
  462. package/dist/twins/__tests__/integration/lifecycle.test.js +0 -127
  463. package/dist/twins/__tests__/integration/lifecycle.test.js.map +0 -1
  464. package/dist/twins/__tests__/integration/persistence-integration.test.d.ts +0 -14
  465. package/dist/twins/__tests__/integration/persistence-integration.test.d.ts.map +0 -1
  466. package/dist/twins/__tests__/integration/persistence-integration.test.js +0 -132
  467. package/dist/twins/__tests__/integration/persistence-integration.test.js.map +0 -1
  468. package/dist/twins/__tests__/persistence.test.d.ts +0 -10
  469. package/dist/twins/__tests__/persistence.test.d.ts.map +0 -1
  470. package/dist/twins/__tests__/persistence.test.js +0 -300
  471. package/dist/twins/__tests__/persistence.test.js.map +0 -1
  472. package/dist/twins/__tests__/registry.test.d.ts +0 -7
  473. package/dist/twins/__tests__/registry.test.d.ts.map +0 -1
  474. package/dist/twins/__tests__/registry.test.js +0 -282
  475. package/dist/twins/__tests__/registry.test.js.map +0 -1
  476. package/dist/twins/__tests__/run-state.test.d.ts +0 -9
  477. package/dist/twins/__tests__/run-state.test.d.ts.map +0 -1
  478. package/dist/twins/__tests__/run-state.test.js +0 -112
  479. package/dist/twins/__tests__/run-state.test.js.map +0 -1
  480. package/dist/twins/__tests__/templates-cli.test.d.ts +0 -10
  481. package/dist/twins/__tests__/templates-cli.test.d.ts.map +0 -1
  482. package/dist/twins/__tests__/templates-cli.test.js +0 -187
  483. package/dist/twins/__tests__/templates-cli.test.js.map +0 -1
  484. package/dist/twins/__tests__/templates.test.d.ts +0 -7
  485. package/dist/twins/__tests__/templates.test.d.ts.map +0 -1
  486. package/dist/twins/__tests__/templates.test.js +0 -87
  487. package/dist/twins/__tests__/templates.test.js.map +0 -1
  488. package/dist/twins/__tests__/twins-cli.test.d.ts +0 -11
  489. package/dist/twins/__tests__/twins-cli.test.d.ts.map +0 -1
  490. package/dist/twins/__tests__/twins-cli.test.js +0 -365
  491. package/dist/twins/__tests__/twins-cli.test.js.map +0 -1
@@ -1,282 +0,0 @@
1
- /**
2
- * Unit tests for TwinRegistry — discovery, validation, and health polling.
3
- *
4
- * Story 47-1.
5
- */
6
- import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
7
- import { tmpdir } from 'node:os';
8
- import { mkdir, writeFile, rm } from 'node:fs/promises';
9
- import { join } from 'node:path';
10
- import { randomUUID } from 'node:crypto';
11
- import { createTwinRegistry } from '../registry.js';
12
- import { TwinDefinitionError, TwinRegistryError } from '../types.js';
13
- // ---------------------------------------------------------------------------
14
- // Test helpers
15
- // ---------------------------------------------------------------------------
16
- let testDir;
17
- async function writeTwin(dir, filename, content) {
18
- await writeFile(join(dir, filename), content, 'utf-8');
19
- }
20
- // ---------------------------------------------------------------------------
21
- // Setup / teardown
22
- // ---------------------------------------------------------------------------
23
- beforeEach(async () => {
24
- testDir = join(tmpdir(), randomUUID());
25
- await mkdir(testDir, { recursive: true });
26
- });
27
- afterEach(async () => {
28
- await rm(testDir, { recursive: true, force: true });
29
- });
30
- // ---------------------------------------------------------------------------
31
- // AC1: YAML Discovery and Full-Field Parsing
32
- // ---------------------------------------------------------------------------
33
- describe('AC1: YAML discovery and full-field parsing', () => {
34
- it('parses a full-field YAML twin definition correctly', async () => {
35
- await writeTwin(testDir, 'stripe.yaml', `
36
- name: stripe
37
- image: stripe/stripe-mock:latest
38
- ports:
39
- - "12111:12111"
40
- environment:
41
- STRIPE_MOCK_PORT: "12111"
42
- healthcheck:
43
- url: "http://localhost:12111/v1/charges"
44
- interval_ms: 500
45
- timeout_ms: 10000
46
- `);
47
- const registry = createTwinRegistry();
48
- await registry.discover(testDir);
49
- const list = registry.list();
50
- expect(list).toHaveLength(1);
51
- const twin = list[0];
52
- expect(twin.name).toBe('stripe');
53
- expect(twin.image).toBe('stripe/stripe-mock:latest');
54
- expect(twin.ports).toEqual([{ host: 12111, container: 12111 }]);
55
- expect(twin.environment).toEqual({ STRIPE_MOCK_PORT: '12111' });
56
- expect(twin.healthcheck).toEqual({
57
- url: 'http://localhost:12111/v1/charges',
58
- interval_ms: 500,
59
- timeout_ms: 10000,
60
- });
61
- });
62
- it('list() returns exactly one entry matching the file contents', async () => {
63
- await writeTwin(testDir, 'postgres.yaml', `name: postgres\nimage: postgres:16\n`);
64
- const registry = createTwinRegistry();
65
- await registry.discover(testDir);
66
- expect(registry.list()).toHaveLength(1);
67
- expect(registry.list()[0].name).toBe('postgres');
68
- expect(registry.list()[0].image).toBe('postgres:16');
69
- });
70
- it('get() returns the correct definition by name', async () => {
71
- await writeTwin(testDir, 'stripe.yaml', `name: stripe\nimage: stripe/stripe-mock:latest\n`);
72
- const registry = createTwinRegistry();
73
- await registry.discover(testDir);
74
- const twin = registry.get('stripe');
75
- expect(twin).toBeDefined();
76
- expect(twin.name).toBe('stripe');
77
- expect(twin.image).toBe('stripe/stripe-mock:latest');
78
- });
79
- });
80
- // ---------------------------------------------------------------------------
81
- // AC2: Required Field Validation — Descriptive Error on Missing Fields
82
- // ---------------------------------------------------------------------------
83
- describe('AC2: required field validation', () => {
84
- it('throws TwinDefinitionError with file path when "name" is missing', async () => {
85
- await writeTwin(testDir, 'bad.yaml', `image: postgres:16\n`);
86
- const registry = createTwinRegistry();
87
- await expect(registry.discover(testDir)).rejects.toThrow(TwinDefinitionError);
88
- await expect(registry.discover(testDir)).rejects.toThrow(/missing required field: name/);
89
- await expect(registry.discover(testDir)).rejects.toThrow(/bad\.yaml/);
90
- });
91
- it('throws TwinDefinitionError with file path when "image" is missing', async () => {
92
- await writeTwin(testDir, 'bad.yaml', `name: postgres\n`);
93
- const registry = createTwinRegistry();
94
- await expect(registry.discover(testDir)).rejects.toThrow(TwinDefinitionError);
95
- await expect(registry.discover(testDir)).rejects.toThrow(/missing required field: image/);
96
- await expect(registry.discover(testDir)).rejects.toThrow(/bad\.yaml/);
97
- });
98
- });
99
- // ---------------------------------------------------------------------------
100
- // AC3: Malformed YAML and Unknown Field Detection
101
- // ---------------------------------------------------------------------------
102
- describe('AC3: malformed YAML and unknown field detection', () => {
103
- it('throws TwinDefinitionError on syntactically invalid YAML', async () => {
104
- await writeTwin(testDir, 'invalid.yaml', `name: [unclosed bracket\nimage: test:latest\n`);
105
- const registry = createTwinRegistry();
106
- await expect(registry.discover(testDir)).rejects.toThrow(TwinDefinitionError);
107
- await expect(registry.discover(testDir)).rejects.toThrow(/invalid\.yaml/);
108
- });
109
- it('throws TwinDefinitionError on unrecognised top-level field', async () => {
110
- await writeTwin(testDir, 'unknown-field.yaml', `name: test\nimage: test:latest\nunknownField: oops\n`);
111
- const registry = createTwinRegistry();
112
- await expect(registry.discover(testDir)).rejects.toThrow(TwinDefinitionError);
113
- await expect(registry.discover(testDir)).rejects.toThrow(/unknown-field\.yaml/);
114
- });
115
- it('still discovers valid sibling files in the SAME directory when one file is invalid', async () => {
116
- // Both files in the same temp directory: bad.yaml (invalid YAML) and valid.yaml
117
- await writeTwin(testDir, 'bad.yaml', `name: [unclosed`);
118
- await writeTwin(testDir, 'valid.yaml', `name: postgres\nimage: postgres:16\n`);
119
- const registry = createTwinRegistry();
120
- let thrownError;
121
- try {
122
- await registry.discover(testDir);
123
- }
124
- catch (err) {
125
- thrownError = err;
126
- }
127
- // The bad.yaml should cause a TwinDefinitionError to be thrown
128
- expect(thrownError).toBeInstanceOf(TwinDefinitionError);
129
- // The valid sibling in the same directory must still be present in the registry (AC3 fail-soft)
130
- expect(registry.list()).toHaveLength(1);
131
- expect(registry.list()[0].name).toBe('postgres');
132
- });
133
- });
134
- // ---------------------------------------------------------------------------
135
- // AC4: Duplicate Twin Name Detection
136
- // ---------------------------------------------------------------------------
137
- describe('AC4: duplicate twin name detection', () => {
138
- it('throws TwinRegistryError mentioning both file paths when duplicate names are found', async () => {
139
- await writeTwin(testDir, 'postgres-a.yaml', `name: postgres\nimage: postgres:16\n`);
140
- await writeTwin(testDir, 'postgres-b.yaml', `name: postgres\nimage: postgres:15\n`);
141
- const registry = createTwinRegistry();
142
- let thrownError;
143
- try {
144
- await registry.discover(testDir);
145
- }
146
- catch (err) {
147
- thrownError = err;
148
- }
149
- expect(thrownError).toBeDefined();
150
- expect(thrownError).toBeInstanceOf(TwinRegistryError);
151
- expect(thrownError.message).toMatch(/postgres/);
152
- expect(thrownError.message).toMatch(/postgres-a\.yaml/);
153
- expect(thrownError.message).toMatch(/postgres-b\.yaml/);
154
- });
155
- });
156
- // ---------------------------------------------------------------------------
157
- // AC5: Port Mapping Parsed into Structured Objects
158
- // ---------------------------------------------------------------------------
159
- describe('AC5: port mapping parsing', () => {
160
- it('parses "5432:5432" into { host: 5432, container: 5432 }', async () => {
161
- await writeTwin(testDir, 'postgres.yaml', `name: postgres\nimage: postgres:16\nports:\n - "5432:5432"\n`);
162
- const registry = createTwinRegistry();
163
- await registry.discover(testDir);
164
- const twin = registry.get('postgres');
165
- expect(twin.ports).toEqual([{ host: 5432, container: 5432 }]);
166
- });
167
- it('parses multiple port mappings correctly', async () => {
168
- await writeTwin(testDir, 'postgres.yaml', `name: postgres\nimage: postgres:16\nports:\n - "5432:5432"\n - "5433:5433"\n`);
169
- const registry = createTwinRegistry();
170
- await registry.discover(testDir);
171
- const twin = registry.get('postgres');
172
- expect(twin.ports).toHaveLength(2);
173
- expect(twin.ports[0]).toEqual({ host: 5432, container: 5432 });
174
- expect(twin.ports[1]).toEqual({ host: 5433, container: 5433 });
175
- });
176
- });
177
- // ---------------------------------------------------------------------------
178
- // AC6: Optional Fields Default Correctly
179
- // ---------------------------------------------------------------------------
180
- describe('AC6: optional fields default correctly', () => {
181
- it('parses a minimal definition (name + image only) without error', async () => {
182
- await writeTwin(testDir, 'minimal.yaml', `name: minimal\nimage: minimal:latest\n`);
183
- const registry = createTwinRegistry();
184
- await expect(registry.discover(testDir)).resolves.toBeUndefined();
185
- const twin = registry.get('minimal');
186
- expect(twin).toBeDefined();
187
- });
188
- it('applies correct defaults for missing optional fields', async () => {
189
- await writeTwin(testDir, 'minimal.yaml', `name: minimal\nimage: minimal:latest\n`);
190
- const registry = createTwinRegistry();
191
- await registry.discover(testDir);
192
- const twin = registry.get('minimal');
193
- expect(twin.environment).toEqual({});
194
- expect(twin.ports).toEqual([]);
195
- expect(twin.healthcheck).toBeUndefined();
196
- });
197
- });
198
- // ---------------------------------------------------------------------------
199
- // AC7: Health Endpoint Polling
200
- // ---------------------------------------------------------------------------
201
- describe('AC7: health endpoint polling', () => {
202
- it('resolves { healthy: true, attempts: 3 } when mockFetch returns 200 on 3rd call', async () => {
203
- const registry = createTwinRegistry();
204
- const twin = {
205
- name: 'test',
206
- image: 'test:latest',
207
- ports: [],
208
- environment: {},
209
- healthcheck: {
210
- url: 'http://localhost:9999/health',
211
- interval_ms: 10,
212
- timeout_ms: 500,
213
- },
214
- };
215
- let callCount = 0;
216
- const mockFetch = vi.fn().mockImplementation(async () => {
217
- callCount++;
218
- if (callCount < 3)
219
- return { ok: false, status: 503 };
220
- return { ok: true, status: 200 };
221
- });
222
- const result = await registry.pollHealth(twin, { fetch: mockFetch });
223
- expect(result).toEqual({ healthy: true, attempts: 3 });
224
- });
225
- it('resolves { healthy: false, error: "..." } when mockFetch always fails and timeout elapses', async () => {
226
- const registry = createTwinRegistry();
227
- const twin = {
228
- name: 'test',
229
- image: 'test:latest',
230
- ports: [],
231
- environment: {},
232
- healthcheck: {
233
- url: 'http://localhost:9999/health',
234
- interval_ms: 20,
235
- // Wider margin (500ms vs 100ms) avoids flakiness under CI load while keeping the test fast
236
- timeout_ms: 500,
237
- },
238
- };
239
- const mockFetch = vi.fn().mockResolvedValue({ ok: false, status: 503 });
240
- const result = await registry.pollHealth(twin, { fetch: mockFetch });
241
- expect(result).toEqual({
242
- healthy: false,
243
- error: 'Health check timed out after 500ms',
244
- });
245
- });
246
- it('resolves { healthy: true, attempts: 0 } when no healthcheck is defined', async () => {
247
- const registry = createTwinRegistry();
248
- const twin = {
249
- name: 'test',
250
- image: 'test:latest',
251
- ports: [],
252
- environment: {},
253
- // healthcheck is intentionally omitted
254
- };
255
- const result = await registry.pollHealth(twin);
256
- expect(result).toEqual({ healthy: true, attempts: 0 });
257
- });
258
- it('treats network errors (thrown exceptions) as non-2xx and continues polling', async () => {
259
- const registry = createTwinRegistry();
260
- const twin = {
261
- name: 'test',
262
- image: 'test:latest',
263
- ports: [],
264
- environment: {},
265
- healthcheck: {
266
- url: 'http://localhost:9999/health',
267
- interval_ms: 10,
268
- timeout_ms: 500,
269
- },
270
- };
271
- let callCount = 0;
272
- const mockFetch = vi.fn().mockImplementation(async () => {
273
- callCount++;
274
- if (callCount < 3)
275
- throw new Error('ECONNREFUSED');
276
- return { ok: true, status: 200 };
277
- });
278
- const result = await registry.pollHealth(twin, { fetch: mockFetch });
279
- expect(result).toEqual({ healthy: true, attempts: 3 });
280
- });
281
- });
282
- //# sourceMappingURL=registry.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"registry.test.js","sourceRoot":"","sources":["../../../src/twins/__tests__/registry.test.ts"],"names":[],"mappings":"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"}
@@ -1,9 +0,0 @@
1
- /**
2
- * Unit tests for the run-state helpers.
3
- *
4
- * Mocks `node:fs/promises` to avoid real filesystem I/O.
5
- *
6
- * Story 47-5 — Task 7.
7
- */
8
- export {};
9
- //# sourceMappingURL=run-state.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"run-state.test.d.ts","sourceRoot":"","sources":["../../../src/twins/__tests__/run-state.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -1,112 +0,0 @@
1
- /**
2
- * Unit tests for the run-state helpers.
3
- *
4
- * Mocks `node:fs/promises` to avoid real filesystem I/O.
5
- *
6
- * Story 47-5 — Task 7.
7
- */
8
- import { describe, it, expect, vi, beforeEach } from 'vitest';
9
- // ---------------------------------------------------------------------------
10
- // Module-level mocks — must be declared before any imports from mocked modules
11
- // ---------------------------------------------------------------------------
12
- vi.mock('node:fs/promises', () => ({
13
- readFile: vi.fn(),
14
- writeFile: vi.fn(),
15
- unlink: vi.fn(),
16
- mkdir: vi.fn(),
17
- }));
18
- // ---------------------------------------------------------------------------
19
- // Import mocked functions after vi.mock declarations
20
- // ---------------------------------------------------------------------------
21
- import { readFile, writeFile, unlink, mkdir } from 'node:fs/promises';
22
- import { runStatePath, readRunState, writeRunState, clearRunState } from '../run-state.js';
23
- // ---------------------------------------------------------------------------
24
- // Helpers
25
- // ---------------------------------------------------------------------------
26
- const PROJECT_DIR = '/test/project';
27
- function makeState(overrides) {
28
- return {
29
- composeDir: '/tmp/abc123',
30
- twinNames: ['localstack', 'wiremock'],
31
- startedAt: '2026-03-23T12:00:00.000Z',
32
- ...overrides,
33
- };
34
- }
35
- // ---------------------------------------------------------------------------
36
- // beforeEach: reset all mocks
37
- // ---------------------------------------------------------------------------
38
- beforeEach(() => {
39
- vi.resetAllMocks();
40
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
41
- vi.mocked(readFile).mockResolvedValue('');
42
- vi.mocked(writeFile).mockResolvedValue(undefined);
43
- vi.mocked(unlink).mockResolvedValue(undefined);
44
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
45
- vi.mocked(mkdir).mockResolvedValue(undefined);
46
- });
47
- // ---------------------------------------------------------------------------
48
- // Tests
49
- // ---------------------------------------------------------------------------
50
- describe('runStatePath', () => {
51
- it('returns path ending in .substrate/twins/.run-state.json', () => {
52
- const p = runStatePath(PROJECT_DIR);
53
- expect(p).toContain('.substrate');
54
- expect(p).toContain('twins');
55
- expect(p).toContain('.run-state.json');
56
- expect(p).toBe(`${PROJECT_DIR}/.substrate/twins/.run-state.json`);
57
- });
58
- });
59
- describe('readRunState', () => {
60
- it('returns null when file does not exist (ENOENT)', async () => {
61
- const err = Object.assign(new Error('ENOENT'), { code: 'ENOENT' });
62
- vi.mocked(readFile).mockRejectedValue(err);
63
- const result = await readRunState(PROJECT_DIR);
64
- expect(result).toBeNull();
65
- });
66
- it('returns parsed TwinRunState when file contains valid JSON', async () => {
67
- const state = makeState();
68
- vi.mocked(readFile).mockResolvedValue(JSON.stringify(state));
69
- const result = await readRunState(PROJECT_DIR);
70
- expect(result).toEqual(state);
71
- });
72
- it('throws when the file contains invalid JSON', async () => {
73
- vi.mocked(readFile).mockResolvedValue('not valid json }{');
74
- await expect(readRunState(PROJECT_DIR)).rejects.toThrow();
75
- });
76
- it('throws on I/O errors other than ENOENT', async () => {
77
- const err = Object.assign(new Error('EACCES'), { code: 'EACCES' });
78
- vi.mocked(readFile).mockRejectedValue(err);
79
- await expect(readRunState(PROJECT_DIR)).rejects.toMatchObject({ code: 'EACCES' });
80
- });
81
- });
82
- describe('writeRunState', () => {
83
- it('calls mkdir with recursive: true before writing', async () => {
84
- const state = makeState();
85
- await writeRunState(PROJECT_DIR, state);
86
- expect(mkdir).toHaveBeenCalledWith(expect.stringContaining('.substrate'), { recursive: true });
87
- });
88
- it('calls writeFile with the correct JSON content and path', async () => {
89
- const state = makeState();
90
- await writeRunState(PROJECT_DIR, state);
91
- const expectedPath = runStatePath(PROJECT_DIR);
92
- const expectedContent = JSON.stringify(state, null, 2);
93
- expect(writeFile).toHaveBeenCalledWith(expectedPath, expectedContent, 'utf-8');
94
- });
95
- });
96
- describe('clearRunState', () => {
97
- it('calls unlink with the state file path', async () => {
98
- await clearRunState(PROJECT_DIR);
99
- expect(unlink).toHaveBeenCalledWith(runStatePath(PROJECT_DIR));
100
- });
101
- it('is a no-op when file does not exist (ENOENT) — does not throw', async () => {
102
- const err = Object.assign(new Error('ENOENT'), { code: 'ENOENT' });
103
- vi.mocked(unlink).mockRejectedValue(err);
104
- await expect(clearRunState(PROJECT_DIR)).resolves.toBeUndefined();
105
- });
106
- it('re-throws I/O errors other than ENOENT', async () => {
107
- const err = Object.assign(new Error('EACCES'), { code: 'EACCES' });
108
- vi.mocked(unlink).mockRejectedValue(err);
109
- await expect(clearRunState(PROJECT_DIR)).rejects.toMatchObject({ code: 'EACCES' });
110
- });
111
- });
112
- //# sourceMappingURL=run-state.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"run-state.test.js","sourceRoot":"","sources":["../../../src/twins/__tests__/run-state.test.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAE7D,8EAA8E;AAC9E,+EAA+E;AAC/E,8EAA8E;AAE9E,EAAE,CAAC,IAAI,CAAC,kBAAkB,EAAE,GAAG,EAAE,CAAC,CAAC;IACjC,QAAQ,EAAE,EAAE,CAAC,EAAE,EAAE;IACjB,SAAS,EAAE,EAAE,CAAC,EAAE,EAAE;IAClB,MAAM,EAAE,EAAE,CAAC,EAAE,EAAE;IACf,KAAK,EAAE,EAAE,CAAC,EAAE,EAAE;CACf,CAAC,CAAC,CAAA;AAEH,8EAA8E;AAC9E,qDAAqD;AACrD,8EAA8E;AAE9E,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAA;AACrE,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAG1F,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,MAAM,WAAW,GAAG,eAAe,CAAA;AAEnC,SAAS,SAAS,CAAC,SAAiC;IAClD,OAAO;QACL,UAAU,EAAE,aAAa;QACzB,SAAS,EAAE,CAAC,YAAY,EAAE,UAAU,CAAC;QACrC,SAAS,EAAE,0BAA0B;QACrC,GAAG,SAAS;KACb,CAAA;AACH,CAAC;AAED,8EAA8E;AAC9E,8BAA8B;AAC9B,8EAA8E;AAE9E,UAAU,CAAC,GAAG,EAAE;IACd,EAAE,CAAC,aAAa,EAAE,CAAA;IAClB,8DAA8D;IAC9D,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,iBAAiB,CAAC,EAAS,CAAC,CAAA;IAChD,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAA;IACjD,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAA;IAC9C,8DAA8D;IAC9D,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,iBAAiB,CAAC,SAAgB,CAAC,CAAA;AACtD,CAAC,CAAC,CAAA;AAEF,8EAA8E;AAC9E,QAAQ;AACR,8EAA8E;AAE9E,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QACjE,MAAM,CAAC,GAAG,YAAY,CAAC,WAAW,CAAC,CAAA;QACnC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,YAAY,CAAC,CAAA;QACjC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;QAC5B,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAA;QACtC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,WAAW,mCAAmC,CAAC,CAAA;IACnE,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAC9D,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;QAClE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAA;QAE1C,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,WAAW,CAAC,CAAA;QAC9C,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE,CAAA;IAC3B,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2DAA2D,EAAE,KAAK,IAAI,EAAE;QACzE,MAAM,KAAK,GAAG,SAAS,EAAE,CAAA;QACzB,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,iBAAiB,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAQ,CAAC,CAAA;QAEnE,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC,WAAW,CAAC,CAAA;QAC9C,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;IAC/B,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;QAC1D,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,iBAAiB,CAAC,mBAA0B,CAAC,CAAA;QAEjE,MAAM,MAAM,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAA;IAC3D,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;QACtD,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;QAClE,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAA;QAE1C,MAAM,MAAM,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;IACnF,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;QAC/D,MAAM,KAAK,GAAG,SAAS,EAAE,CAAA;QACzB,MAAM,aAAa,CAAC,WAAW,EAAE,KAAK,CAAC,CAAA;QAEvC,MAAM,CAAC,KAAK,CAAC,CAAC,oBAAoB,CAChC,MAAM,CAAC,gBAAgB,CAAC,YAAY,CAAC,EACrC,EAAE,SAAS,EAAE,IAAI,EAAE,CACpB,CAAA;IACH,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,wDAAwD,EAAE,KAAK,IAAI,EAAE;QACtE,MAAM,KAAK,GAAG,SAAS,EAAE,CAAA;QACzB,MAAM,aAAa,CAAC,WAAW,EAAE,KAAK,CAAC,CAAA;QAEvC,MAAM,YAAY,GAAG,YAAY,CAAC,WAAW,CAAC,CAAA;QAC9C,MAAM,eAAe,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAA;QACtD,MAAM,CAAC,SAAS,CAAC,CAAC,oBAAoB,CAAC,YAAY,EAAE,eAAe,EAAE,OAAO,CAAC,CAAA;IAChF,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,EAAE,CAAC,uCAAuC,EAAE,KAAK,IAAI,EAAE;QACrD,MAAM,aAAa,CAAC,WAAW,CAAC,CAAA;QAEhC,MAAM,CAAC,MAAM,CAAC,CAAC,oBAAoB,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC,CAAA;IAChE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,+DAA+D,EAAE,KAAK,IAAI,EAAE;QAC7E,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;QAClE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAA;QAExC,MAAM,MAAM,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,aAAa,EAAE,CAAA;IACnE,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,wCAAwC,EAAE,KAAK,IAAI,EAAE;QACtD,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;QAClE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,iBAAiB,CAAC,GAAG,CAAC,CAAA;QAExC,MAAM,MAAM,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAA;IACpF,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA"}
@@ -1,10 +0,0 @@
1
- /**
2
- * Integration tests for the `twins init` and `twins templates` CLI logic.
3
- *
4
- * Story 47-4 — Task 7.
5
- *
6
- * These tests invoke the underlying logic directly (not via Commander subprocess) to avoid
7
- * overhead and to allow precise assertion on file system state and exit codes.
8
- */
9
- export {};
10
- //# sourceMappingURL=templates-cli.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"templates-cli.test.d.ts","sourceRoot":"","sources":["../../../src/twins/__tests__/templates-cli.test.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG"}
@@ -1,187 +0,0 @@
1
- /**
2
- * Integration tests for the `twins init` and `twins templates` CLI logic.
3
- *
4
- * Story 47-4 — Task 7.
5
- *
6
- * These tests invoke the underlying logic directly (not via Commander subprocess) to avoid
7
- * overhead and to allow precise assertion on file system state and exit codes.
8
- */
9
- import { describe, it, expect, beforeEach, afterEach } from 'vitest';
10
- import * as fs from 'node:fs';
11
- import * as fsPromises from 'node:fs/promises';
12
- import * as os from 'node:os';
13
- import * as path from 'node:path';
14
- import yaml from 'js-yaml';
15
- import { getTwinTemplate, listTwinTemplates } from '../templates.js';
16
- import { TwinDefinitionSchema } from '../schema.js';
17
- /**
18
- * Extracted init logic from the `factory twins init` Commander action.
19
- * Returns stdout, stderr, and exit code without actually calling `process.exit`.
20
- */
21
- async function runInitLogic(opts, cwd) {
22
- let stdout = '';
23
- let stderr = '';
24
- const entry = getTwinTemplate(opts.template);
25
- if (!entry) {
26
- const available = listTwinTemplates()
27
- .map((t) => t.name)
28
- .join(', ');
29
- stderr = `Error: Unknown template '${opts.template}'. Available: ${available}\n`;
30
- return { exitCode: 1, stdout, stderr };
31
- }
32
- const targetPath = path.join(cwd, '.substrate', 'twins', `${opts.template}.yaml`);
33
- if (!opts.force) {
34
- try {
35
- await fsPromises.access(targetPath);
36
- // File exists — error without --force
37
- stderr = `Error: File already exists: ${targetPath} — use --force to overwrite\n`;
38
- return { exitCode: 1, stdout, stderr };
39
- }
40
- catch {
41
- // access() threw → file does not exist → proceed
42
- }
43
- }
44
- await fsPromises.mkdir(path.dirname(targetPath), { recursive: true });
45
- const yamlContent = yaml.dump(entry.definition);
46
- await fsPromises.writeFile(targetPath, yamlContent, 'utf-8');
47
- stdout = `Created ${targetPath}\n`;
48
- return { exitCode: 0, stdout, stderr };
49
- }
50
- // ---------------------------------------------------------------------------
51
- // Helpers — mirror the templates action logic from factory-command.ts
52
- // ---------------------------------------------------------------------------
53
- /**
54
- * Extracted templates listing logic from the `factory twins templates` Commander action.
55
- * Returns the stdout string that would be written to process.stdout.
56
- */
57
- function runTemplatesLogic() {
58
- const templates = listTwinTemplates();
59
- let stdout = '';
60
- for (const t of templates) {
61
- stdout += ` ${t.name.padEnd(16)} ${t.description}\n`;
62
- }
63
- return stdout;
64
- }
65
- // ---------------------------------------------------------------------------
66
- // Tests
67
- // ---------------------------------------------------------------------------
68
- let tmpDir;
69
- beforeEach(() => {
70
- tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'twins-cli-'));
71
- });
72
- afterEach(() => {
73
- fs.rmSync(tmpDir, { recursive: true, force: true });
74
- });
75
- describe('factory twins init — localstack', () => {
76
- it('creates .substrate/twins/localstack.yaml', async () => {
77
- const result = await runInitLogic({ template: 'localstack', force: false }, tmpDir);
78
- expect(result.exitCode).toBe(0);
79
- const filePath = path.join(tmpDir, '.substrate', 'twins', 'localstack.yaml');
80
- expect(fs.existsSync(filePath)).toBe(true);
81
- const content = fs.readFileSync(filePath, 'utf-8');
82
- const parsed = yaml.load(content);
83
- expect(parsed.name).toBe('localstack');
84
- expect(parsed.image).toBe('localstack/localstack:latest');
85
- expect(parsed.ports).toContain('4566:4566');
86
- expect(parsed.healthcheck?.url).toBe('http://localhost:4566/_localstack/health');
87
- // Must pass schema validation
88
- expect(() => TwinDefinitionSchema.parse(parsed)).not.toThrow();
89
- });
90
- });
91
- describe('factory twins init — wiremock', () => {
92
- it('creates .substrate/twins/wiremock.yaml', async () => {
93
- const result = await runInitLogic({ template: 'wiremock', force: false }, tmpDir);
94
- expect(result.exitCode).toBe(0);
95
- const filePath = path.join(tmpDir, '.substrate', 'twins', 'wiremock.yaml');
96
- expect(fs.existsSync(filePath)).toBe(true);
97
- const content = fs.readFileSync(filePath, 'utf-8');
98
- const parsed = yaml.load(content);
99
- expect(parsed.name).toBe('wiremock');
100
- expect(parsed.image).toBe('wiremock/wiremock:latest');
101
- expect(parsed.ports).toContain('8080:8080');
102
- expect(parsed.healthcheck?.url).toBe('http://localhost:8080/__admin/health');
103
- // Must pass schema validation
104
- expect(() => TwinDefinitionSchema.parse(parsed)).not.toThrow();
105
- });
106
- });
107
- describe('factory twins init — unknown template', () => {
108
- it('exits with code 1 and mentions Available templates', async () => {
109
- const result = await runInitLogic({ template: 'bogus' }, tmpDir);
110
- expect(result.exitCode).toBe(1);
111
- expect(result.stderr).toContain('Unknown template');
112
- expect(result.stderr).toContain('bogus');
113
- expect(result.stderr).toContain('Available:');
114
- expect(result.stderr).toContain('localstack');
115
- expect(result.stderr).toContain('wiremock');
116
- });
117
- });
118
- describe('factory twins init — existing file without --force', () => {
119
- it('exits with code 1 and does NOT overwrite existing file', async () => {
120
- // Pre-create the file with sentinel content
121
- const twinsDir = path.join(tmpDir, '.substrate', 'twins');
122
- fs.mkdirSync(twinsDir, { recursive: true });
123
- const filePath = path.join(twinsDir, 'localstack.yaml');
124
- const sentinelContent = 'sentinel: content\n';
125
- fs.writeFileSync(filePath, sentinelContent, 'utf-8');
126
- const result = await runInitLogic({ template: 'localstack', force: false }, tmpDir);
127
- expect(result.exitCode).toBe(1);
128
- expect(result.stderr).toContain('already exists');
129
- expect(result.stderr).toContain('--force');
130
- // File must NOT have been overwritten
131
- const actual = fs.readFileSync(filePath, 'utf-8');
132
- expect(actual).toBe(sentinelContent);
133
- });
134
- });
135
- describe('factory twins init — existing file with --force', () => {
136
- it('overwrites the existing file with valid YAML', async () => {
137
- // Pre-create the file with sentinel content
138
- const twinsDir = path.join(tmpDir, '.substrate', 'twins');
139
- fs.mkdirSync(twinsDir, { recursive: true });
140
- const filePath = path.join(twinsDir, 'localstack.yaml');
141
- fs.writeFileSync(filePath, 'sentinel: content\n', 'utf-8');
142
- const result = await runInitLogic({ template: 'localstack', force: true }, tmpDir);
143
- expect(result.exitCode).toBe(0);
144
- const content = fs.readFileSync(filePath, 'utf-8');
145
- const parsed = yaml.load(content);
146
- expect(parsed.name).toBe('localstack');
147
- expect(() => TwinDefinitionSchema.parse(parsed)).not.toThrow();
148
- });
149
- });
150
- describe('factory twins templates — output format', () => {
151
- it('outputs one line per template with name and description', () => {
152
- const output = runTemplatesLogic();
153
- const lines = output.split('\n').filter((l) => l.trim().length > 0);
154
- // At least two entries (localstack + wiremock)
155
- expect(lines.length).toBeGreaterThanOrEqual(2);
156
- // Each line must be non-empty and contain a name+description pair
157
- for (const line of lines) {
158
- expect(line.trim().length).toBeGreaterThan(0);
159
- }
160
- });
161
- it('includes localstack with its description on one line', () => {
162
- const output = runTemplatesLogic();
163
- const lines = output.split('\n').filter((l) => l.trim().length > 0);
164
- const localstackLine = lines.find((l) => l.includes('localstack'));
165
- expect(localstackLine).toBeDefined();
166
- // Line must contain the description text
167
- expect(localstackLine).toContain('LocalStack');
168
- });
169
- it('includes wiremock with its description on one line', () => {
170
- const output = runTemplatesLogic();
171
- const lines = output.split('\n').filter((l) => l.trim().length > 0);
172
- const wiremockLine = lines.find((l) => l.includes('wiremock'));
173
- expect(wiremockLine).toBeDefined();
174
- // Line must contain the description text
175
- expect(wiremockLine).toContain('WireMock');
176
- });
177
- it('uses padEnd(16) so name and description are tab-aligned', () => {
178
- const output = runTemplatesLogic();
179
- // Each line should start with two spaces and the name padded to ≥16 chars + two spaces
180
- const lines = output.split('\n').filter((l) => l.trim().length > 0);
181
- for (const line of lines) {
182
- // Format: " <name padEnd(16)> <description>"
183
- expect(line).toMatch(/^ \S.{14,} \S/);
184
- }
185
- });
186
- });
187
- //# sourceMappingURL=templates-cli.test.js.map