From bbcff6a4927c8afe2fc164639f909ba0756f41bf Mon Sep 17 00:00:00 2001 From: DarKylX Date: Sun, 25 Oct 2026 20:34:36 +0300 Subject: [PATCH] Invoke and verify the exported retention SSH transport --- .github/workflows/dev-image-retention.yml | 13 ++++- tests/test_dev_image_retention_workflow.cjs | 63 +++++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 tests/test_dev_image_retention_workflow.cjs diff --git a/.github/workflows/dev-image-retention.yml b/.github/workflows/dev-image-retention.yml index 19ccab70..0633005f 100644 --- a/.github/workflows/dev-image-retention.yml +++ b/.github/workflows/dev-image-retention.yml @@ -36,6 +36,8 @@ jobs: node-version: '24' - name: Install verified SSH transport run: npm install --prefix "$RUNNER_TEMP/dev-migration-ssh" --ignore-scripts --no-package-lock --no-audit --no-fund ssh2@1.16.0 + - name: Verify workflow transport invocation + run: node --test tests/test_dev_image_retention_workflow.cjs - name: Plan or apply exact unused DEV image removals env: NODE_PATH: ${{ runner.temp }}/dev-migration-ssh/node_modules @@ -55,5 +57,14 @@ jobs: if (mode === 'apply' && !/^[0-9a-f]{64}$/.test(digest)) throw new Error('Inspect a plan first'); const source = fs.readFileSync('scripts/dev_image_retention.py', 'utf8'); process.env.DEV_DEPLOY_SCRIPT = `set -euo pipefail\npython3 - ${mode} ${digest} <<'DEV_IMAGE_RETENTION_PY'\n${source}\nDEV_IMAGE_RETENTION_PY\n`; - require('./scripts/dev_ci_ssh.cjs'); + require('./scripts/dev_ci_ssh.cjs').deploy({ + host: process.env.DEV_SERVER_HOST, + username: process.env.DEV_SERVER_USER, + password: process.env.DEV_SERVER_PASSWORD, + hostFingerprint: process.env.DEV_SSH_HOST_KEY_SHA256, + script: process.env.DEV_DEPLOY_SCRIPT, + }).catch(error => { + console.error(error.message); + process.exitCode = 1; + }); NODE diff --git a/tests/test_dev_image_retention_workflow.cjs b/tests/test_dev_image_retention_workflow.cjs new file mode 100644 index 00000000..3bc512ce --- /dev/null +++ b/tests/test_dev_image_retention_workflow.cjs @@ -0,0 +1,63 @@ +const assert = require('node:assert/strict'); +const fs = require('node:fs'); +const test = require('node:test'); +const vm = require('node:vm'); + +const yaml = fs.readFileSync('.github/workflows/dev-image-retention.yml', 'utf8').replace(/\r/g, ''); +const source = yaml.split(" node <<'NODE'\n")[1].split('\n NODE')[0].replace(/^ /gm, ''); + +async function execute(mode, digest, fail = false) { + const calls = []; + const errors = []; + const context = { + process: { env: { + RETENTION_MODE: mode, + PLAN_SHA256: digest, + DEV_SERVER_HOST: 'dev-host', + DEV_SERVER_USER: 'dev-user', + DEV_SERVER_PASSWORD: 'synthetic-secret', + DEV_SSH_HOST_KEY_SHA256: 'pinned-dev-key', + }, exitCode: 0 }, + console: { error: message => errors.push(message) }, + require(name) { + if (name === 'node:fs') return { readFileSync: () => 'print("PLAN")' }; + assert.equal(name, './scripts/dev_ci_ssh.cjs'); + return { deploy: async config => { + calls.push(config); + if (fail) throw new Error('DEV SSH deployment failed'); + } }; + }, + }; + vm.runInNewContext(source, context); + await new Promise(resolve => setImmediate(resolve)); + return { calls, errors, context }; +} + +test('plan actually invokes the exported SSH transport with separate credentials', async () => { + const { calls, context } = await execute('plan', ''); + assert.equal(calls.length, 1); + assert.equal(calls[0].hostFingerprint, 'pinned-dev-key'); + assert.equal(calls[0].password, 'synthetic-secret'); + assert.match(calls[0].script, /python3 - plan none <<'DEV_IMAGE_RETENTION_PY'/); + assert.ok(!calls[0].script.includes('synthetic-secret')); + assert.equal(context.process.exitCode, 0); +}); + +test('apply passes exactly the reviewed hexadecimal inventory digest', async () => { + const { calls } = await execute('apply', 'a'.repeat(64)); + assert.equal(calls.length, 1); + assert.ok(calls[0].script.includes('python3 - apply ' + 'a'.repeat(64))); +}); + +test('invalid modes and shell text are rejected before the transport', async () => { + await assert.rejects(execute('other', ''), /Invalid mode/); + await assert.rejects(execute('plan', '$(id)'), /Invalid plan digest/); + await assert.rejects(execute('apply', ''), /Inspect a plan first/); +}); + +test('remote failure makes the workflow fail instead of reporting a successful plan', async () => { + const { calls, errors, context } = await execute('plan', '', true); + assert.equal(calls.length, 1); + assert.equal(context.process.exitCode, 1); + assert.deepEqual(errors, ['DEV SSH deployment failed']); +});