import { test } from 'node:test'; import assert from 'node:assert/strict'; import { mkdtempSync, mkdirSync, readFileSync, writeFileSync, rmSync, existsSync } from 'node:fs'; import { tmpdir } from 'node:os'; import { join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { spawnSync } from 'node:child_process'; for (const status of ['0', '23', '124', '137', 'empty']) { for (const pending of [false, true]) { test('runtime prestart status ' + status + ', pending migration ' + pending, () => { const dir = mkdtempSync(join(tmpdir(), 'dsh-prestart-')); try { if (status !== 'empty') { mkdirSync(join(dir, 'profiles/web'), { recursive: true, mode: 0o700 }); writeFileSync(join(dir, 'profiles/web/package.json'), '{}', { mode: 0o600 }); if (pending) writeFileSync(join(dir, 'profiles/web/.dsh-lockfile-pending'), 'pending'); } const scripts = { node: `#!/bin/bash exit 0 `, timeout: `#!/bin/bash printf 'timeout %s\n' "$*" >> "$TRACE" test "$1 $2 $3" = "--signal=TERM --kill-after=5s 120s" || exit 90 shift 3 exec "$@" `, dsh: `#!/bin/bash printf 'dsh %s\n' "$*" >> "$TRACE" test "$npm_config_ignore_scripts:$npm_config_ignore_pnpmfile" = true:true || exit 91 if [ "$1" = plugin ]; then test "$*" = "plugin --profile web install --ignore-scripts$EXPECTED_ARGS" || exit 92 exit "$PLUGIN_STATUS" fi exit 0 `, caddy: `#!/bin/bash echo caddy >> "$TRACE" sleep 0.1 `, }; for (const [name, script] of Object.entries(scripts)) { writeFileSync(join(dir, name), script, { mode: 0o700 }); } const result = spawnSync('bash', [fileURLToPath(new URL('./entrypoint.sh', import.meta.url)), 'web', '--no-open', '--port', '3080'], { env: { ...process.env, PATH: dir + ':' + process.env.PATH, DSH_HOME: dir, TRACE: join(dir, 'trace'), PLUGIN_STATUS: status, EXPECTED_ARGS: pending ? ' --no-frozen-lockfile' : '' }, encoding: 'utf8', timeout: 5000, }); assert.ifError(result.error); const output = result.stdout + result.stderr; const trace = readFileSync(join(dir, 'trace'), 'utf8'); assert.equal(result.status, status === 'empty' ? 0 : Number(status), output); if (status === 'empty') { assert.doesNotMatch(trace, /plugin/); assert.match(trace, /dsh web/); return; } assert.match(trace, /dsh plugin --profile web install --ignore-scripts/); assert.equal(existsSync(join(dir, 'profiles/web/.dsh-lockfile-pending')), pending && status !== '0'); if (status === '0') { assert.match(output, /reconciliation complete/); assert.match(trace, /dsh web/); } else { assert.ok(output.includes('failed (exit ' + status + ')'), output); assert.doesNotMatch(trace, /caddy|dsh web/); } } finally { rmSync(dir, { recursive: true, force: true }); } }); } } test('runtime pins pnpm and uses nonroot with tini process-group forwarding', () => { const dockerfile = readFileSync(new URL('./Dockerfile', import.meta.url), 'utf8'); for (const required of ['pnpm@10.11.0', 'test "$(pnpm --version)" = "10.11.0"', 'USER node', '"/usr/bin/tini", "-g"']) { assert.ok(dockerfile.includes(required), 'missing ' + required); } });