@celilo/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (267) hide show
  1. package/README.md +1566 -0
  2. package/bin/celilo +16 -0
  3. package/drizzle/0000_complex_puma.sql +179 -0
  4. package/drizzle/0001_dizzy_wolfpack.sql +2 -0
  5. package/drizzle/0002_web_routes.sql +16 -0
  6. package/drizzle/0003_backup_storage.sql +32 -0
  7. package/drizzle/meta/0000_snapshot.json +1151 -0
  8. package/drizzle/meta/0001_snapshot.json +1167 -0
  9. package/drizzle/meta/0002_snapshot.json +1257 -0
  10. package/drizzle/meta/_journal.json +27 -0
  11. package/package.json +64 -0
  12. package/schemas/system_config.json +106 -0
  13. package/src/__integration__/container-services-cli.integration.test.ts +246 -0
  14. package/src/ansible/dependencies.test.ts +309 -0
  15. package/src/ansible/dependencies.ts +896 -0
  16. package/src/ansible/inventory.test.ts +463 -0
  17. package/src/ansible/inventory.ts +445 -0
  18. package/src/ansible/secrets.ts +222 -0
  19. package/src/ansible/validation.test.ts +92 -0
  20. package/src/ansible/validation.ts +272 -0
  21. package/src/api-clients/digitalocean.ts +94 -0
  22. package/src/api-clients/proxmox.ts +655 -0
  23. package/src/capabilities/logging-wrapper.test.ts +217 -0
  24. package/src/capabilities/lookup.test.ts +149 -0
  25. package/src/capabilities/lookup.ts +89 -0
  26. package/src/capabilities/public-web-helpers.test.ts +198 -0
  27. package/src/capabilities/public-web-publish.test.ts +458 -0
  28. package/src/capabilities/registration.test.ts +395 -0
  29. package/src/capabilities/registration.ts +200 -0
  30. package/src/capabilities/route-validation.test.ts +121 -0
  31. package/src/capabilities/route-validation.ts +96 -0
  32. package/src/capabilities/secret-ref.test.ts +313 -0
  33. package/src/capabilities/secret-validation.ts +157 -0
  34. package/src/capabilities/secrets.test.ts +750 -0
  35. package/src/capabilities/secrets.ts +244 -0
  36. package/src/capabilities/validation.test.ts +613 -0
  37. package/src/capabilities/validation.ts +160 -0
  38. package/src/capabilities/well-known.test.ts +238 -0
  39. package/src/capabilities/well-known.ts +222 -0
  40. package/src/cli/cli.test.ts +654 -0
  41. package/src/cli/command-registry.ts +742 -0
  42. package/src/cli/command-tree-parser.test.ts +180 -0
  43. package/src/cli/command-tree-parser.ts +193 -0
  44. package/src/cli/commands/backup-create.ts +137 -0
  45. package/src/cli/commands/backup-delete.ts +74 -0
  46. package/src/cli/commands/backup-import.ts +97 -0
  47. package/src/cli/commands/backup-list.ts +132 -0
  48. package/src/cli/commands/backup-name.ts +73 -0
  49. package/src/cli/commands/backup-prune.ts +98 -0
  50. package/src/cli/commands/backup-restore.ts +122 -0
  51. package/src/cli/commands/capability-info.ts +121 -0
  52. package/src/cli/commands/capability-list.ts +47 -0
  53. package/src/cli/commands/completion.ts +87 -0
  54. package/src/cli/commands/hook-run.ts +176 -0
  55. package/src/cli/commands/ipam.ts +607 -0
  56. package/src/cli/commands/machine-add.ts +235 -0
  57. package/src/cli/commands/machine-earmark.ts +82 -0
  58. package/src/cli/commands/machine-list.ts +77 -0
  59. package/src/cli/commands/machine-remove.ts +90 -0
  60. package/src/cli/commands/machine-status.ts +131 -0
  61. package/src/cli/commands/module-audit.ts +51 -0
  62. package/src/cli/commands/module-build.ts +60 -0
  63. package/src/cli/commands/module-config.ts +170 -0
  64. package/src/cli/commands/module-deploy.ts +71 -0
  65. package/src/cli/commands/module-generate.ts +236 -0
  66. package/src/cli/commands/module-health.ts +108 -0
  67. package/src/cli/commands/module-import.ts +80 -0
  68. package/src/cli/commands/module-list.ts +43 -0
  69. package/src/cli/commands/module-logs.ts +73 -0
  70. package/src/cli/commands/module-remove.ts +162 -0
  71. package/src/cli/commands/module-show.ts +208 -0
  72. package/src/cli/commands/module-status.ts +131 -0
  73. package/src/cli/commands/module-types.ts +189 -0
  74. package/src/cli/commands/module-upgrade.ts +192 -0
  75. package/src/cli/commands/package.ts +68 -0
  76. package/src/cli/commands/secret-list.ts +99 -0
  77. package/src/cli/commands/secret-set.ts +134 -0
  78. package/src/cli/commands/service-add-digitalocean.ts +133 -0
  79. package/src/cli/commands/service-add-proxmox.ts +342 -0
  80. package/src/cli/commands/service-config-get.ts +83 -0
  81. package/src/cli/commands/service-config-set.ts +145 -0
  82. package/src/cli/commands/service-list.ts +74 -0
  83. package/src/cli/commands/service-reconfigure.ts +230 -0
  84. package/src/cli/commands/service-remove.ts +103 -0
  85. package/src/cli/commands/service-verify.ts +240 -0
  86. package/src/cli/commands/status.ts +216 -0
  87. package/src/cli/commands/storage-add-local.ts +106 -0
  88. package/src/cli/commands/storage-add-s3.ts +114 -0
  89. package/src/cli/commands/storage-list.ts +72 -0
  90. package/src/cli/commands/storage-remove.ts +54 -0
  91. package/src/cli/commands/storage-set-default.ts +44 -0
  92. package/src/cli/commands/storage-verify.ts +54 -0
  93. package/src/cli/commands/system-config.ts +168 -0
  94. package/src/cli/commands/system-init.ts +314 -0
  95. package/src/cli/commands/system-secret-get.ts +98 -0
  96. package/src/cli/commands/system-secret-set.ts +76 -0
  97. package/src/cli/commands/system-vault-password.ts +34 -0
  98. package/src/cli/completion.test.ts +37 -0
  99. package/src/cli/completion.ts +482 -0
  100. package/src/cli/fuel-gauge.test.ts +208 -0
  101. package/src/cli/fuel-gauge.ts +405 -0
  102. package/src/cli/generate-zsh-completion.test.ts +95 -0
  103. package/src/cli/generate-zsh-completion.ts +497 -0
  104. package/src/cli/index.ts +1583 -0
  105. package/src/cli/interactive-config.test.ts +201 -0
  106. package/src/cli/interactive-config.ts +62 -0
  107. package/src/cli/parser.test.ts +227 -0
  108. package/src/cli/parser.ts +244 -0
  109. package/src/cli/prompts.test.ts +33 -0
  110. package/src/cli/prompts.ts +121 -0
  111. package/src/cli/types.ts +38 -0
  112. package/src/cli/validators.test.ts +235 -0
  113. package/src/cli/validators.ts +188 -0
  114. package/src/config/env.ts +41 -0
  115. package/src/config/paths.test.ts +172 -0
  116. package/src/config/paths.ts +108 -0
  117. package/src/db/client.ts +190 -0
  118. package/src/db/migrate.ts +30 -0
  119. package/src/db/schema.test.ts +221 -0
  120. package/src/db/schema.ts +434 -0
  121. package/src/hooks/capability-loader-firewall.test.ts +246 -0
  122. package/src/hooks/capability-loader.test.ts +100 -0
  123. package/src/hooks/capability-loader.ts +520 -0
  124. package/src/hooks/define-hook.test.ts +488 -0
  125. package/src/hooks/executor.test.ts +462 -0
  126. package/src/hooks/executor.ts +469 -0
  127. package/src/hooks/logger.test.ts +54 -0
  128. package/src/hooks/logger.ts +95 -0
  129. package/src/hooks/test-fixtures/failing-hook.ts +13 -0
  130. package/src/hooks/test-fixtures/no-default-hook.ts +6 -0
  131. package/src/hooks/test-fixtures/success-hook.ts +20 -0
  132. package/src/hooks/test-fixtures/unbranded-hook.ts +11 -0
  133. package/src/hooks/test-fixtures/void-hook.ts +13 -0
  134. package/src/hooks/types.ts +89 -0
  135. package/src/infrastructure/property-extractor.test.ts +194 -0
  136. package/src/infrastructure/property-extractor.ts +151 -0
  137. package/src/ipam/allocator.test.ts +442 -0
  138. package/src/ipam/allocator.ts +369 -0
  139. package/src/ipam/auto-allocator.test.ts +247 -0
  140. package/src/ipam/auto-allocator.ts +270 -0
  141. package/src/ipam/subnet-parser.test.ts +107 -0
  142. package/src/ipam/subnet-parser.ts +136 -0
  143. package/src/manifest/contracts/index.ts +61 -0
  144. package/src/manifest/contracts/v1.ts +118 -0
  145. package/src/manifest/json-schema-roundtrip.test.ts +99 -0
  146. package/src/manifest/schema.ts +367 -0
  147. package/src/manifest/template-validator.test.ts +231 -0
  148. package/src/manifest/template-validator.ts +322 -0
  149. package/src/manifest/validate.test.ts +1180 -0
  150. package/src/manifest/validate.ts +415 -0
  151. package/src/module/import.test.ts +355 -0
  152. package/src/module/import.ts +676 -0
  153. package/src/module/packaging/audit.ts +169 -0
  154. package/src/module/packaging/build.ts +228 -0
  155. package/src/module/packaging/checksum.ts +41 -0
  156. package/src/module/packaging/extract.ts +234 -0
  157. package/src/module/packaging/signature.ts +47 -0
  158. package/src/secrets/encryption.test.ts +284 -0
  159. package/src/secrets/encryption.ts +162 -0
  160. package/src/secrets/generators.test.ts +112 -0
  161. package/src/secrets/generators.ts +127 -0
  162. package/src/secrets/master-key.test.ts +159 -0
  163. package/src/secrets/master-key.ts +114 -0
  164. package/src/secrets/storage.test.ts +115 -0
  165. package/src/secrets/storage.ts +106 -0
  166. package/src/secrets/vault.test.ts +35 -0
  167. package/src/secrets/vault.ts +42 -0
  168. package/src/services/backup-create.ts +532 -0
  169. package/src/services/backup-metadata.ts +198 -0
  170. package/src/services/backup-restore.ts +229 -0
  171. package/src/services/backup-retention.ts +84 -0
  172. package/src/services/backup-storage.ts +281 -0
  173. package/src/services/build-stream.test.ts +122 -0
  174. package/src/services/build-stream.ts +201 -0
  175. package/src/services/config-interview.ts +694 -0
  176. package/src/services/container-service.test.ts +298 -0
  177. package/src/services/container-service.ts +401 -0
  178. package/src/services/cross-module-data-manager.test.ts +405 -0
  179. package/src/services/cross-module-data-manager.ts +412 -0
  180. package/src/services/deploy-ansible.ts +88 -0
  181. package/src/services/deploy-planner.ts +153 -0
  182. package/src/services/deploy-preflight.ts +274 -0
  183. package/src/services/deploy-ssh.ts +131 -0
  184. package/src/services/deploy-terraform.test.ts +55 -0
  185. package/src/services/deploy-terraform.ts +445 -0
  186. package/src/services/deploy-validation.ts +311 -0
  187. package/src/services/dns-auto-register.ts +211 -0
  188. package/src/services/health-runner.ts +184 -0
  189. package/src/services/infrastructure-selector.test.ts +485 -0
  190. package/src/services/infrastructure-selector.ts +245 -0
  191. package/src/services/infrastructure-variable-resolver.test.ts +751 -0
  192. package/src/services/infrastructure-variable-resolver.ts +234 -0
  193. package/src/services/machine-detector.ts +328 -0
  194. package/src/services/machine-pool.test.ts +405 -0
  195. package/src/services/machine-pool.ts +316 -0
  196. package/src/services/manifest-validation.ts +120 -0
  197. package/src/services/module-build.test.ts +290 -0
  198. package/src/services/module-build.ts +431 -0
  199. package/src/services/module-config.test.ts +237 -0
  200. package/src/services/module-config.ts +298 -0
  201. package/src/services/module-deploy.ts +862 -0
  202. package/src/services/module-types-drift.test.ts +73 -0
  203. package/src/services/module-types-generator.test.ts +288 -0
  204. package/src/services/module-types-generator.ts +189 -0
  205. package/src/services/proxmox-state-recovery.ts +140 -0
  206. package/src/services/schema-validation.ts +155 -0
  207. package/src/services/secret-schema-loader.test.ts +311 -0
  208. package/src/services/secret-schema-loader.ts +239 -0
  209. package/src/services/ssh-key-manager.test.ts +283 -0
  210. package/src/services/ssh-key-manager.ts +193 -0
  211. package/src/services/storage-providers/local.ts +105 -0
  212. package/src/services/storage-providers/s3.ts +182 -0
  213. package/src/services/storage-providers/types.ts +24 -0
  214. package/src/services/system-config-schema-types.ts +25 -0
  215. package/src/services/system-config-validator.test.ts +160 -0
  216. package/src/services/system-config-validator.ts +74 -0
  217. package/src/services/system-init.test.ts +153 -0
  218. package/src/services/system-init.ts +253 -0
  219. package/src/services/terraform-safety.ts +174 -0
  220. package/src/services/zone-detector.test.ts +110 -0
  221. package/src/services/zone-detector.ts +102 -0
  222. package/src/services/zone-policy.test.ts +97 -0
  223. package/src/services/zone-policy.ts +126 -0
  224. package/src/templates/generator.test.ts +645 -0
  225. package/src/templates/generator.ts +1119 -0
  226. package/src/templates/types.ts +62 -0
  227. package/src/test-utils/INTERACTIVE_PROMPTS.md +167 -0
  228. package/src/test-utils/cli-context-interactive.test.ts +152 -0
  229. package/src/test-utils/cli-context-server.test.ts +66 -0
  230. package/src/test-utils/cli-context.test.ts +273 -0
  231. package/src/test-utils/cli-context.ts +677 -0
  232. package/src/test-utils/cli-result.test.ts +282 -0
  233. package/src/test-utils/cli-result.ts +241 -0
  234. package/src/test-utils/cli.ts +55 -0
  235. package/src/test-utils/completion-harness.test.ts +126 -0
  236. package/src/test-utils/completion-harness.ts +82 -0
  237. package/src/test-utils/database.test.ts +182 -0
  238. package/src/test-utils/database.ts +126 -0
  239. package/src/test-utils/filesystem.test.ts +208 -0
  240. package/src/test-utils/filesystem.ts +142 -0
  241. package/src/test-utils/fixtures.test.ts +123 -0
  242. package/src/test-utils/fixtures.ts +160 -0
  243. package/src/test-utils/golden-diff.ts +197 -0
  244. package/src/test-utils/index.ts +77 -0
  245. package/src/test-utils/integration.ts +81 -0
  246. package/src/test-utils/module-fixtures.ts +468 -0
  247. package/src/test-utils/modules.test.ts +144 -0
  248. package/src/test-utils/modules.ts +183 -0
  249. package/src/test-utils/setup-test-db.ts +90 -0
  250. package/src/test-utils/value-extractor.test.ts +231 -0
  251. package/src/test-utils/value-extractor.ts +228 -0
  252. package/src/types/infrastructure.ts +157 -0
  253. package/src/utils/shell.test.ts +365 -0
  254. package/src/utils/shell.ts +159 -0
  255. package/src/validation/schemas.ts +166 -0
  256. package/src/variables/ansible-resolver.test.ts +142 -0
  257. package/src/variables/ansible-resolver.ts +69 -0
  258. package/src/variables/capability-self-ref.test.ts +220 -0
  259. package/src/variables/context.test.ts +1265 -0
  260. package/src/variables/context.ts +624 -0
  261. package/src/variables/declarative-derivation.test.ts +743 -0
  262. package/src/variables/declarative-derivation.ts +200 -0
  263. package/src/variables/parser.test.ts +231 -0
  264. package/src/variables/parser.ts +76 -0
  265. package/src/variables/resolver.test.ts +458 -0
  266. package/src/variables/resolver.ts +282 -0
  267. package/src/variables/types.ts +59 -0
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Template generation types
3
+ */
4
+
5
+ /**
6
+ * Template file to process
7
+ */
8
+ export interface TemplateFile {
9
+ sourcePath: string;
10
+ targetPath: string;
11
+ content: string;
12
+ }
13
+
14
+ /**
15
+ * Generated file output
16
+ */
17
+ export interface GeneratedFile {
18
+ path: string;
19
+ content: string;
20
+ }
21
+
22
+ /**
23
+ * Infrastructure information for generated templates
24
+ */
25
+ export interface InfrastructureInfo {
26
+ type: 'machine' | 'container_service';
27
+ machineId?: string;
28
+ machineName?: string;
29
+ serviceId?: string;
30
+ serviceName?: string;
31
+ zone: string;
32
+ }
33
+
34
+ /**
35
+ * Template generation result
36
+ */
37
+ export interface GenerateSuccess {
38
+ success: true;
39
+ files: GeneratedFile[];
40
+ outputPath: string;
41
+ infrastructure?: InfrastructureInfo;
42
+ }
43
+
44
+ export interface GenerateError {
45
+ success: false;
46
+ error: string;
47
+ details?: unknown;
48
+ }
49
+
50
+ export type GenerateResult = GenerateSuccess | GenerateError;
51
+
52
+ /**
53
+ * Template generation options
54
+ */
55
+ export interface GenerateOptions {
56
+ moduleId: string;
57
+ modulePath: string;
58
+ outputPath: string;
59
+ db?: ReturnType<typeof import('../db/client').getDb>;
60
+ /** Skip required variable validation (used by deploy flow which handles interview separately) */
61
+ skipVariableValidation?: boolean;
62
+ }
@@ -0,0 +1,167 @@
1
+ # Interactive Prompts Testing — `CLIContext` reference
2
+
3
+ `CLIContext` (in `cli-context.ts`) maintains a single long-lived CLI
4
+ process per test that accepts multiple commands. It also exposes a
5
+ fluent API for testing CLI commands that read from stdin — interactive
6
+ prompts, wizards, confirmation steps.
7
+
8
+ This document is a cookbook of patterns for the interactive bits.
9
+ For unit-test-style assertions on command output, see `cli-result.ts`
10
+ and the existing tests under `cli-context.test.ts`.
11
+
12
+ These examples were originally a `.test.ts` file under `examples/` that
13
+ was permanently `describe.skip`'d because the specific commands they
14
+ imagined (e.g. `module configure homebridge`, `system setup wizard`)
15
+ don't exist in the CLI. They're kept here as illustrative reference,
16
+ not as a regression suite.
17
+
18
+ ## Pattern: respond to a single prompt
19
+
20
+ Use `cli.on(/regex/).respond(text)` after starting a command. The
21
+ fluent helper waits for output matching the pattern, then writes the
22
+ response to stdin.
23
+
24
+ ```typescript
25
+ const commandPromise = cli.run('module configure homebridge');
26
+
27
+ await cli.on(/Enter hostname:/).respond('iot.local\n');
28
+
29
+ const result = await commandPromise;
30
+ expect(result.exitCode).toBe(0);
31
+ ```
32
+
33
+ ## Pattern: respond to multiple prompts in sequence
34
+
35
+ Wizards typically prompt for several fields back-to-back. Chain
36
+ `cli.on().respond()` calls; each one waits for its own pattern before
37
+ sending input.
38
+
39
+ ```typescript
40
+ const commandPromise = cli.run('system init --interactive');
41
+
42
+ await cli.on(/Enter primary domain:/).respond('example.com\n');
43
+ await cli.on(/Enter admin email:/).respond('admin@example.com\n');
44
+ await cli.on(/Confirm settings/).respond('yes\n');
45
+
46
+ const result = await commandPromise;
47
+ expect(result.exitCode).toBe(0);
48
+ ```
49
+
50
+ ## Pattern: conditional responses based on output
51
+
52
+ Sometimes a command emits a warning or branching prompt only under
53
+ certain conditions. Use `cli.expectOutput(/pattern/)` to wait for the
54
+ trigger, then decide what to send.
55
+
56
+ ```typescript
57
+ const commandPromise = cli.run('module deploy homebridge');
58
+
59
+ await cli.expectOutput(/Ready to deploy/);
60
+
61
+ await cli
62
+ .expectOutput(/WARNING/, { timeout: 1000 })
63
+ .then(async () => {
64
+ // Warning appeared — confirm explicitly
65
+ await cli.sendKeys('yes\n');
66
+ })
67
+ .catch(() => {
68
+ // No warning — quick confirm
69
+ cli.sendKeys('y\n');
70
+ });
71
+
72
+ const result = await commandPromise;
73
+ expect(result.exitCode).toBe(0);
74
+ ```
75
+
76
+ ## Pattern: testing input validation
77
+
78
+ Provide a bad value, assert the validation error appears, then provide
79
+ a good value.
80
+
81
+ ```typescript
82
+ const commandPromise = cli.run('machine add');
83
+
84
+ await cli.on(/Enter hostname:/).respond('invalid hostname!\n');
85
+
86
+ await cli.expectOutput(/Invalid hostname format/);
87
+
88
+ await cli.on(/Enter hostname:/).respond('valid-hostname\n');
89
+
90
+ const result = await commandPromise;
91
+ expect(result.exitCode).toBe(0);
92
+ ```
93
+
94
+ ## Pattern: timeout when a prompt never appears
95
+
96
+ `expectOutput` rejects with a timeout error if its pattern doesn't
97
+ appear within the configured window. Use this to assert the absence
98
+ of output.
99
+
100
+ ```typescript
101
+ const commandPromise = cli.run('some-command');
102
+
103
+ await expect(
104
+ cli.expectOutput(/Prompt that never appears/, { timeout: 1000 }),
105
+ ).rejects.toThrow(/Timeout waiting for pattern/);
106
+
107
+ await commandPromise;
108
+ ```
109
+
110
+ ## Pattern: complex multi-step wizard
111
+
112
+ Combine `expectOutput` (to assert section markers) with `cli.on().respond()`
113
+ (to provide values) for full wizard tests.
114
+
115
+ ```typescript
116
+ const commandPromise = cli.run('system setup wizard');
117
+
118
+ // Step 1: network
119
+ await cli.expectOutput(/=== Network Configuration ===/);
120
+ await cli.on(/Enter subnet/).respond('192.168.0.0/24\n');
121
+ await cli.on(/Enter gateway/).respond('192.168.0.1\n');
122
+
123
+ // Step 2: DNS
124
+ await cli.expectOutput(/=== DNS Configuration ===/);
125
+ await cli.on(/Primary DNS/).respond('8.8.8.8\n');
126
+ await cli.on(/Secondary DNS/).respond('1.1.1.1\n');
127
+
128
+ // Step 3: confirm
129
+ await cli.expectOutput(/=== Review Settings ===/);
130
+ await cli.expectOutput(/Network: 192.168.0.0\/24/);
131
+ await cli.on(/Confirm/).respond('yes\n');
132
+
133
+ const result = await commandPromise;
134
+ expect(result.exitCode).toBe(0);
135
+ expect(result.stdout).toContain('Setup complete');
136
+ ```
137
+
138
+ ## Pattern: verbose logging while debugging
139
+
140
+ `cli.setVerbose(true)` makes the harness print every prompt and
141
+ response to the test log. Useful for debugging a flaky interactive
142
+ test.
143
+
144
+ ```typescript
145
+ cli.setVerbose(true);
146
+
147
+ const commandPromise = cli.run('debug interactive-test');
148
+
149
+ await cli.on(/Enter value:/).respond('test\n');
150
+
151
+ const result = await commandPromise;
152
+ expect(result.exitCode).toBe(0);
153
+ ```
154
+
155
+ ## Status
156
+
157
+ The interactive prompt API is fully implemented and tested:
158
+ - ✅ `outputBuffer` is populated with all CLI output
159
+ - ✅ `expectOutput()` waits for patterns to appear
160
+ - ✅ `sendKeys()` sends input to process stdin
161
+ - ✅ `ResponseBuilder` provides the fluent `.on().respond()` API
162
+ - ✅ Unit tests in `cli-context.test.ts` and `cli-context-interactive.test.ts`
163
+
164
+ The API is ready to use for testing real interactive commands. The
165
+ patterns above are templates — they don't reference any actual CLI
166
+ command, so don't paste them verbatim into a test file expecting them
167
+ to run.
@@ -0,0 +1,152 @@
1
+ /**
2
+ * CLIContext Interactive Prompts Tests
3
+ *
4
+ * Tests for interactive prompt handling with persistent process
5
+ */
6
+
7
+ import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
8
+ import { CLIContext } from './cli-context';
9
+ import { type IntegrationTestContext, setupIntegrationTest } from './integration';
10
+
11
+ describe('CLIContext - Interactive Prompts', () => {
12
+ let ctx: IntegrationTestContext;
13
+ let cli: CLIContext;
14
+
15
+ beforeEach(async () => {
16
+ ctx = await setupIntegrationTest();
17
+ cli = await CLIContext.create('src/cli/index.ts', {
18
+ CELILO_DB_PATH: ctx.dbPath,
19
+ CELILO_DATA_DIR: ctx.dataDir,
20
+ });
21
+ });
22
+
23
+ afterEach(async () => {
24
+ await cli.dispose();
25
+ await ctx.cleanup();
26
+ });
27
+
28
+ describe('output capturing', () => {
29
+ test('captures command output in buffer', async () => {
30
+ await cli.run('module list');
31
+
32
+ // The output should be captured for expectOutput to search
33
+ await cli.expectOutput(/modules/i, { timeout: 1000 });
34
+ });
35
+
36
+ test('expectOutput waits for pattern to appear', async () => {
37
+ // Start a command that will produce output
38
+ const runPromise = cli.run('module list');
39
+
40
+ // Wait for output pattern
41
+ await cli.expectOutput(/No modules/i, { timeout: 2000 });
42
+
43
+ // Command should still complete successfully
44
+ const result = await runPromise;
45
+ expect(result.exitCode).toBe(0);
46
+ });
47
+
48
+ test('expectOutput throws on timeout if pattern not found', async () => {
49
+ await cli.run('module list');
50
+
51
+ // Try to find a pattern that doesn't exist
52
+ await expect(
53
+ cli.expectOutput(/THIS_PATTERN_DOES_NOT_EXIST/, { timeout: 500 }),
54
+ ).rejects.toThrow(/Timeout waiting for pattern/);
55
+ });
56
+
57
+ test('expectOutput works with regex patterns', async () => {
58
+ await cli.run('module list');
59
+
60
+ // Should match case-insensitive
61
+ await cli.expectOutput(/MODULES/i, { timeout: 1000 });
62
+ });
63
+
64
+ test('expectOutput works with string patterns', async () => {
65
+ await cli.run('module list');
66
+
67
+ // Should match exact string
68
+ await cli.expectOutput('No modules', { timeout: 1000 });
69
+ });
70
+ });
71
+
72
+ describe('ResponseBuilder API', () => {
73
+ test('on().respond() provides fluent API', () => {
74
+ // Should return ResponseBuilder
75
+ const builder = cli.on(/Enter hostname:/);
76
+ expect(builder).toBeDefined();
77
+ expect(typeof builder.respond).toBe('function');
78
+ });
79
+
80
+ test('ResponseBuilder waits for pattern then sends input', async () => {
81
+ // This test demonstrates the API even though we don't have
82
+ // a command that actually prompts for input yet
83
+
84
+ // The API should work when we have interactive commands
85
+ const builder = cli.on(/Would you like to continue/);
86
+ expect(builder).toBeDefined();
87
+ });
88
+ });
89
+
90
+ describe('sendKeys', () => {
91
+ test('sendKeys writes to process stdin', async () => {
92
+ // sendKeys should not throw
93
+ await cli.sendKeys('test input\n');
94
+ // If we get here, it didn't throw
95
+ expect(true).toBe(true);
96
+ });
97
+
98
+ test('sendKeys can be called multiple times', async () => {
99
+ await cli.sendKeys('line 1\n');
100
+ await cli.sendKeys('line 2\n');
101
+ await cli.sendKeys('line 3\n');
102
+
103
+ // If we get here, none of them threw
104
+ expect(true).toBe(true);
105
+ });
106
+ });
107
+
108
+ describe('output buffer management', () => {
109
+ test('output accumulates across multiple commands', async () => {
110
+ await cli.run('module list');
111
+ await cli.run('module list');
112
+
113
+ // Buffer should contain output from both commands
114
+ await cli.expectOutput(/modules/i);
115
+ });
116
+
117
+ test('can search for patterns from earlier commands', async () => {
118
+ await cli.run('module list');
119
+ await cli.run('system config list');
120
+
121
+ // Should still find pattern from first command
122
+ await cli.expectOutput(/modules/i);
123
+
124
+ // And pattern from second command
125
+ await cli.expectOutput(/config/i);
126
+ });
127
+ });
128
+
129
+ describe('real-world interactive scenarios', () => {
130
+ test('handles mixed JSON and non-JSON output', async () => {
131
+ // The CLI outputs both JSON responses (for protocol)
132
+ // and regular text (Database initialized, logs, etc.)
133
+
134
+ const result = await cli.run('module list');
135
+
136
+ // Should get JSON response
137
+ expect(result.exitCode).toBe(0);
138
+
139
+ // And should be able to find text output
140
+ await cli.expectOutput(/modules/i);
141
+ });
142
+
143
+ test('verbose mode shows all I/O', async () => {
144
+ cli.setVerbose(true);
145
+
146
+ await cli.run('module list');
147
+
148
+ // Verbose mode should log but not break functionality
149
+ await cli.expectOutput(/modules/i);
150
+ });
151
+ });
152
+ });
@@ -0,0 +1,66 @@
1
+ /**
2
+ * CLIContext Server Mode Tests
3
+ *
4
+ * Basic tests for persistent process functionality
5
+ */
6
+
7
+ import { describe, expect, test } from 'bun:test';
8
+ import { mkdtemp, rm } from 'node:fs/promises';
9
+ import { tmpdir } from 'node:os';
10
+ import { join } from 'node:path';
11
+ import { CLIContext } from './cli-context';
12
+
13
+ describe('CLIContext - Server Mode', () => {
14
+ test('starts persistent process successfully', async () => {
15
+ // Create temp directories for isolated test
16
+ const dbPath = join(await mkdtemp(join(tmpdir(), 'cli-test-')), 'test.db');
17
+ const dataDir = await mkdtemp(join(tmpdir(), 'cli-test-data-'));
18
+
19
+ const cli = await CLIContext.create('src/cli/index.ts', {
20
+ CELILO_DB_PATH: dbPath,
21
+ CELILO_DATA_DIR: dataDir,
22
+ });
23
+
24
+ try {
25
+ // If we get here, process started successfully
26
+ expect(cli).toBeDefined();
27
+
28
+ // Try running a simple command
29
+ const result = await cli.run('module list');
30
+ expect(result.exitCode).toBe(0);
31
+ } finally {
32
+ await cli.dispose();
33
+ // Cleanup
34
+ await rm(dbPath, { force: true });
35
+ await rm(dataDir, { recursive: true, force: true });
36
+ }
37
+ }, 15000); // 15 second timeout for this test
38
+
39
+ test('reuses same process for multiple commands', async () => {
40
+ const dbPath = join(await mkdtemp(join(tmpdir(), 'cli-test-')), 'test.db');
41
+ const dataDir = await mkdtemp(join(tmpdir(), 'cli-test-data-'));
42
+
43
+ const cli = await CLIContext.create('src/cli/index.ts', {
44
+ CELILO_DB_PATH: dbPath,
45
+ CELILO_DATA_DIR: dataDir,
46
+ });
47
+
48
+ try {
49
+ // Run multiple commands - should reuse same process
50
+ const result1 = await cli.run('module list');
51
+ expect(result1.exitCode).toBe(0);
52
+
53
+ const result2 = await cli.run('module list');
54
+ expect(result2.exitCode).toBe(0);
55
+
56
+ const result3 = await cli.run('module list');
57
+ expect(result3.exitCode).toBe(0);
58
+
59
+ // All commands executed without spawning new processes
60
+ } finally {
61
+ await cli.dispose();
62
+ await rm(dbPath, { force: true });
63
+ await rm(dataDir, { recursive: true, force: true });
64
+ }
65
+ }, 15000);
66
+ });