Files
orca/src/shared/ssh-pty-id.test.ts
T
Brennan Benson f23d0b166f fix(relay): mint PTY ids that carry the relay incarnation instead of a restarting counter (#16901)
* fix(relay): scope PTY ids to mint epochs

* test(relay): treat minted PTY ids as opaque

* test(relay): pin mint-epoch id shape and restore spawn-sequence assertions

The epoch escaping had no test: dropping encodeURIComponent left the whole
relay suite green. Pin the three-field id shape against an epoch that carries
both separators, and cover a colon-bearing relay id through the unchanged
app-side SSH id wrapper.

subprocess.test.ts had traded `pty-1`/`pty-2` for `expect.any(String)`, which
discarded the invariant those two cases exist to prove: an early node-pty load
failure burns no sequence, a late spawn failure burns one.

* test(relay): mirror production epoch escaping in testPtyId

The harness built the expected id without the encodeURIComponent production
applies at the mint site. A test epoch carrying a reserved character would
diverge silently across ~40 assertions in 11 files.
2026-08-30 14:49:18 -07:00

57 lines
2.7 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import { toSshExecutionHostId } from './execution-host'
import { parseAppSshPtyId, toAppSshPtyId, toRelaySshPtyId } from './ssh-pty-id'
describe('ssh pty id routing', () => {
const CONNECTION = 'ssh-1779863656395-57g1q1'
const HOST_ID = toSshExecutionHostId(CONNECTION) // "ssh:ssh-1779863656395-57g1q1"
const APP_ID = toAppSshPtyId(CONNECTION, 'pty-3') // "ssh:ssh-1779863656395-57g1q1@@pty-3"
it('encodes the bare target id into the app pty id', () => {
expect(APP_ID).toBe('ssh:ssh-1779863656395-57g1q1@@pty-3')
expect(parseAppSshPtyId(APP_ID)).toEqual({ connectionId: CONNECTION, relayPtyId: 'pty-3' })
})
it('round-trips app <-> relay ids for the matching bare connection', () => {
expect(toRelaySshPtyId(CONNECTION, APP_ID)).toBe('pty-3')
expect(toAppSshPtyId(CONNECTION, APP_ID)).toBe(APP_ID)
})
// Why: reconnect/restore callers may pass the execution-host id form
// ("ssh:<targetId>") from a workspace `hostId`; it names the same connection
// as the bare id embedded in the app pty id and must not throw or misencode.
it('accepts the execution-host id form as the same connection', () => {
expect(toRelaySshPtyId(HOST_ID, APP_ID)).toBe('pty-3')
expect(toAppSshPtyId(HOST_ID, APP_ID)).toBe(APP_ID)
// A fresh app id built from the host-id form still stores the bare id.
expect(toAppSshPtyId(HOST_ID, 'pty-7')).toBe('ssh:ssh-1779863656395-57g1q1@@pty-7')
})
// Why: relays mint epoch-scoped ids (`pty2:<epoch>:<n>`) that carry colons, while
// this wrapper — unchanged here, and the version shipped in older clients — splits
// an app id on the FIRST `@@` after `ssh:`. Pin the round trip so a colon-bearing
// relay id stays routable instead of losing its owning connection.
it('round-trips a mint-epoch relay pty id', () => {
const relayPtyId = 'pty2:0f8fad5b-d9cb-469f-a165-70867728950e:12'
const appId = toAppSshPtyId(CONNECTION, relayPtyId)
expect(appId).toBe(`ssh:${CONNECTION}@@${relayPtyId}`)
expect(parseAppSshPtyId(appId)).toEqual({ connectionId: CONNECTION, relayPtyId })
expect(toRelaySshPtyId(CONNECTION, appId)).toBe(relayPtyId)
expect(toRelaySshPtyId(HOST_ID, appId)).toBe(relayPtyId)
expect(toAppSshPtyId(CONNECTION, appId)).toBe(appId)
expect(() => toRelaySshPtyId('ssh-other', appId)).toThrow(
`belongs to SSH connection "${CONNECTION}"`
)
})
it('still rejects a pty id owned by a genuinely different connection', () => {
expect(() => toRelaySshPtyId('ssh-other', APP_ID)).toThrow(
`belongs to SSH connection "${CONNECTION}"`
)
expect(() => toAppSshPtyId(toSshExecutionHostId('ssh-other'), APP_ID)).toThrow(
`belongs to SSH connection "${CONNECTION}"`
)
})
})