// Run after building the native executable. No npm packages are required. import { test } from 'node:test'; import assert from 'node:assert/strict'; import { spawnSync } from 'node:child_process'; import { readFileSync, writeFileSync, mkdtempSync, readdirSync, rmSync } from 'node:fs'; import { createHash } from 'node:crypto'; import { tmpdir } from 'node:os'; import { resolve, join } from 'node:path'; const binary = resolve(process.env.DEPLOYCTL_BIN ?? (process.platform === 'win32' ? 'dist/deployctl.exe' : 'dist/deployctl')); const intent = JSON.parse(readFileSync(new URL('../protocol/examples/plan-request.json', import.meta.url), 'utf8')); test('real CLI artifact verification rejects missing metadata without writes', () => { const directory = mkdtempSync(join(tmpdir(), 'deployctl-artifact-smoke-')); try { const result = spawnSync(binary, ['verify-artifacts'], { encoding:'utf8', timeout:10000, input:JSON.stringify({directory, artifactDirectory:directory, suite:'resolute', architecture:'amd64', versions:{}}), }); assert.notEqual(result.status, 0); assert.equal(result.stdout, ''); assert.equal(result.stderr.includes('unsupported command'), false); assert.equal(result.stderr.includes(directory), false); assert.deepEqual(readdirSync(directory), []); } finally { rmSync(directory, {recursive:true}); } }); test('real CLI rejects caller-asserted repository authentication', () => { const result = spawnSync(binary, ['verify-repository'], { encoding: 'utf8', timeout: 10000, input: JSON.stringify({directory:'secret-relative', suite:'resolute', architecture:'amd64', versions:{}, repositoryAuthenticated:true}), }); assert.notEqual(result.status, 0); assert.equal(result.stdout, ''); assert.equal(result.stderr.includes('secret-relative'), false); }); test('real CLI environment draft rejects malformed locks without writing', () => { const directory = mkdtempSync(join(tmpdir(), 'deployctl-environment-smoke-')); try { const probe = spawnSync(binary, ['preflight'], { encoding: 'utf8', timeout: 10000 }); assert.equal(probe.status, 0, probe.stderr); const report = JSON.parse(probe.stdout).report; const suite = report.runtime.os === 'linux' ? report.distribution.codename : 'resolute'; const arch = report.runtime.os === 'linux' ? report.runtime.architecture : 'amd64'; const lock = { protocolVersion: 1, repository: 'https://download.docker.com/linux/ubuntu', suite, architecture: arch, releaseDigest: `sha256:${'a'.repeat(64)}`, packages: ['docker-ce', 'docker-ce-cli', 'containerd.io', 'docker-buildx-plugin', 'docker-compose-plugin'].map(name => ({ name, version: '1.2.3-1', filename: `dists/${suite}/pool/stable/${arch}/${name}_1.2.3-1_${arch}.deb`, digest: `sha256:${'b'.repeat(64)}`, size: 123, })), }; const run = () => spawnSync(binary, ['plan-environment'], { cwd: directory, encoding: 'utf8', timeout: 10000, input: JSON.stringify({lock}) }); const result = run(); assert.equal(result.status, 0, result.stderr); const draft = JSON.parse(result.stdout).draft; assert.equal(draft.executable, false); assert.equal(draft.repositoryAuthenticated, false); assert.ok(draft.blockers.includes('dependency_transaction_unresolved')); assert.equal(draft.requestedPackages.length, 5); lock.packages[0].version = 'secret;reboot'; const invalid = run(); assert.notEqual(invalid.status, 0); assert.equal(invalid.stdout, ''); assert.equal(invalid.stderr.includes('secret'), false); assert.deepEqual(readdirSync(directory), []); } finally { rmSync(directory, { recursive: true }); } }); test('real CLI preflight reports blockers without creating files', () => { const directory = mkdtempSync(join(tmpdir(), 'deployctl-preflight-smoke-')); try { const result = spawnSync(binary, ['preflight'], { cwd: directory, encoding: 'utf8', timeout: 10000 }); assert.equal(result.status, 0, result.stderr); const response = JSON.parse(result.stdout); assert.equal(response.mode, 'local-environment-proposal'); assert.equal(response.proposal.executable, false); assert.ok(response.proposal.blockers.includes('package_versions_unresolved')); assert.ok(response.proposal.blockers.includes('host_identity_unverified')); assert.deepEqual(readdirSync(directory), []); } finally { rmSync(directory, { recursive: true }); } }); test('real CLI previews and verifies without writing into its working directory', () => { const directory = mkdtempSync(join(tmpdir(), 'deployctl-smoke-')); const run = (command, input) => spawnSync(binary, [command], { input: JSON.stringify(input), encoding: 'utf8', cwd: directory, timeout: 10000, }); try { const preview = run('plan', intent); assert.equal(preview.error, undefined); assert.equal(preview.status, 0, preview.stderr); const response = JSON.parse(preview.stdout); assert.equal(response.executable, false); assert.equal(response.mode, 'offline-preview'); assert.equal(response.plan.projectName, 'sd-git-one'); const check = run('verify-plan', { plan: response.plan, current: intent }); assert.equal(check.status, 0, check.stderr); assert.equal(JSON.parse(check.stdout).valid, true); const changed = run('verify-plan', { plan: response.plan, current: { ...intent, hostId: 'different-host' } }); assert.notEqual(changed.status, 0); assert.equal(changed.stdout, ''); const apply = run('apply', response.plan); assert.notEqual(apply.status, 0); assert.equal(apply.stdout, ''); assert.deepEqual(readdirSync(directory), []); } finally { // Only remove the unique test directory this test just created. rmSync(directory, { recursive: true }); } }); test('real CLI inspects locally and verifies packages without claiming execution safety', () => { const inspection = spawnSync(binary, ['inspect'], { encoding: 'utf8', timeout: 10000 }); assert.equal(inspection.status, 0, inspection.stderr); assert.equal(JSON.parse(inspection.stdout).deploymentReady, false); const directory = mkdtempSync(join(tmpdir(), 'deployctl-package-smoke-')); const digest = bytes => `sha256:${createHash('sha256').update(bytes).digest('hex')}`; try { const payload = 'services: {}\n'; const manifest = JSON.stringify({ protocolVersion: 1, appId: 'example', version: '1.0.0', runtime: 'compose', entrypoint: 'compose.yaml', platforms: ['linux/amd64'], components: [{ name: 'server', image: `example/server@sha256:${'a'.repeat(64)}` }], files: [{ path: 'compose.yaml', digest: digest(payload) }], }); writeFileSync(join(directory, 'manifest.json'), manifest); writeFileSync(join(directory, 'compose.yaml'), payload); const result = spawnSync(binary, ['verify-package'], { encoding: 'utf8', timeout: 10000, input: JSON.stringify({ directory, expectedDigest: digest(manifest) }), }); assert.equal(result.status, 0, result.stderr); const verified = JSON.parse(result.stdout); assert.equal(verified.verified, true); assert.equal(verified.executable, false); assert.equal(verified.publisherAuthenticated, false); assert.deepEqual(readdirSync(directory).sort(), ['compose.yaml', 'manifest.json']); assert.equal(readFileSync(join(directory, 'compose.yaml'), 'utf8'), payload); } finally { rmSync(directory, { recursive: true }); } }); test('real CLI separates package integrity from restricted policy and rejects tampering', () => { const directory = mkdtempSync(join(tmpdir(), 'deployctl-policy-smoke-')); const digest = bytes => `sha256:${createHash('sha256').update(bytes).digest('hex')}`; const image = `example/server@sha256:${'a'.repeat(64)}`; const document = { services: { server: { image, user: '1000:1000', read_only: true, cap_drop: ['ALL'], security_opt: ['no-new-privileges:true'], restart: 'no', networks: ['backend'], volumes: [] } }, networks: { backend: { internal: true } }, volumes: {}, }; const run = (command, expectedDigest) => spawnSync(binary, [command], { encoding: 'utf8', timeout: 10000, cwd: directory, input: JSON.stringify({ directory, expectedDigest }), }); const writePackage = () => { const payload = JSON.stringify(document); const manifest = JSON.stringify({ protocolVersion: 1, appId: 'example', version: '1.0.0', runtime: 'compose', entrypoint: 'compose.json', platforms: ['linux/amd64'], components: [{ name: 'server', image }], files: [{ path: 'compose.json', digest: digest(payload) }], }); writeFileSync(join(directory, 'manifest.json'), manifest); writeFileSync(join(directory, 'compose.json'), payload); return digest(manifest); }; try { const pin = writePackage(); const check = run('check-package', pin); assert.equal(check.status, 0, check.stderr); const result = JSON.parse(check.stdout); assert.equal(result.policyPassed, true); assert.equal(result.executable, false); assert.equal(result.publisherAuthenticated, false); assert.equal(result.digest, pin); writeFileSync(join(directory, 'compose.json'), '{}'); assert.notEqual(run('check-package', pin).status, 0); document.services.server.privileged = true; const unsafePin = writePackage(); assert.equal(run('verify-package', unsafePin).status, 0); const rejected = run('check-package', unsafePin); assert.notEqual(rejected.status, 0); assert.equal(rejected.stdout, ''); assert.deepEqual(readdirSync(directory).sort(), ['compose.json', 'manifest.json']); } finally { rmSync(directory, { recursive: true }); } });