@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,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module Test Utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides functions for importing and cleaning up test modules.
|
|
5
|
+
* Simplifies integration test setup/teardown.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { eq } from 'drizzle-orm';
|
|
9
|
+
import type { DbClient } from '../db/client';
|
|
10
|
+
import { modules } from '../db/schema';
|
|
11
|
+
import { importModule } from '../module/import';
|
|
12
|
+
import { setModuleConfigValue } from '../services/module-config';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Options for importing test modules
|
|
16
|
+
*/
|
|
17
|
+
export interface ImportTestModuleOptions {
|
|
18
|
+
/**
|
|
19
|
+
* Module configuration (key-value pairs)
|
|
20
|
+
*/
|
|
21
|
+
config?: Record<string, string | number | boolean>;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Module secrets (will be encrypted)
|
|
25
|
+
*/
|
|
26
|
+
secrets?: Record<string, string>;
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Skip validation (useful for testing error cases)
|
|
30
|
+
*/
|
|
31
|
+
skipValidation?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Import test module from fixtures
|
|
36
|
+
*
|
|
37
|
+
* Imports module, applies configuration, and validates.
|
|
38
|
+
* Use for integration tests that need real modules.
|
|
39
|
+
*
|
|
40
|
+
* @param modulePath - Path to module directory
|
|
41
|
+
* @param db - Test database
|
|
42
|
+
* @param options - Import options
|
|
43
|
+
* @returns Module ID
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```typescript
|
|
47
|
+
* const moduleId = await importTestModule(
|
|
48
|
+
* './test-fixtures/modules/artifact-test',
|
|
49
|
+
* db,
|
|
50
|
+
* {
|
|
51
|
+
* config: { vmid: '100', hostname: 'test' },
|
|
52
|
+
* secrets: { api_key: 'test-key' }
|
|
53
|
+
* }
|
|
54
|
+
* );
|
|
55
|
+
* await cleanupTestModule(moduleId, db);
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
export async function importTestModule(
|
|
59
|
+
modulePath: string,
|
|
60
|
+
db: DbClient,
|
|
61
|
+
options: ImportTestModuleOptions = {},
|
|
62
|
+
): Promise<string> {
|
|
63
|
+
// Import module
|
|
64
|
+
const result = await importModule({ sourcePath: modulePath, db });
|
|
65
|
+
|
|
66
|
+
// Check if import was successful
|
|
67
|
+
if (!result.success) {
|
|
68
|
+
throw new Error(`Failed to import module: ${result.error}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Apply configuration if provided
|
|
72
|
+
if (options.config) {
|
|
73
|
+
for (const [key, value] of Object.entries(options.config)) {
|
|
74
|
+
await setModuleConfigValue(result.moduleId, key, String(value), db);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// TODO: Apply secrets when secret management is implemented
|
|
79
|
+
if (options.secrets) {
|
|
80
|
+
// For now, secrets are not yet implemented
|
|
81
|
+
// This will be added in the future
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return result.moduleId;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Clean up test module
|
|
89
|
+
*
|
|
90
|
+
* Deletes module and all related data (configs, capabilities, secrets).
|
|
91
|
+
* Use in test cleanup to prevent state leakage.
|
|
92
|
+
*
|
|
93
|
+
* @param moduleId - Module ID to delete
|
|
94
|
+
* @param db - Test database
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```typescript
|
|
98
|
+
* const moduleId = await importTestModule('./test-fixtures/modules/artifact-test', db);
|
|
99
|
+
* // ... tests ...
|
|
100
|
+
* await cleanupTestModule(moduleId, db);
|
|
101
|
+
* ```
|
|
102
|
+
*/
|
|
103
|
+
export async function cleanupTestModule(moduleId: string, db: DbClient): Promise<void> {
|
|
104
|
+
await db.delete(modules).where(eq(modules.id, moduleId));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Import multiple test modules
|
|
109
|
+
*
|
|
110
|
+
* Imports multiple modules in sequence.
|
|
111
|
+
* Useful for tests that need multiple modules.
|
|
112
|
+
*
|
|
113
|
+
* @param moduleSpecs - Array of module specs
|
|
114
|
+
* @param db - Test database
|
|
115
|
+
* @returns Array of module IDs
|
|
116
|
+
*
|
|
117
|
+
* @example
|
|
118
|
+
* ```typescript
|
|
119
|
+
* const [module1, module2] = await importMultipleTestModules([
|
|
120
|
+
* { path: './test-fixtures/modules/artifact-test' },
|
|
121
|
+
* { path: './test-fixtures/modules/caddy', config: { hostname: 'www' } }
|
|
122
|
+
* ], db);
|
|
123
|
+
* await cleanupMultipleTestModules([module1, module2], db);
|
|
124
|
+
* ```
|
|
125
|
+
*/
|
|
126
|
+
export async function importMultipleTestModules(
|
|
127
|
+
moduleSpecs: Array<{
|
|
128
|
+
path: string;
|
|
129
|
+
options?: ImportTestModuleOptions;
|
|
130
|
+
}>,
|
|
131
|
+
db: DbClient,
|
|
132
|
+
): Promise<string[]> {
|
|
133
|
+
const moduleIds: string[] = [];
|
|
134
|
+
for (const spec of moduleSpecs) {
|
|
135
|
+
const moduleId = await importTestModule(spec.path, db, spec.options);
|
|
136
|
+
moduleIds.push(moduleId);
|
|
137
|
+
}
|
|
138
|
+
return moduleIds;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Clean up multiple test modules
|
|
143
|
+
*
|
|
144
|
+
* @param moduleIds - Array of module IDs to delete
|
|
145
|
+
* @param db - Test database
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```typescript
|
|
149
|
+
* const moduleIds = await importMultipleTestModules([...], db);
|
|
150
|
+
* // ... tests ...
|
|
151
|
+
* await cleanupMultipleTestModules(moduleIds, db);
|
|
152
|
+
* ```
|
|
153
|
+
*/
|
|
154
|
+
export async function cleanupMultipleTestModules(moduleIds: string[], db: DbClient): Promise<void> {
|
|
155
|
+
for (const moduleId of moduleIds) {
|
|
156
|
+
await cleanupTestModule(moduleId, db);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Get module state
|
|
162
|
+
*
|
|
163
|
+
* Retrieves current module state from database.
|
|
164
|
+
* Useful for asserting state transitions in tests.
|
|
165
|
+
*
|
|
166
|
+
* @param moduleId - Module ID
|
|
167
|
+
* @param db - Test database
|
|
168
|
+
* @returns Module state or null if not found
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* ```typescript
|
|
172
|
+
* const state = await getModuleState('artifact-test', db);
|
|
173
|
+
* expect(state).toBe('IMPORTED');
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
export async function getModuleState(moduleId: string, db: DbClient): Promise<string | null> {
|
|
177
|
+
const result = await db
|
|
178
|
+
.select({ state: modules.state })
|
|
179
|
+
.from(modules)
|
|
180
|
+
.where(eq(modules.id, moduleId));
|
|
181
|
+
|
|
182
|
+
return result.length > 0 ? result[0].state : null;
|
|
183
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { unlink } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { migrate } from 'drizzle-orm/bun-sqlite/migrator';
|
|
5
|
+
import { type DbClient, createDbClient } from '../db/client';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Find migrations folder relative to current working directory
|
|
9
|
+
*/
|
|
10
|
+
function findMigrationsFolder(): string {
|
|
11
|
+
// Try common locations
|
|
12
|
+
const candidates = [
|
|
13
|
+
'./drizzle', // Running from backend directory
|
|
14
|
+
'./backend/drizzle', // Running from celilo directory
|
|
15
|
+
join(process.cwd(), 'drizzle'), // Absolute from backend
|
|
16
|
+
join(process.cwd(), 'backend', 'drizzle'), // Absolute from celilo
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
for (const candidate of candidates) {
|
|
20
|
+
const metaPath = join(candidate, 'meta', '_journal.json');
|
|
21
|
+
if (existsSync(metaPath)) {
|
|
22
|
+
return candidate;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
throw new Error(`Could not find drizzle migrations folder. Tried: ${candidates.join(', ')}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Setup test database with real migrations
|
|
31
|
+
*
|
|
32
|
+
* This helper ensures tests use the same schema as production by running
|
|
33
|
+
* actual migrations instead of manual CREATE TABLE statements.
|
|
34
|
+
*
|
|
35
|
+
* Benefits:
|
|
36
|
+
* - Single source of truth for schema
|
|
37
|
+
* - Tests automatically use latest schema
|
|
38
|
+
* - Validates migration correctness
|
|
39
|
+
*
|
|
40
|
+
* @param testDbPath - Path to test database file
|
|
41
|
+
* @returns Database client instance
|
|
42
|
+
*/
|
|
43
|
+
export async function setupTestDatabase(testDbPath: string) {
|
|
44
|
+
// Create database client
|
|
45
|
+
const db = createDbClient({ path: testDbPath });
|
|
46
|
+
|
|
47
|
+
// Find and run migrations
|
|
48
|
+
const migrationsFolder = findMigrationsFolder();
|
|
49
|
+
await migrate(db, { migrationsFolder });
|
|
50
|
+
|
|
51
|
+
return db;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Cleanup test database
|
|
56
|
+
*
|
|
57
|
+
* @param db - Database client to close
|
|
58
|
+
* @param testDbPath - Path to test database file to delete
|
|
59
|
+
*/
|
|
60
|
+
export async function cleanupTestDatabase(db: DbClient, testDbPath: string) {
|
|
61
|
+
// Close database connection
|
|
62
|
+
db.$client.close();
|
|
63
|
+
|
|
64
|
+
// Delete test database file
|
|
65
|
+
if (existsSync(testDbPath)) {
|
|
66
|
+
await unlink(testDbPath);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Also delete WAL and SHM files if they exist
|
|
70
|
+
const walPath = `${testDbPath}-wal`;
|
|
71
|
+
const shmPath = `${testDbPath}-shm`;
|
|
72
|
+
|
|
73
|
+
if (existsSync(walPath)) {
|
|
74
|
+
await unlink(walPath);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (existsSync(shmPath)) {
|
|
78
|
+
await unlink(shmPath);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Generate unique test database path
|
|
84
|
+
*
|
|
85
|
+
* @param prefix - Optional prefix for the database file (defaults to 'test-celilo')
|
|
86
|
+
* @returns Unique database file path
|
|
87
|
+
*/
|
|
88
|
+
export function generateTestDbPath(prefix = 'test-celilo'): string {
|
|
89
|
+
return `./${prefix}-${Date.now()}-${Math.random()}.db`;
|
|
90
|
+
}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Value Extractor Tests
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
|
|
6
|
+
import { existsSync } from 'node:fs';
|
|
7
|
+
import { mkdir, rm, writeFile } from 'node:fs/promises';
|
|
8
|
+
import {
|
|
9
|
+
compareExtractedValues,
|
|
10
|
+
extractAnsibleValues,
|
|
11
|
+
extractTerraformValues,
|
|
12
|
+
} from './value-extractor';
|
|
13
|
+
|
|
14
|
+
const TEST_DIR = './test-value-extractor-temp';
|
|
15
|
+
|
|
16
|
+
describe('Value Extractor', () => {
|
|
17
|
+
beforeEach(async () => {
|
|
18
|
+
if (existsSync(TEST_DIR)) {
|
|
19
|
+
await rm(TEST_DIR, { recursive: true });
|
|
20
|
+
}
|
|
21
|
+
await mkdir(TEST_DIR, { recursive: true });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
afterEach(async () => {
|
|
25
|
+
if (existsSync(TEST_DIR)) {
|
|
26
|
+
await rm(TEST_DIR, { recursive: true });
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
describe('extractTerraformValues', () => {
|
|
31
|
+
test('should extract all terraform values correctly', async () => {
|
|
32
|
+
const terraformContent = `
|
|
33
|
+
resource "proxmox_lxc" "homebridge" {
|
|
34
|
+
target_node = var.node_name
|
|
35
|
+
vmid = 2110
|
|
36
|
+
hostname = "iot"
|
|
37
|
+
ostemplate = var.lxc_template_id
|
|
38
|
+
|
|
39
|
+
cores = 2
|
|
40
|
+
memory = 1024
|
|
41
|
+
|
|
42
|
+
network {
|
|
43
|
+
name = "eth0"
|
|
44
|
+
bridge = "vmbr0"
|
|
45
|
+
tag = 192
|
|
46
|
+
ip = "192.168.0.110/24"
|
|
47
|
+
gw = "192.168.0.254"
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
rootfs {
|
|
51
|
+
storage = "datacenter"
|
|
52
|
+
size = "20G"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
`;
|
|
56
|
+
|
|
57
|
+
const terraformPath = `${TEST_DIR}/main.tf`;
|
|
58
|
+
await writeFile(terraformPath, terraformContent);
|
|
59
|
+
|
|
60
|
+
const values = await extractTerraformValues(terraformPath);
|
|
61
|
+
|
|
62
|
+
expect(values.vmid).toBe(2110);
|
|
63
|
+
expect(values.hostname).toBe('iot');
|
|
64
|
+
expect(values.ip).toBe('192.168.0.110/24');
|
|
65
|
+
expect(values.gateway).toBe('192.168.0.254');
|
|
66
|
+
expect(values.vlan).toBe(192);
|
|
67
|
+
expect(values.cores).toBe(2);
|
|
68
|
+
expect(values.memory).toBe(1024);
|
|
69
|
+
expect(values.storage).toBe('datacenter');
|
|
70
|
+
expect(values.rootfs_size).toBe('20G');
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('should handle missing values gracefully', async () => {
|
|
74
|
+
const terraformContent = `
|
|
75
|
+
resource "proxmox_lxc" "homebridge" {
|
|
76
|
+
vmid = 2110
|
|
77
|
+
hostname = "iot"
|
|
78
|
+
}
|
|
79
|
+
`;
|
|
80
|
+
|
|
81
|
+
const terraformPath = `${TEST_DIR}/main.tf`;
|
|
82
|
+
await writeFile(terraformPath, terraformContent);
|
|
83
|
+
|
|
84
|
+
const values = await extractTerraformValues(terraformPath);
|
|
85
|
+
|
|
86
|
+
expect(values.vmid).toBe(2110);
|
|
87
|
+
expect(values.hostname).toBe('iot');
|
|
88
|
+
expect(values.ip).toBeUndefined();
|
|
89
|
+
expect(values.cores).toBeUndefined();
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
describe('extractAnsibleValues', () => {
|
|
94
|
+
test('should extract all ansible values correctly', async () => {
|
|
95
|
+
// Create proper inventory structure
|
|
96
|
+
await mkdir(`${TEST_DIR}/ansible/inventory/group_vars`, { recursive: true });
|
|
97
|
+
|
|
98
|
+
// Create group_vars/all.yml with system config (not a playbook)
|
|
99
|
+
const groupVarsContent = `---
|
|
100
|
+
# System-wide variables available to all hosts
|
|
101
|
+
dns_primary: 192.168.0.1
|
|
102
|
+
dns_fallback: 8.8.8.8 1.1.1.1
|
|
103
|
+
routing_internal_gateway: 192.168.0.254
|
|
104
|
+
`;
|
|
105
|
+
|
|
106
|
+
await writeFile(`${TEST_DIR}/ansible/inventory/group_vars/all.yml`, groupVarsContent);
|
|
107
|
+
|
|
108
|
+
const values = await extractAnsibleValues(`${TEST_DIR}/ansible`);
|
|
109
|
+
|
|
110
|
+
expect(values.dns_primary).toBe('192.168.0.1');
|
|
111
|
+
expect(values.dns_fallback).toBe('8.8.8.8 1.1.1.1');
|
|
112
|
+
expect(values.gateway).toBe('192.168.0.254');
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe('compareExtractedValues', () => {
|
|
117
|
+
test('should pass when all values match', async () => {
|
|
118
|
+
// Setup test files
|
|
119
|
+
await mkdir(`${TEST_DIR}/terraform`, { recursive: true });
|
|
120
|
+
await mkdir(`${TEST_DIR}/ansible/inventory/group_vars`, { recursive: true });
|
|
121
|
+
|
|
122
|
+
const terraformContent = `
|
|
123
|
+
resource "proxmox_lxc" "homebridge" {
|
|
124
|
+
vmid = 2110
|
|
125
|
+
hostname = "iot"
|
|
126
|
+
cores = 1
|
|
127
|
+
memory = 1024
|
|
128
|
+
|
|
129
|
+
network {
|
|
130
|
+
tag = 192
|
|
131
|
+
ip = "192.168.0.110/24"
|
|
132
|
+
gw = "192.168.0.254"
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
rootfs {
|
|
136
|
+
storage = "datacenter"
|
|
137
|
+
size = "20G"
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
`;
|
|
141
|
+
|
|
142
|
+
const groupVarsContent = `---
|
|
143
|
+
# System-wide variables available to all hosts
|
|
144
|
+
dns_primary: 192.168.0.1
|
|
145
|
+
dns_fallback: 8.8.8.8 1.1.1.1
|
|
146
|
+
routing_internal_gateway: 192.168.0.254
|
|
147
|
+
`;
|
|
148
|
+
|
|
149
|
+
await writeFile(`${TEST_DIR}/terraform/main.tf`, terraformContent);
|
|
150
|
+
await writeFile(`${TEST_DIR}/ansible/inventory/group_vars/all.yml`, groupVarsContent);
|
|
151
|
+
|
|
152
|
+
const expectedValues = {
|
|
153
|
+
terraform: {
|
|
154
|
+
vmid: 2110,
|
|
155
|
+
hostname: 'iot',
|
|
156
|
+
ip: '192.168.0.110/24',
|
|
157
|
+
gateway: '192.168.0.254',
|
|
158
|
+
vlan: 192,
|
|
159
|
+
cores: 1,
|
|
160
|
+
memory: 1024,
|
|
161
|
+
storage: 'datacenter',
|
|
162
|
+
rootfs_size: '20G',
|
|
163
|
+
},
|
|
164
|
+
ansible: {
|
|
165
|
+
dns_primary: '192.168.0.1',
|
|
166
|
+
dns_fallback: '8.8.8.8 1.1.1.1',
|
|
167
|
+
gateway: '192.168.0.254',
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
const result = await compareExtractedValues(TEST_DIR, expectedValues);
|
|
172
|
+
|
|
173
|
+
expect(result.pass).toBe(true);
|
|
174
|
+
expect(result.mismatches).toHaveLength(0);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
test('should detect mismatches when values differ', async () => {
|
|
178
|
+
// Setup test files with wrong values
|
|
179
|
+
await mkdir(`${TEST_DIR}/terraform`, { recursive: true });
|
|
180
|
+
await mkdir(`${TEST_DIR}/ansible/inventory/group_vars`, { recursive: true });
|
|
181
|
+
|
|
182
|
+
const terraformContent = `
|
|
183
|
+
resource "proxmox_lxc" "homebridge" {
|
|
184
|
+
vmid = 9999
|
|
185
|
+
hostname = "wrong-host"
|
|
186
|
+
cores = 1
|
|
187
|
+
}
|
|
188
|
+
`;
|
|
189
|
+
|
|
190
|
+
const groupVarsContent = `---
|
|
191
|
+
# System-wide variables with wrong values
|
|
192
|
+
dns_primary: 1.1.1.1
|
|
193
|
+
`;
|
|
194
|
+
|
|
195
|
+
await writeFile(`${TEST_DIR}/terraform/main.tf`, terraformContent);
|
|
196
|
+
await writeFile(`${TEST_DIR}/ansible/inventory/group_vars/all.yml`, groupVarsContent);
|
|
197
|
+
|
|
198
|
+
const expectedValues = {
|
|
199
|
+
terraform: {
|
|
200
|
+
vmid: 2110,
|
|
201
|
+
hostname: 'iot',
|
|
202
|
+
cores: 1,
|
|
203
|
+
},
|
|
204
|
+
ansible: {
|
|
205
|
+
dns_primary: '192.168.0.1',
|
|
206
|
+
},
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
const result = await compareExtractedValues(TEST_DIR, expectedValues);
|
|
210
|
+
|
|
211
|
+
expect(result.pass).toBe(false);
|
|
212
|
+
expect(result.mismatches).toHaveLength(3);
|
|
213
|
+
|
|
214
|
+
// Check specific mismatches
|
|
215
|
+
const vmidMismatch = result.mismatches.find((m) => m.path === 'terraform.vmid');
|
|
216
|
+
expect(vmidMismatch).toBeDefined();
|
|
217
|
+
expect(vmidMismatch?.expected).toBe(2110);
|
|
218
|
+
expect(vmidMismatch?.actual).toBe(9999);
|
|
219
|
+
|
|
220
|
+
const hostnameMismatch = result.mismatches.find((m) => m.path === 'terraform.hostname');
|
|
221
|
+
expect(hostnameMismatch).toBeDefined();
|
|
222
|
+
expect(hostnameMismatch?.expected).toBe('iot');
|
|
223
|
+
expect(hostnameMismatch?.actual).toBe('wrong-host');
|
|
224
|
+
|
|
225
|
+
const dnsMismatch = result.mismatches.find((m) => m.path === 'ansible.dns_primary');
|
|
226
|
+
expect(dnsMismatch).toBeDefined();
|
|
227
|
+
expect(dnsMismatch?.expected).toBe('192.168.0.1');
|
|
228
|
+
expect(dnsMismatch?.actual).toBe('1.1.1.1');
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
});
|