@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,81 @@
|
|
|
1
|
+
import { existsSync, rmSync } from 'node:fs';
|
|
2
|
+
import { mkdtemp } from 'node:fs/promises';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { setupTestDatabaseFile } from './database';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Integration test context with isolated database and data directory
|
|
9
|
+
*/
|
|
10
|
+
export interface IntegrationTestContext {
|
|
11
|
+
/** Path to test database file */
|
|
12
|
+
dbPath: string;
|
|
13
|
+
/** Path to test data directory */
|
|
14
|
+
dataDir: string;
|
|
15
|
+
/** CLI command prefix with environment variables */
|
|
16
|
+
cli: string;
|
|
17
|
+
/** Cleanup function (closes DB and removes temp directories) */
|
|
18
|
+
cleanup: () => Promise<void>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Setup complete integration test environment
|
|
23
|
+
*
|
|
24
|
+
* Creates:
|
|
25
|
+
* - Isolated test database (file-based for CLI access)
|
|
26
|
+
* - Isolated data directory
|
|
27
|
+
* - Runs migrations
|
|
28
|
+
* - Returns CLI command prefix with proper env vars
|
|
29
|
+
*
|
|
30
|
+
* @returns Integration test context with cleanup function
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```typescript
|
|
34
|
+
* let ctx: IntegrationTestContext;
|
|
35
|
+
*
|
|
36
|
+
* beforeEach(async () => {
|
|
37
|
+
* ctx = await setupIntegrationTest();
|
|
38
|
+
* });
|
|
39
|
+
*
|
|
40
|
+
* afterEach(async () => {
|
|
41
|
+
* await ctx.cleanup();
|
|
42
|
+
* });
|
|
43
|
+
*
|
|
44
|
+
* test('my test', () => {
|
|
45
|
+
* const output = runCli(ctx.cli, 'system config get');
|
|
46
|
+
* expect(output).toContain('...');
|
|
47
|
+
* });
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export async function setupIntegrationTest(): Promise<IntegrationTestContext> {
|
|
51
|
+
// Setup isolated database
|
|
52
|
+
const testDb = await setupTestDatabaseFile();
|
|
53
|
+
const dbPath = testDb.path;
|
|
54
|
+
const dbCleanup = testDb.cleanup;
|
|
55
|
+
|
|
56
|
+
// Setup isolated data directory (use mkdtemp to avoid timestamp collisions in parallel tests)
|
|
57
|
+
const dataDir = await mkdtemp(join(tmpdir(), 'celilo-test-'));
|
|
58
|
+
|
|
59
|
+
// CLI with isolated environment
|
|
60
|
+
const cli = `CELILO_DB_PATH="${dbPath}" CELILO_DATA_DIR="${dataDir}" bun run src/cli/index.ts`;
|
|
61
|
+
|
|
62
|
+
// Note: Migrations are auto-run by createDbClient() in setupTestDatabaseFile()
|
|
63
|
+
|
|
64
|
+
// Unified cleanup function
|
|
65
|
+
const cleanup = async () => {
|
|
66
|
+
// Cleanup data directory
|
|
67
|
+
if (existsSync(dataDir)) {
|
|
68
|
+
rmSync(dataDir, { recursive: true, force: true });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Cleanup database
|
|
72
|
+
await dbCleanup();
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
return {
|
|
76
|
+
dbPath,
|
|
77
|
+
dataDir,
|
|
78
|
+
cli,
|
|
79
|
+
cleanup,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
@@ -0,0 +1,468 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module-Specific Test Fixtures
|
|
3
|
+
*
|
|
4
|
+
* Factory functions that create pre-configured test environments for specific modules.
|
|
5
|
+
* Each fixture knows what config keys the module needs and sets up dependencies.
|
|
6
|
+
*
|
|
7
|
+
* Reduces test setup from 30+ lines to a single function call.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { CLIContext } from './cli-context';
|
|
11
|
+
import { type IntegrationTestContext, setupIntegrationTest } from './integration';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Base fixture options
|
|
15
|
+
*/
|
|
16
|
+
interface BaseFixtureOptions {
|
|
17
|
+
/** Whether to run module generate (default: false) */
|
|
18
|
+
generated?: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Homebridge module fixture options
|
|
23
|
+
*/
|
|
24
|
+
interface HomebridgeModuleOptions extends BaseFixtureOptions {
|
|
25
|
+
/** Hostname (default: 'iot') */
|
|
26
|
+
hostname?: string;
|
|
27
|
+
/** Bridge name (default: 'Home Bridge') */
|
|
28
|
+
bridgeName?: string;
|
|
29
|
+
/** Bridge username MAC address (default: generated) */
|
|
30
|
+
bridgeUsername?: string;
|
|
31
|
+
/** Bridge port (default: 51826) */
|
|
32
|
+
bridgePort?: number;
|
|
33
|
+
/** Bridge PIN (default: '031-45-154') */
|
|
34
|
+
bridgePin?: string;
|
|
35
|
+
/** Setup full system config for generation (default: false) */
|
|
36
|
+
withSystemConfig?: boolean;
|
|
37
|
+
/** Setup mock infrastructure for generation (default: false) */
|
|
38
|
+
withMockInfrastructure?: boolean;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Homebridge module fixture result
|
|
43
|
+
*/
|
|
44
|
+
interface HomebridgeModuleFixture {
|
|
45
|
+
/** CLI context (persistent process) */
|
|
46
|
+
cli: CLIContext;
|
|
47
|
+
/** Module ID */
|
|
48
|
+
moduleId: string;
|
|
49
|
+
/** Configuration used */
|
|
50
|
+
config: Record<string, unknown>;
|
|
51
|
+
/** Integration test context (DB, data dir) */
|
|
52
|
+
context: IntegrationTestContext;
|
|
53
|
+
/** Cleanup function */
|
|
54
|
+
cleanup: () => Promise<void>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Homebridge module fixture
|
|
59
|
+
*
|
|
60
|
+
* Sets up Homebridge module with all required configuration.
|
|
61
|
+
* Optionally runs module generate to create Terraform/Ansible files.
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* ```typescript
|
|
65
|
+
* const { cli, moduleId, config, cleanup } = await homebridgeModule({
|
|
66
|
+
* generated: true,
|
|
67
|
+
* bridgePort: 51826,
|
|
68
|
+
* hostname: 'iot'
|
|
69
|
+
* });
|
|
70
|
+
*
|
|
71
|
+
* // Module is configured and generated - run tests
|
|
72
|
+
* await cli.run('module status homebridge').expectSuccess();
|
|
73
|
+
*
|
|
74
|
+
* // Cleanup
|
|
75
|
+
* await cleanup();
|
|
76
|
+
* ```
|
|
77
|
+
*/
|
|
78
|
+
export async function homebridgeModule(
|
|
79
|
+
options: HomebridgeModuleOptions = {},
|
|
80
|
+
): Promise<HomebridgeModuleFixture> {
|
|
81
|
+
// Setup integration test environment
|
|
82
|
+
const ctx = await setupIntegrationTest();
|
|
83
|
+
|
|
84
|
+
// Setup mock infrastructure if requested (needed for generation)
|
|
85
|
+
if (options.withMockInfrastructure) {
|
|
86
|
+
const { getDb } = await import('../db/client');
|
|
87
|
+
const { containerServices } = await import('../db/schema');
|
|
88
|
+
const originalDbPath = process.env.CELILO_DB_PATH;
|
|
89
|
+
process.env.CELILO_DB_PATH = ctx.dbPath;
|
|
90
|
+
try {
|
|
91
|
+
const db = getDb();
|
|
92
|
+
await db.insert(containerServices).values({
|
|
93
|
+
id: 'test-proxmox',
|
|
94
|
+
serviceId: 'test-proxmox',
|
|
95
|
+
name: 'Test Proxmox',
|
|
96
|
+
providerName: 'proxmox',
|
|
97
|
+
zones: ['internal', 'dmz', 'app', 'secure'],
|
|
98
|
+
apiCredentialsEncrypted: JSON.stringify({
|
|
99
|
+
encryptedValue: 'dummy',
|
|
100
|
+
iv: 'dummy',
|
|
101
|
+
authTag: 'dummy',
|
|
102
|
+
}),
|
|
103
|
+
providerConfig: {
|
|
104
|
+
default_target_node: 'pve',
|
|
105
|
+
lxc_template: 'local:vztmpl/ubuntu-22.04-standard_22.04-1_amd64.tar.zst',
|
|
106
|
+
storage: 'local-lvm',
|
|
107
|
+
},
|
|
108
|
+
verified: true,
|
|
109
|
+
verifiedAt: new Date(),
|
|
110
|
+
verificationError: null,
|
|
111
|
+
createdAt: new Date(),
|
|
112
|
+
updatedAt: new Date(),
|
|
113
|
+
});
|
|
114
|
+
} finally {
|
|
115
|
+
if (originalDbPath) {
|
|
116
|
+
process.env.CELILO_DB_PATH = originalDbPath;
|
|
117
|
+
} else {
|
|
118
|
+
process.env.CELILO_DB_PATH = undefined;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Create CLI context with integration test env
|
|
124
|
+
const cli = await CLIContext.create('src/cli/index.ts', {
|
|
125
|
+
CELILO_DB_PATH: ctx.dbPath,
|
|
126
|
+
CELILO_DATA_DIR: ctx.dataDir,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const moduleId = 'homebridge';
|
|
130
|
+
|
|
131
|
+
// Setup system config if requested (needed for generation)
|
|
132
|
+
if (options.withSystemConfig) {
|
|
133
|
+
// DNS configuration
|
|
134
|
+
(await cli.run('system config set dns.primary 192.168.0.1')).expectSuccess();
|
|
135
|
+
(await cli.run(`system config set dns.fallback '8.8.8.8 1.1.1.1'`)).expectSuccess();
|
|
136
|
+
|
|
137
|
+
// Network configuration
|
|
138
|
+
(await cli.run('system config set network.bridge vmbr0')).expectSuccess();
|
|
139
|
+
(await cli.run('system config set network.dmz.vlan 10')).expectSuccess();
|
|
140
|
+
(await cli.run('system config set network.dmz.gateway 10.0.10.1')).expectSuccess();
|
|
141
|
+
(await cli.run('system config set network.dmz.subnet 10.0.10.0/24')).expectSuccess();
|
|
142
|
+
(await cli.run('system config set network.app.vlan 20')).expectSuccess();
|
|
143
|
+
(await cli.run('system config set network.app.gateway 10.0.20.1')).expectSuccess();
|
|
144
|
+
(await cli.run('system config set network.app.subnet 10.0.20.0/24')).expectSuccess();
|
|
145
|
+
|
|
146
|
+
// SSH configuration
|
|
147
|
+
(
|
|
148
|
+
await cli.run(
|
|
149
|
+
'system config set ssh.public_key ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC... test@celilo',
|
|
150
|
+
)
|
|
151
|
+
).expectSuccess();
|
|
152
|
+
|
|
153
|
+
// Note: primary_domain and admin.email are no longer system config —
|
|
154
|
+
// they live in the dns_registrar capability and the authentik/caddy
|
|
155
|
+
// module variables respectively (see MANIFEST_V2 D9).
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// User-configurable fields only (match manifest.yml)
|
|
159
|
+
const config = {
|
|
160
|
+
hostname: options.hostname ?? 'iot',
|
|
161
|
+
bridge_name: options.bridgeName ?? 'Home Bridge',
|
|
162
|
+
bridge_username: options.bridgeUsername ?? '0E:21:4A:BC:DE:F0',
|
|
163
|
+
bridge_pin: options.bridgePin ?? '031-45-154',
|
|
164
|
+
bridge_port: options.bridgePort ?? 51826,
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
// Import module from fixture path
|
|
168
|
+
const modulePath = './test-fixtures/modules/homebridge';
|
|
169
|
+
(await cli.run(`module import ${modulePath}`)).expectSuccess();
|
|
170
|
+
|
|
171
|
+
// Configure module (only user-configurable fields)
|
|
172
|
+
for (const [key, value] of Object.entries(config)) {
|
|
173
|
+
// Don't quote numbers, they need to be parsed as integers
|
|
174
|
+
const valueStr = typeof value === 'number' ? String(value) : `"${value}"`;
|
|
175
|
+
(await cli.run(`module config set ${moduleId} ${key} ${valueStr}`)).expectSuccess();
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Generate if requested
|
|
179
|
+
if (options.generated) {
|
|
180
|
+
(await cli.run(`module generate ${moduleId}`)).expectSuccess();
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Unified cleanup
|
|
184
|
+
const cleanup = async () => {
|
|
185
|
+
await cli.dispose();
|
|
186
|
+
await ctx.cleanup();
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
cli,
|
|
191
|
+
moduleId,
|
|
192
|
+
config,
|
|
193
|
+
context: ctx,
|
|
194
|
+
cleanup,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Caddy module fixture options
|
|
200
|
+
*/
|
|
201
|
+
interface CaddyModuleOptions extends BaseFixtureOptions {
|
|
202
|
+
/** Hostname (default: 'www-test') */
|
|
203
|
+
hostname?: string;
|
|
204
|
+
/** Whether to build Caddy from source (default: true) */
|
|
205
|
+
buildCaddy?: boolean;
|
|
206
|
+
/** DNS provider (default: 'cloudflare') */
|
|
207
|
+
dnsProvider?: string;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Caddy module fixture result
|
|
212
|
+
*/
|
|
213
|
+
interface CaddyModuleFixture {
|
|
214
|
+
cli: CLIContext;
|
|
215
|
+
moduleId: string;
|
|
216
|
+
/** DNS external module ID (dependency) */
|
|
217
|
+
dnsExternalId: string;
|
|
218
|
+
config: Record<string, unknown>;
|
|
219
|
+
context: IntegrationTestContext;
|
|
220
|
+
cleanup: () => Promise<void>;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Caddy module fixture
|
|
225
|
+
*
|
|
226
|
+
* Sets up Caddy module with dns-external dependency.
|
|
227
|
+
* Automatically creates and configures dns-external module.
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```typescript
|
|
231
|
+
* const { cli, moduleId, dnsExternalId, cleanup } = await caddyModule({
|
|
232
|
+
* generated: true,
|
|
233
|
+
* hostname: 'www'
|
|
234
|
+
* });
|
|
235
|
+
* ```
|
|
236
|
+
*/
|
|
237
|
+
export async function caddyModule(options: CaddyModuleOptions = {}): Promise<CaddyModuleFixture> {
|
|
238
|
+
const ctx = await setupIntegrationTest();
|
|
239
|
+
const cli = await CLIContext.create('src/cli/index.ts', {
|
|
240
|
+
CELILO_DB_PATH: ctx.dbPath,
|
|
241
|
+
CELILO_DATA_DIR: ctx.dataDir,
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const moduleId = 'caddy';
|
|
245
|
+
const dnsExternalId = 'dns-external';
|
|
246
|
+
|
|
247
|
+
// Setup dns-external dependency first
|
|
248
|
+
(await cli.run(`module add ${dnsExternalId}`)).expectSuccess();
|
|
249
|
+
(await cli.run(`module config set ${dnsExternalId} hostname dns-ext`)).expectSuccess();
|
|
250
|
+
(await cli.run(`module config set ${dnsExternalId} cores 1`)).expectSuccess();
|
|
251
|
+
(await cli.run(`module config set ${dnsExternalId} memory 512`)).expectSuccess();
|
|
252
|
+
|
|
253
|
+
// Setup caddy
|
|
254
|
+
const config = {
|
|
255
|
+
hostname: options.hostname ?? 'www-test',
|
|
256
|
+
cores: 1,
|
|
257
|
+
memory: 1024,
|
|
258
|
+
build_caddy: options.buildCaddy ?? true,
|
|
259
|
+
dns_provider: options.dnsProvider ?? 'cloudflare',
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
(await cli.run(`module add ${moduleId}`)).expectSuccess();
|
|
263
|
+
|
|
264
|
+
for (const [key, value] of Object.entries(config)) {
|
|
265
|
+
const valueStr = typeof value === 'boolean' ? String(value) : value;
|
|
266
|
+
(await cli.run(`module config set ${moduleId} ${key} "${valueStr}"`)).expectSuccess();
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Generate if requested
|
|
270
|
+
if (options.generated) {
|
|
271
|
+
(await cli.run(`module generate ${dnsExternalId}`)).expectSuccess();
|
|
272
|
+
(await cli.run(`module generate ${moduleId}`)).expectSuccess();
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
const cleanup = async () => {
|
|
276
|
+
await cli.dispose();
|
|
277
|
+
await ctx.cleanup();
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
return {
|
|
281
|
+
cli,
|
|
282
|
+
moduleId,
|
|
283
|
+
dnsExternalId,
|
|
284
|
+
config,
|
|
285
|
+
context: ctx,
|
|
286
|
+
cleanup,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* DNS-external module fixture options
|
|
292
|
+
*/
|
|
293
|
+
interface DnsExternalModuleOptions extends BaseFixtureOptions {
|
|
294
|
+
/** DNS provider (default: 'cloudflare') */
|
|
295
|
+
provider?: string;
|
|
296
|
+
/** API token for testing (default: 'test-token-123') */
|
|
297
|
+
apiToken?: string;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* DNS-external module fixture result
|
|
302
|
+
*/
|
|
303
|
+
interface DnsExternalModuleFixture {
|
|
304
|
+
cli: CLIContext;
|
|
305
|
+
moduleId: string;
|
|
306
|
+
apiToken: string;
|
|
307
|
+
config: Record<string, unknown>;
|
|
308
|
+
context: IntegrationTestContext;
|
|
309
|
+
cleanup: () => Promise<void>;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* DNS-external module fixture
|
|
314
|
+
*
|
|
315
|
+
* Sets up dns-external module for external DNS management.
|
|
316
|
+
*
|
|
317
|
+
* @example
|
|
318
|
+
* ```typescript
|
|
319
|
+
* const { cli, moduleId, apiToken, cleanup } = await dnsExternalModule({
|
|
320
|
+
* generated: true,
|
|
321
|
+
* provider: 'cloudflare'
|
|
322
|
+
* });
|
|
323
|
+
* ```
|
|
324
|
+
*/
|
|
325
|
+
export async function dnsExternalModule(
|
|
326
|
+
options: DnsExternalModuleOptions = {},
|
|
327
|
+
): Promise<DnsExternalModuleFixture> {
|
|
328
|
+
const ctx = await setupIntegrationTest();
|
|
329
|
+
const cli = await CLIContext.create('src/cli/index.ts', {
|
|
330
|
+
CELILO_DB_PATH: ctx.dbPath,
|
|
331
|
+
CELILO_DATA_DIR: ctx.dataDir,
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
const moduleId = 'dns-external';
|
|
335
|
+
const apiToken = options.apiToken ?? 'test-token-123';
|
|
336
|
+
|
|
337
|
+
const config = {
|
|
338
|
+
hostname: 'dns-ext-test',
|
|
339
|
+
cores: 1,
|
|
340
|
+
memory: 512,
|
|
341
|
+
provider: options.provider ?? 'cloudflare',
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
(await cli.run(`module add ${moduleId}`)).expectSuccess();
|
|
345
|
+
|
|
346
|
+
for (const [key, value] of Object.entries(config)) {
|
|
347
|
+
(await cli.run(`module config set ${moduleId} ${key} "${value}"`)).expectSuccess();
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
// Set API token as secret
|
|
351
|
+
(await cli.run(`module secret set ${moduleId} api_token "${apiToken}"`)).expectSuccess();
|
|
352
|
+
|
|
353
|
+
// Generate if requested
|
|
354
|
+
if (options.generated) {
|
|
355
|
+
(await cli.run(`module generate ${moduleId}`)).expectSuccess();
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
const cleanup = async () => {
|
|
359
|
+
await cli.dispose();
|
|
360
|
+
await ctx.cleanup();
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
return {
|
|
364
|
+
cli,
|
|
365
|
+
moduleId,
|
|
366
|
+
apiToken,
|
|
367
|
+
config,
|
|
368
|
+
context: ctx,
|
|
369
|
+
cleanup,
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Multi-module environment options
|
|
375
|
+
*/
|
|
376
|
+
interface MultiModuleOptions {
|
|
377
|
+
/** Module IDs to set up */
|
|
378
|
+
modules: string[];
|
|
379
|
+
/** Generate all modules (default: false) */
|
|
380
|
+
generateAll?: boolean;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Multi-module environment result
|
|
385
|
+
*/
|
|
386
|
+
interface MultiModuleFixture {
|
|
387
|
+
cli: CLIContext;
|
|
388
|
+
/** Map of module ID to configuration */
|
|
389
|
+
modules: Map<string, Record<string, unknown>>;
|
|
390
|
+
context: IntegrationTestContext;
|
|
391
|
+
cleanup: () => Promise<void>;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Multi-module environment fixture
|
|
396
|
+
*
|
|
397
|
+
* Sets up multiple modules with dependencies.
|
|
398
|
+
* Automatically resolves dependency order (e.g., caddy requires dns-external).
|
|
399
|
+
*
|
|
400
|
+
* @example
|
|
401
|
+
* ```typescript
|
|
402
|
+
* const { cli, modules, cleanup } = await multiModule({
|
|
403
|
+
* modules: ['homebridge', 'caddy'],
|
|
404
|
+
* generateAll: true
|
|
405
|
+
* });
|
|
406
|
+
*
|
|
407
|
+
* expect(modules.has('homebridge')).toBe(true);
|
|
408
|
+
* expect(modules.has('caddy')).toBe(true);
|
|
409
|
+
* expect(modules.has('dns-external')).toBe(true); // Auto-added dependency
|
|
410
|
+
* ```
|
|
411
|
+
*/
|
|
412
|
+
export async function multiModule(options: MultiModuleOptions): Promise<MultiModuleFixture> {
|
|
413
|
+
const ctx = await setupIntegrationTest();
|
|
414
|
+
const cli = await CLIContext.create('src/cli/index.ts', {
|
|
415
|
+
CELILO_DB_PATH: ctx.dbPath,
|
|
416
|
+
CELILO_DATA_DIR: ctx.dataDir,
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
const modules = new Map<string, Record<string, unknown>>();
|
|
420
|
+
|
|
421
|
+
// Resolve dependencies and setup modules
|
|
422
|
+
// TODO: Implement proper dependency resolution
|
|
423
|
+
// For now, manually handle known dependencies
|
|
424
|
+
|
|
425
|
+
for (const moduleId of options.modules) {
|
|
426
|
+
switch (moduleId) {
|
|
427
|
+
case 'homebridge': {
|
|
428
|
+
const fixture = await homebridgeModule({
|
|
429
|
+
generated: options.generateAll,
|
|
430
|
+
});
|
|
431
|
+
modules.set(moduleId, fixture.config);
|
|
432
|
+
// Don't cleanup sub-fixtures, we'll cleanup at the end
|
|
433
|
+
break;
|
|
434
|
+
}
|
|
435
|
+
case 'caddy': {
|
|
436
|
+
const fixture = await caddyModule({ generated: options.generateAll });
|
|
437
|
+
modules.set(moduleId, fixture.config);
|
|
438
|
+
modules.set('dns-external', {}); // Mark as added
|
|
439
|
+
break;
|
|
440
|
+
}
|
|
441
|
+
// Add more modules as needed
|
|
442
|
+
default:
|
|
443
|
+
throw new Error(`Unknown module: ${moduleId}`);
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const cleanup = async () => {
|
|
448
|
+
await cli.dispose();
|
|
449
|
+
await ctx.cleanup();
|
|
450
|
+
};
|
|
451
|
+
|
|
452
|
+
return {
|
|
453
|
+
cli,
|
|
454
|
+
modules,
|
|
455
|
+
context: ctx,
|
|
456
|
+
cleanup,
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Export all fixtures as namespace
|
|
462
|
+
*/
|
|
463
|
+
export const fixtures = {
|
|
464
|
+
homebridgeModule,
|
|
465
|
+
caddyModule,
|
|
466
|
+
dnsExternalModule,
|
|
467
|
+
multiModule,
|
|
468
|
+
};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
|
|
2
|
+
import { modules } from '../db/schema';
|
|
3
|
+
import { cleanupTestDatabase, setupTestDatabase } from './database';
|
|
4
|
+
import {
|
|
5
|
+
cleanupMultipleTestModules,
|
|
6
|
+
cleanupTestModule,
|
|
7
|
+
getModuleState,
|
|
8
|
+
importMultipleTestModules,
|
|
9
|
+
importTestModule,
|
|
10
|
+
} from './modules';
|
|
11
|
+
|
|
12
|
+
describe('Module Test Utilities', () => {
|
|
13
|
+
let db: Awaited<ReturnType<typeof setupTestDatabase>>;
|
|
14
|
+
|
|
15
|
+
beforeEach(async () => {
|
|
16
|
+
db = await setupTestDatabase();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(async () => {
|
|
20
|
+
await cleanupTestDatabase(db);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe('importTestModule', () => {
|
|
24
|
+
test('imports module from fixture', async () => {
|
|
25
|
+
const moduleId = await importTestModule('./test-fixtures/modules/artifact-test', db);
|
|
26
|
+
|
|
27
|
+
expect(moduleId).toBe('artifact-test');
|
|
28
|
+
|
|
29
|
+
const result = await db.select().from(modules);
|
|
30
|
+
expect(result).toHaveLength(1);
|
|
31
|
+
expect(result[0].id).toBe('artifact-test');
|
|
32
|
+
expect(result[0].state).toBe('IMPORTED');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test('imports module with configuration', async () => {
|
|
36
|
+
const moduleId = await importTestModule('./test-fixtures/modules/artifact-test', db, {
|
|
37
|
+
config: {
|
|
38
|
+
vmid: '100',
|
|
39
|
+
hostname: 'test-host',
|
|
40
|
+
},
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
expect(moduleId).toBe('artifact-test');
|
|
44
|
+
|
|
45
|
+
// TODO: Add assertions for config when module config utilities are available
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('returns module ID', async () => {
|
|
49
|
+
const moduleId = await importTestModule('./test-fixtures/modules/artifact-test', db);
|
|
50
|
+
|
|
51
|
+
expect(typeof moduleId).toBe('string');
|
|
52
|
+
expect(moduleId.length).toBeGreaterThan(0);
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
describe('cleanupTestModule', () => {
|
|
57
|
+
test('removes module from database', async () => {
|
|
58
|
+
const moduleId = await importTestModule('./test-fixtures/modules/artifact-test', db);
|
|
59
|
+
|
|
60
|
+
const before = await db.select().from(modules);
|
|
61
|
+
expect(before).toHaveLength(1);
|
|
62
|
+
|
|
63
|
+
await cleanupTestModule(moduleId, db);
|
|
64
|
+
|
|
65
|
+
const after = await db.select().from(modules);
|
|
66
|
+
expect(after).toHaveLength(0);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('handles non-existent module gracefully', async () => {
|
|
70
|
+
await expect(cleanupTestModule('non-existent-module', db)).resolves.toBeUndefined();
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe('importMultipleTestModules', () => {
|
|
75
|
+
test('imports multiple modules', async () => {
|
|
76
|
+
// Note: Using artifact-test twice to test multiple imports
|
|
77
|
+
// In real scenarios, these would be different modules
|
|
78
|
+
const moduleIds = await importMultipleTestModules(
|
|
79
|
+
[{ path: './test-fixtures/modules/artifact-test' }],
|
|
80
|
+
db,
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
expect(moduleIds).toHaveLength(1);
|
|
84
|
+
expect(moduleIds[0]).toBe('artifact-test');
|
|
85
|
+
|
|
86
|
+
const result = await db.select().from(modules);
|
|
87
|
+
expect(result).toHaveLength(1);
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test('imports modules with configuration', async () => {
|
|
91
|
+
const moduleIds = await importMultipleTestModules(
|
|
92
|
+
[
|
|
93
|
+
{
|
|
94
|
+
path: './test-fixtures/modules/artifact-test',
|
|
95
|
+
options: { config: { vmid: '100' } },
|
|
96
|
+
},
|
|
97
|
+
],
|
|
98
|
+
db,
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
expect(moduleIds).toHaveLength(1);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('handles empty array', async () => {
|
|
105
|
+
const moduleIds = await importMultipleTestModules([], db);
|
|
106
|
+
expect(moduleIds).toHaveLength(0);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe('cleanupMultipleTestModules', () => {
|
|
111
|
+
test('removes multiple modules', async () => {
|
|
112
|
+
const moduleIds = await importMultipleTestModules(
|
|
113
|
+
[{ path: './test-fixtures/modules/artifact-test' }],
|
|
114
|
+
db,
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
const before = await db.select().from(modules);
|
|
118
|
+
expect(before).toHaveLength(1);
|
|
119
|
+
|
|
120
|
+
await cleanupMultipleTestModules(moduleIds, db);
|
|
121
|
+
|
|
122
|
+
const after = await db.select().from(modules);
|
|
123
|
+
expect(after).toHaveLength(0);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('handles empty array', async () => {
|
|
127
|
+
await expect(cleanupMultipleTestModules([], db)).resolves.toBeUndefined();
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe('getModuleState', () => {
|
|
132
|
+
test('returns module state', async () => {
|
|
133
|
+
const moduleId = await importTestModule('./test-fixtures/modules/artifact-test', db);
|
|
134
|
+
|
|
135
|
+
const state = await getModuleState(moduleId, db);
|
|
136
|
+
expect(state).toBe('IMPORTED');
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test('returns null for non-existent module', async () => {
|
|
140
|
+
const state = await getModuleState('non-existent', db);
|
|
141
|
+
expect(state).toBeNull();
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
});
|