@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,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
+ });