import { afterEach, describe, expect, it } from 'vitest' import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'fs' import { tmpdir } from 'os' import { dirname, join, resolve } from 'path' import { spawnSync } from 'child_process' const dirs: string[] = [] afterEach(() => dirs.splice(0).forEach(dir => rmSync(dir, { recursive: true, force: true }))) describe.skipIf(process.platform === 'win32')('deployment CLI readiness', () => { it.each([true, false])('requires executable CLI readiness even when HTTP checks pass: %s', available => { const root = mkdtempSync(join(tmpdir(), 'hermes-deploy-test-')) dirs.push(root) const bin = join(root, 'bin') mkdirSync(bin) const shim = (name: string, body: string) => writeFileSync(join(bin, name), `#!/bin/sh\n${body}\n`, { mode: 0o755 }) shim('npm', 'if [ "$1" = "-v" ]; then echo 11; fi\nexit 0') shim('systemctl', 'exit 0') shim('curl', `case "$*" in\n *cli-status*) echo '${JSON.stringify({ available, reason: available ? 'ready' : 'permission_denied' })}' ;;\n *) printf 200 ;;\nesac`) const result = spawnSync('bash', [resolve('deploy.sh'), '--skip-pull', '--skip-deps', '--skip-build'], { encoding: 'utf8', timeout: 15000, env: { ...process.env, PATH: [bin, dirname(process.execPath), process.env.PATH].join(':'), HERMES_UI_STATE_DIR: join(root, 'state'), HERMES_UI_DEPLOY_SCOPED: '1' }, }) expect(result.error).toBeUndefined() expect(result.status).toBe(available ? 0 : 1) expect(result.stdout + result.stderr).toContain(available ? 'Hermes CLI is executable by the service' : 'permission_denied') }) })