Files
orca/src/main/ssh/ssh-relay-opencode-pinned-node.test.ts
T
8afa1db50c feat(ssh): rung B glibc 2.17 compat runtime; gate remote vault on host node:sqlite (#24148)
* feat(ssh): wire rung B to the glibc 2.17 compat runtime; gate rung C vault on full node:sqlite

- COMPAT_RELAY_RUNTIMES lists linux-x64-glibc217; rung B plans the compat slot and compat
  pinned Node when glibc is below 2.28 or rung A refused with libc_floor/missing_lib.
- The relay version folds the compat runtime's executable hash; refusals are cached per runtime.
- The orcad template stages an optional linux-x64-glibc217 target (base package + compat
  node-pty slot + compat runtime marker); the verifier and materializer accept it.
- node-pty slot loader falls back to the compat slot when the default slot is missing or
  needs a newer glibc.
- Runtime store GC keeps the compat pin beside the default one on every relay connect.
- hasNodeSqliteReaderApi (DatabaseSync + backup) gates relay session search and the relay
  OpenCode reader, which now names the host Node version in its unavailable reason; the SSH
  vault reader installs the compat Node on old-glibc hosts and uploads nothing when no
  pinned Node can run.
- Rung D: a remembered noexec reports home_noexec and never advises installing Node.

* fix(ssh): re-prove a replayed noexec after rung D so allowing exec recovers the host

* fix(ssh): keep the rung B compat runtime pinned in the relay-connect store GC

* test(ssh): mock deployment-target facts in the Windows OpenCode runtime tests

* ci(ssh): build the glibc 2.17 compat slot for the hostile-host matrix; CentOS 7 lands on rung B

---------

Co-authored-by: m4air <m4air@m4airs-Air.localdomain>
Co-authored-by: m4air <m4air@Mac.localdomain>
2026-10-01 05:32:05 -07:00

98 lines
4.2 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest'
import type * as NodeRuntimeStore from './orcad-remote-node-runtime'
const mocks = vi.hoisted(() => ({
target: vi.fn(),
archive: vi.fn(),
ensure: vi.fn()
}))
vi.mock('./orcad-deployment-target', () => ({ resolveOrcadDeploymentTargetFacts: mocks.target }))
vi.mock('./pinned-runtime-materializer', () => ({
materializeNodeRuntimeArchive: mocks.archive
}))
vi.mock('./orcad-remote-node-runtime', async (original) => ({
...(await original<typeof NodeRuntimeStore>()),
ensureRemoteOrcadNodeRuntime: mocks.ensure
}))
import { NODE_RUNTIME_ASSETS, NODE_RUNTIME_COMPAT_ASSETS } from '../../shared/node-runtime-pin'
import type { SshConnection } from './ssh-connection'
import { getRemoteHostPlatform } from './ssh-remote-platform'
import { preparePinnedNodeForVault } from './ssh-relay-opencode-pinned-node'
// oxlint-disable-next-line typescript/consistent-type-assertions -- SAFETY: every remote call is mocked; the connection is only passed through.
const conn = {} as SshConnection
function prepare(platform: 'linux-x64' | 'win32-x64', exec: (command: string) => Promise<string>) {
const relayDir = `${platform === 'win32-x64' ? 'C:/Users/ada' : '/home/ada'}/.orca-remote/relay-build`
return preparePinnedNodeForVault({
conn,
host: getRemoteHostPlatform(platform),
relayDir,
cacheRoot: '/cache',
signal: new AbortController().signal,
exec,
remote: (operation) => operation()
})
}
beforeEach(() => {
vi.clearAllMocks()
})
describe('pinned Node for the SSH vault reader', () => {
it.each([
['linux-x64', 'linux-x64-glibc', { major: 2, minor: 31 }, '/home/ada', 'bin/node'],
['win32-x64', 'win32-x64', null, 'C:/Users/ada', 'node.exe']
] as const)(
'installs the official archive into the shared runtimes/ store on %s hosts',
async (platform, target, glibc, home, executableName) => {
const sha = NODE_RUNTIME_ASSETS[target].executableSha256
const executable = `${home}/.orca-remote/runtimes/node-${sha}/${executableName}`
mocks.target.mockResolvedValue({ target, glibc })
mocks.ensure.mockImplementation(async (options) => {
expect(options).toMatchObject({ slotDir: `${home}/.orca-remote/relay-build`, target })
await options.archivePath()
return { executable, transfer: 'uploaded' }
})
mocks.archive.mockResolvedValue('/cache/node-archive')
expect(await prepare(platform, vi.fn())).toEqual({ executable, runtimeSha256: sha })
expect(mocks.archive).toHaveBeenCalledWith(target, '/cache', expect.any(Object))
}
)
it('fetches no archive when the host store already has a verified runtime', async () => {
mocks.target.mockResolvedValue({ target: 'win32-x64', glibc: null })
mocks.ensure.mockResolvedValue({ executable: 'C:/x/node.exe', transfer: 'cached' })
await prepare('win32-x64', vi.fn())
expect(mocks.archive).not.toHaveBeenCalled()
})
it('installs the glibc 2.17 compat Node for the reader on a host below the default floor', async () => {
const sha = NODE_RUNTIME_COMPAT_ASSETS['linux-x64-glibc217'].executableSha256
const executable = `/home/ada/.orca-remote/runtimes/node-${sha}/bin/node`
mocks.target.mockResolvedValue({ target: 'linux-x64-glibc', glibc: { major: 2, minor: 17 } })
mocks.ensure.mockImplementation(async (options) => {
expect(options).toMatchObject({ target: 'linux-x64-glibc217' })
await options.archivePath()
return { executable, transfer: 'uploaded' }
})
mocks.archive.mockResolvedValue('/cache/node-217.tar.gz')
// The compat sha is the relay dir's store ref, so store GC keeps this runtime.
expect(await prepare('linux-x64', vi.fn())).toEqual({ executable, runtimeSha256: sha })
expect(mocks.archive).toHaveBeenCalledWith('linux-x64-glibc217', '/cache', expect.any(Object))
})
it('uploads nothing when no Orca-managed Node runs on the host glibc', async () => {
mocks.target.mockResolvedValue({ target: 'linux-arm64-glibc', glibc: { major: 2, minor: 17 } })
await expect(prepare('linux-x64', vi.fn())).rejects.toThrow(
"No Orca-managed Node runs on this host's glibc 2.17"
)
expect(mocks.ensure).not.toHaveBeenCalled()
expect(mocks.archive).not.toHaveBeenCalled()
})
})