@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.
- package/README.md +1566 -0
- package/bin/celilo +16 -0
- package/drizzle/0000_complex_puma.sql +179 -0
- package/drizzle/0001_dizzy_wolfpack.sql +2 -0
- package/drizzle/0002_web_routes.sql +16 -0
- package/drizzle/0003_backup_storage.sql +32 -0
- package/drizzle/meta/0000_snapshot.json +1151 -0
- package/drizzle/meta/0001_snapshot.json +1167 -0
- package/drizzle/meta/0002_snapshot.json +1257 -0
- package/drizzle/meta/_journal.json +27 -0
- package/package.json +64 -0
- package/schemas/system_config.json +106 -0
- package/src/__integration__/container-services-cli.integration.test.ts +246 -0
- package/src/ansible/dependencies.test.ts +309 -0
- package/src/ansible/dependencies.ts +896 -0
- package/src/ansible/inventory.test.ts +463 -0
- package/src/ansible/inventory.ts +445 -0
- package/src/ansible/secrets.ts +222 -0
- package/src/ansible/validation.test.ts +92 -0
- package/src/ansible/validation.ts +272 -0
- package/src/api-clients/digitalocean.ts +94 -0
- package/src/api-clients/proxmox.ts +655 -0
- package/src/capabilities/logging-wrapper.test.ts +217 -0
- package/src/capabilities/lookup.test.ts +149 -0
- package/src/capabilities/lookup.ts +89 -0
- package/src/capabilities/public-web-helpers.test.ts +198 -0
- package/src/capabilities/public-web-publish.test.ts +458 -0
- package/src/capabilities/registration.test.ts +395 -0
- package/src/capabilities/registration.ts +200 -0
- package/src/capabilities/route-validation.test.ts +121 -0
- package/src/capabilities/route-validation.ts +96 -0
- package/src/capabilities/secret-ref.test.ts +313 -0
- package/src/capabilities/secret-validation.ts +157 -0
- package/src/capabilities/secrets.test.ts +750 -0
- package/src/capabilities/secrets.ts +244 -0
- package/src/capabilities/validation.test.ts +613 -0
- package/src/capabilities/validation.ts +160 -0
- package/src/capabilities/well-known.test.ts +238 -0
- package/src/capabilities/well-known.ts +222 -0
- package/src/cli/cli.test.ts +654 -0
- package/src/cli/command-registry.ts +742 -0
- package/src/cli/command-tree-parser.test.ts +180 -0
- package/src/cli/command-tree-parser.ts +193 -0
- package/src/cli/commands/backup-create.ts +137 -0
- package/src/cli/commands/backup-delete.ts +74 -0
- package/src/cli/commands/backup-import.ts +97 -0
- package/src/cli/commands/backup-list.ts +132 -0
- package/src/cli/commands/backup-name.ts +73 -0
- package/src/cli/commands/backup-prune.ts +98 -0
- package/src/cli/commands/backup-restore.ts +122 -0
- package/src/cli/commands/capability-info.ts +121 -0
- package/src/cli/commands/capability-list.ts +47 -0
- package/src/cli/commands/completion.ts +87 -0
- package/src/cli/commands/hook-run.ts +176 -0
- package/src/cli/commands/ipam.ts +607 -0
- package/src/cli/commands/machine-add.ts +235 -0
- package/src/cli/commands/machine-earmark.ts +82 -0
- package/src/cli/commands/machine-list.ts +77 -0
- package/src/cli/commands/machine-remove.ts +90 -0
- package/src/cli/commands/machine-status.ts +131 -0
- package/src/cli/commands/module-audit.ts +51 -0
- package/src/cli/commands/module-build.ts +60 -0
- package/src/cli/commands/module-config.ts +170 -0
- package/src/cli/commands/module-deploy.ts +71 -0
- package/src/cli/commands/module-generate.ts +236 -0
- package/src/cli/commands/module-health.ts +108 -0
- package/src/cli/commands/module-import.ts +80 -0
- package/src/cli/commands/module-list.ts +43 -0
- package/src/cli/commands/module-logs.ts +73 -0
- package/src/cli/commands/module-remove.ts +162 -0
- package/src/cli/commands/module-show.ts +208 -0
- package/src/cli/commands/module-status.ts +131 -0
- package/src/cli/commands/module-types.ts +189 -0
- package/src/cli/commands/module-upgrade.ts +192 -0
- package/src/cli/commands/package.ts +68 -0
- package/src/cli/commands/secret-list.ts +99 -0
- package/src/cli/commands/secret-set.ts +134 -0
- package/src/cli/commands/service-add-digitalocean.ts +133 -0
- package/src/cli/commands/service-add-proxmox.ts +342 -0
- package/src/cli/commands/service-config-get.ts +83 -0
- package/src/cli/commands/service-config-set.ts +145 -0
- package/src/cli/commands/service-list.ts +74 -0
- package/src/cli/commands/service-reconfigure.ts +230 -0
- package/src/cli/commands/service-remove.ts +103 -0
- package/src/cli/commands/service-verify.ts +240 -0
- package/src/cli/commands/status.ts +216 -0
- package/src/cli/commands/storage-add-local.ts +106 -0
- package/src/cli/commands/storage-add-s3.ts +114 -0
- package/src/cli/commands/storage-list.ts +72 -0
- package/src/cli/commands/storage-remove.ts +54 -0
- package/src/cli/commands/storage-set-default.ts +44 -0
- package/src/cli/commands/storage-verify.ts +54 -0
- package/src/cli/commands/system-config.ts +168 -0
- package/src/cli/commands/system-init.ts +314 -0
- package/src/cli/commands/system-secret-get.ts +98 -0
- package/src/cli/commands/system-secret-set.ts +76 -0
- package/src/cli/commands/system-vault-password.ts +34 -0
- package/src/cli/completion.test.ts +37 -0
- package/src/cli/completion.ts +482 -0
- package/src/cli/fuel-gauge.test.ts +208 -0
- package/src/cli/fuel-gauge.ts +405 -0
- package/src/cli/generate-zsh-completion.test.ts +95 -0
- package/src/cli/generate-zsh-completion.ts +497 -0
- package/src/cli/index.ts +1583 -0
- package/src/cli/interactive-config.test.ts +201 -0
- package/src/cli/interactive-config.ts +62 -0
- package/src/cli/parser.test.ts +227 -0
- package/src/cli/parser.ts +244 -0
- package/src/cli/prompts.test.ts +33 -0
- package/src/cli/prompts.ts +121 -0
- package/src/cli/types.ts +38 -0
- package/src/cli/validators.test.ts +235 -0
- package/src/cli/validators.ts +188 -0
- package/src/config/env.ts +41 -0
- package/src/config/paths.test.ts +172 -0
- package/src/config/paths.ts +108 -0
- package/src/db/client.ts +190 -0
- package/src/db/migrate.ts +30 -0
- package/src/db/schema.test.ts +221 -0
- package/src/db/schema.ts +434 -0
- package/src/hooks/capability-loader-firewall.test.ts +246 -0
- package/src/hooks/capability-loader.test.ts +100 -0
- package/src/hooks/capability-loader.ts +520 -0
- package/src/hooks/define-hook.test.ts +488 -0
- package/src/hooks/executor.test.ts +462 -0
- package/src/hooks/executor.ts +469 -0
- package/src/hooks/logger.test.ts +54 -0
- package/src/hooks/logger.ts +95 -0
- package/src/hooks/test-fixtures/failing-hook.ts +13 -0
- package/src/hooks/test-fixtures/no-default-hook.ts +6 -0
- package/src/hooks/test-fixtures/success-hook.ts +20 -0
- package/src/hooks/test-fixtures/unbranded-hook.ts +11 -0
- package/src/hooks/test-fixtures/void-hook.ts +13 -0
- package/src/hooks/types.ts +89 -0
- package/src/infrastructure/property-extractor.test.ts +194 -0
- package/src/infrastructure/property-extractor.ts +151 -0
- package/src/ipam/allocator.test.ts +442 -0
- package/src/ipam/allocator.ts +369 -0
- package/src/ipam/auto-allocator.test.ts +247 -0
- package/src/ipam/auto-allocator.ts +270 -0
- package/src/ipam/subnet-parser.test.ts +107 -0
- package/src/ipam/subnet-parser.ts +136 -0
- package/src/manifest/contracts/index.ts +61 -0
- package/src/manifest/contracts/v1.ts +118 -0
- package/src/manifest/json-schema-roundtrip.test.ts +99 -0
- package/src/manifest/schema.ts +367 -0
- package/src/manifest/template-validator.test.ts +231 -0
- package/src/manifest/template-validator.ts +322 -0
- package/src/manifest/validate.test.ts +1180 -0
- package/src/manifest/validate.ts +415 -0
- package/src/module/import.test.ts +355 -0
- package/src/module/import.ts +676 -0
- package/src/module/packaging/audit.ts +169 -0
- package/src/module/packaging/build.ts +228 -0
- package/src/module/packaging/checksum.ts +41 -0
- package/src/module/packaging/extract.ts +234 -0
- package/src/module/packaging/signature.ts +47 -0
- package/src/secrets/encryption.test.ts +284 -0
- package/src/secrets/encryption.ts +162 -0
- package/src/secrets/generators.test.ts +112 -0
- package/src/secrets/generators.ts +127 -0
- package/src/secrets/master-key.test.ts +159 -0
- package/src/secrets/master-key.ts +114 -0
- package/src/secrets/storage.test.ts +115 -0
- package/src/secrets/storage.ts +106 -0
- package/src/secrets/vault.test.ts +35 -0
- package/src/secrets/vault.ts +42 -0
- package/src/services/backup-create.ts +532 -0
- package/src/services/backup-metadata.ts +198 -0
- package/src/services/backup-restore.ts +229 -0
- package/src/services/backup-retention.ts +84 -0
- package/src/services/backup-storage.ts +281 -0
- package/src/services/build-stream.test.ts +122 -0
- package/src/services/build-stream.ts +201 -0
- package/src/services/config-interview.ts +694 -0
- package/src/services/container-service.test.ts +298 -0
- package/src/services/container-service.ts +401 -0
- package/src/services/cross-module-data-manager.test.ts +405 -0
- package/src/services/cross-module-data-manager.ts +412 -0
- package/src/services/deploy-ansible.ts +88 -0
- package/src/services/deploy-planner.ts +153 -0
- package/src/services/deploy-preflight.ts +274 -0
- package/src/services/deploy-ssh.ts +131 -0
- package/src/services/deploy-terraform.test.ts +55 -0
- package/src/services/deploy-terraform.ts +445 -0
- package/src/services/deploy-validation.ts +311 -0
- package/src/services/dns-auto-register.ts +211 -0
- package/src/services/health-runner.ts +184 -0
- package/src/services/infrastructure-selector.test.ts +485 -0
- package/src/services/infrastructure-selector.ts +245 -0
- package/src/services/infrastructure-variable-resolver.test.ts +751 -0
- package/src/services/infrastructure-variable-resolver.ts +234 -0
- package/src/services/machine-detector.ts +328 -0
- package/src/services/machine-pool.test.ts +405 -0
- package/src/services/machine-pool.ts +316 -0
- package/src/services/manifest-validation.ts +120 -0
- package/src/services/module-build.test.ts +290 -0
- package/src/services/module-build.ts +431 -0
- package/src/services/module-config.test.ts +237 -0
- package/src/services/module-config.ts +298 -0
- package/src/services/module-deploy.ts +862 -0
- package/src/services/module-types-drift.test.ts +73 -0
- package/src/services/module-types-generator.test.ts +288 -0
- package/src/services/module-types-generator.ts +189 -0
- package/src/services/proxmox-state-recovery.ts +140 -0
- package/src/services/schema-validation.ts +155 -0
- package/src/services/secret-schema-loader.test.ts +311 -0
- package/src/services/secret-schema-loader.ts +239 -0
- package/src/services/ssh-key-manager.test.ts +283 -0
- package/src/services/ssh-key-manager.ts +193 -0
- package/src/services/storage-providers/local.ts +105 -0
- package/src/services/storage-providers/s3.ts +182 -0
- package/src/services/storage-providers/types.ts +24 -0
- package/src/services/system-config-schema-types.ts +25 -0
- package/src/services/system-config-validator.test.ts +160 -0
- package/src/services/system-config-validator.ts +74 -0
- package/src/services/system-init.test.ts +153 -0
- package/src/services/system-init.ts +253 -0
- package/src/services/terraform-safety.ts +174 -0
- package/src/services/zone-detector.test.ts +110 -0
- package/src/services/zone-detector.ts +102 -0
- package/src/services/zone-policy.test.ts +97 -0
- package/src/services/zone-policy.ts +126 -0
- package/src/templates/generator.test.ts +645 -0
- package/src/templates/generator.ts +1119 -0
- package/src/templates/types.ts +62 -0
- package/src/test-utils/INTERACTIVE_PROMPTS.md +167 -0
- package/src/test-utils/cli-context-interactive.test.ts +152 -0
- package/src/test-utils/cli-context-server.test.ts +66 -0
- package/src/test-utils/cli-context.test.ts +273 -0
- package/src/test-utils/cli-context.ts +677 -0
- package/src/test-utils/cli-result.test.ts +282 -0
- package/src/test-utils/cli-result.ts +241 -0
- package/src/test-utils/cli.ts +55 -0
- package/src/test-utils/completion-harness.test.ts +126 -0
- package/src/test-utils/completion-harness.ts +82 -0
- package/src/test-utils/database.test.ts +182 -0
- package/src/test-utils/database.ts +126 -0
- package/src/test-utils/filesystem.test.ts +208 -0
- package/src/test-utils/filesystem.ts +142 -0
- package/src/test-utils/fixtures.test.ts +123 -0
- package/src/test-utils/fixtures.ts +160 -0
- package/src/test-utils/golden-diff.ts +197 -0
- package/src/test-utils/index.ts +77 -0
- package/src/test-utils/integration.ts +81 -0
- package/src/test-utils/module-fixtures.ts +468 -0
- package/src/test-utils/modules.test.ts +144 -0
- package/src/test-utils/modules.ts +183 -0
- package/src/test-utils/setup-test-db.ts +90 -0
- package/src/test-utils/value-extractor.test.ts +231 -0
- package/src/test-utils/value-extractor.ts +228 -0
- package/src/types/infrastructure.ts +157 -0
- package/src/utils/shell.test.ts +365 -0
- package/src/utils/shell.ts +159 -0
- package/src/validation/schemas.ts +166 -0
- package/src/variables/ansible-resolver.test.ts +142 -0
- package/src/variables/ansible-resolver.ts +69 -0
- package/src/variables/capability-self-ref.test.ts +220 -0
- package/src/variables/context.test.ts +1265 -0
- package/src/variables/context.ts +624 -0
- package/src/variables/declarative-derivation.test.ts +743 -0
- package/src/variables/declarative-derivation.ts +200 -0
- package/src/variables/parser.test.ts +231 -0
- package/src/variables/parser.ts +76 -0
- package/src/variables/resolver.test.ts +458 -0
- package/src/variables/resolver.ts +282 -0
- 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
|
+
});
|