mirror of
https://github.com/stablyai/orca.git
synced 2026-10-24 08:03:58 +00:00
Audit sweep over `src/relay`, `src/preload` and `src/shared` (1,087 test files reviewed). 101 case declarations removed across 40 files, 6 test files deleted outright, 1,143 lines gone. Executed-case count falls further, since several removals were `it.each` tables. Dominant patterns, by frequency: - Self-comparisons that cannot fail: `expect(f(x)).toBe(f(x))`, `JSON.parse(JSON.stringify(literal))` deep-equalling the literal for a type with no codec, and `normalizeKeyToken(t) === normalizeKeyToken(t)` presented as proof of memoization. - Object literals asserting their own fields back, where the guarantee comes from the type annotation and the runtime assertion cannot fail. - Copied inventories: constants compared to their own initializers, and a function returning a copy of an exported constant checked against that constant's literal contents. - Duplicate invocations of a contract owned at a stronger boundary, including provider-local replays of a shared helper. - Table rows varying a field production never reads, so every row runs one path. - Names promising more than the input exercises: a "Windows launch" case in a module with no platform input, and a case whose named branch is never entered. Two production symbols go with them, each a test-only export whose sole caller was a deleted case: - `getGitHubProjectRefInputByteLength` — a one-line forward to `getClipboardTextByteLength`. The real bound (`GITHUB_PROJECT_REF_INPUT_MAX_BYTES`) and its guard stay. - `GRAB_STYLE_PROPERTIES` — an intended shared source of truth that nothing ever consulted; the property set is hand-enumerated at three independent sites. One case was deliberately restored and strengthened rather than dropped. The relay integration suite is the only place the real `SshChannelMultiplexer` is wired to `RelayDispatcher`, so it reaches transport behavior the handler suites cannot (they use `createMockDispatcher`). Its `fs.writeFile` roundtrip is the one case producing a void result, and `JSON.stringify` drops an absent `result` member — a shape no other surviving case exercises. Restored with an assertion pinning what the client actually observes: `null`, not `undefined`. That assertion failed on first run, so the fact was previously unasserted anywhere. One deletion was reverted mid-audit. A case asserting that optional fields stay invisible to "old attach and ready decoders" builds those decoders from `z.object` schemas declared in the test file, so it demonstrates zod's unknown-key stripping rather than anything shipped. It is nonetheless the only forward-compatibility coverage these envelopes have, and `reliability-gates.jsonc:6232` names it as evidence verbatim, so it stays. Note that `check-reliability-gates.mjs` passed both with and without it: the script resolves manifest paths and commands, and does not check that a named assertion still corresponds to a live case. Kept deliberately: everything a reliability gate cites as evidence; the three `registers all expected handlers` RPC manifests (a dropped registration is a silent wire break no type checker catches, and one carries the STA-4571 `pty.ackData` ratchet); the `child-process` direct-import ratchet; and prototype-spy cases paired with a `.repeat(10_000)` input, which assert a real memory bound rather than merely forbidding a technique. Verified: `pnpm test src/shared src/relay src/preload` (1073 files, 11996 passed, 1 pre-existing `it.fails`, 131 skipped), `pnpm tc` after clearing `.tsbuildinfo`, `check-reliability-gates.mjs` (140 gates), `check:code-quality:changed` (0 new findings).
208 lines
7.5 KiB
TypeScript
208 lines
7.5 KiB
TypeScript
/**
|
|
* End-to-end in-process integration test.
|
|
*
|
|
* Wires the client-side SshChannelMultiplexer directly to the relay-side
|
|
* RelayDispatcher through an in-memory pipe — no SSH, no subprocess.
|
|
* Validates the full JSON-RPC roundtrip: client request → framing →
|
|
* relay decode → handler → response → framing → client decode → result.
|
|
*/
|
|
import { describe, expect, it, beforeEach, afterEach } from 'vitest'
|
|
import { mkdtempSync, writeFileSync } from 'node:fs'
|
|
import { rm, readFile } from 'node:fs/promises'
|
|
import * as path from 'node:path'
|
|
import { tmpdir } from 'node:os'
|
|
import { execFileSync } from 'node:child_process'
|
|
import { randomBytes } from 'node:crypto'
|
|
|
|
import {
|
|
SshChannelMultiplexer,
|
|
type MultiplexerTransport
|
|
} from '../main/ssh/ssh-channel-multiplexer'
|
|
|
|
import { RelayDispatcher } from './dispatcher'
|
|
import { RelayContext } from './context'
|
|
import { FsHandler } from './fs-handler'
|
|
import { GitHandler } from './git-handler'
|
|
|
|
function gitInit(dir: string): void {
|
|
execFileSync('git', ['init'], { cwd: dir, stdio: 'pipe' })
|
|
execFileSync('git', ['config', 'user.email', 'test@test.com'], { cwd: dir, stdio: 'pipe' })
|
|
execFileSync('git', ['config', 'user.name', 'Test'], { cwd: dir, stdio: 'pipe' })
|
|
}
|
|
|
|
function gitCommit(dir: string, message: string): void {
|
|
execFileSync('git', ['add', '.'], { cwd: dir, stdio: 'pipe' })
|
|
execFileSync('git', ['commit', '-m', message], { cwd: dir, stdio: 'pipe' })
|
|
}
|
|
|
|
describe('Integration: Client Mux ↔ Relay Dispatcher', () => {
|
|
let tmpDir: string
|
|
let mux: SshChannelMultiplexer
|
|
let dispatcher: RelayDispatcher
|
|
let fsHandler: FsHandler
|
|
beforeEach(() => {
|
|
tmpDir = mkdtempSync(path.join(tmpdir(), 'relay-e2e-'))
|
|
|
|
// Build the in-memory pipe
|
|
let relayFeedFn: (data: Buffer) => void
|
|
|
|
const clientDataCallbacks: ((data: Buffer) => void)[] = []
|
|
const clientCloseCallbacks: (() => void)[] = []
|
|
|
|
const clientTransport: MultiplexerTransport = {
|
|
write: (data: Buffer) => {
|
|
// Client → Relay
|
|
setImmediate(() => relayFeedFn?.(data))
|
|
},
|
|
onData: (cb) => {
|
|
clientDataCallbacks.push(cb)
|
|
},
|
|
onClose: (cb) => {
|
|
clientCloseCallbacks.push(cb)
|
|
}
|
|
}
|
|
|
|
// Relay side
|
|
dispatcher = new RelayDispatcher((data: Buffer) => {
|
|
// Relay → Client
|
|
setImmediate(() => {
|
|
for (const cb of clientDataCallbacks) {
|
|
cb(data)
|
|
}
|
|
})
|
|
})
|
|
|
|
relayFeedFn = (data: Buffer) => dispatcher.feed(data)
|
|
|
|
// Register handlers on the relay
|
|
const context = new RelayContext()
|
|
fsHandler = new FsHandler(dispatcher, context)
|
|
new GitHandler(dispatcher, context)
|
|
|
|
// Create client mux
|
|
mux = new SshChannelMultiplexer(clientTransport)
|
|
})
|
|
|
|
afterEach(async () => {
|
|
mux.dispose()
|
|
dispatcher.dispose()
|
|
fsHandler.dispose()
|
|
await rm(tmpDir, { recursive: true, force: true })
|
|
})
|
|
|
|
// ─── Filesystem ─────────────────────────────────────────────────
|
|
|
|
describe('Filesystem operations', () => {
|
|
it('readDir returns directory entries', async () => {
|
|
writeFileSync(path.join(tmpDir, 'hello.txt'), 'world')
|
|
writeFileSync(path.join(tmpDir, 'readme.md'), '# Hi')
|
|
|
|
const result = (await mux.request('fs.readDir', { dirPath: tmpDir })) as {
|
|
name: string
|
|
isDirectory: boolean
|
|
isSymlink: boolean
|
|
}[]
|
|
|
|
expect(result.length).toBe(2)
|
|
const names = result.map((e) => e.name).sort()
|
|
expect(names).toEqual(['hello.txt', 'readme.md'])
|
|
})
|
|
|
|
it('readFile returns text content', async () => {
|
|
writeFileSync(path.join(tmpDir, 'data.txt'), 'some content')
|
|
|
|
const result = (await mux.request('fs.readFile', {
|
|
filePath: path.join(tmpDir, 'data.txt')
|
|
})) as { content: string; isBinary: boolean }
|
|
|
|
expect(result.content).toBe('some content')
|
|
expect(result.isBinary).toBe(false)
|
|
})
|
|
|
|
// Why this one stays: writeRelayFile resolves to undefined, so the response frame
|
|
// carries neither `result` nor `error` — the one payload shape the other cases here
|
|
// do not produce, and one a mock dispatcher never has to settle. The client sees
|
|
// `null`, not `undefined`: the member is absent from the frame, not carried as void.
|
|
it('writeFile creates/overwrites file content', async () => {
|
|
const filePath = path.join(tmpDir, 'output.txt')
|
|
|
|
await expect(
|
|
mux.request('fs.writeFile', { filePath, content: 'written via relay' })
|
|
).resolves.toBeNull()
|
|
|
|
const content = await readFile(filePath, 'utf-8')
|
|
expect(content).toBe('written via relay')
|
|
})
|
|
|
|
it('readFileStream round-trip preserves a 12 MB binary file', async () => {
|
|
const filePath = path.join(tmpDir, 'big.png')
|
|
const original = randomBytes(12 * 1024 * 1024)
|
|
writeFileSync(filePath, original)
|
|
const { readFileViaStream } = await import('../main/ssh/ssh-filesystem-stream-reader')
|
|
const { content } = await readFileViaStream(mux, filePath)
|
|
expect(Buffer.from(content, 'base64').equals(original)).toBe(true)
|
|
}, 30_000)
|
|
})
|
|
|
|
// ─── Git ────────────────────────────────────────────────────────
|
|
|
|
describe('Git operations', () => {
|
|
beforeEach(() => {
|
|
gitInit(tmpDir)
|
|
writeFileSync(path.join(tmpDir, 'file.txt'), 'initial')
|
|
gitCommit(tmpDir, 'initial commit')
|
|
})
|
|
|
|
it('git.diff returns original and modified content', async () => {
|
|
writeFileSync(path.join(tmpDir, 'file.txt'), 'updated content')
|
|
|
|
const result = (await mux.request('git.diff', {
|
|
worktreePath: tmpDir,
|
|
filePath: 'file.txt',
|
|
staged: false
|
|
})) as { kind: string; originalContent: string; modifiedContent: string }
|
|
|
|
expect(result.kind).toBe('text')
|
|
expect(result.originalContent).toBe('initial')
|
|
expect(result.modifiedContent).toBe('updated content')
|
|
})
|
|
})
|
|
|
|
// ─── Error propagation ──────────────────────────────────────────
|
|
|
|
describe('Error propagation', () => {
|
|
it('method-not-found error for unknown methods', async () => {
|
|
await expect(mux.request('nonexistent.method', {})).rejects.toThrow('Method not found')
|
|
})
|
|
|
|
it('handler errors propagate as JSON-RPC errors', async () => {
|
|
await expect(
|
|
mux.request('fs.readFile', { filePath: '/does/not/exist/at/all' })
|
|
).rejects.toThrow()
|
|
})
|
|
})
|
|
|
|
// ─── Notifications ──────────────────────────────────────────────
|
|
|
|
describe('Notifications', () => {
|
|
it('relay notifications reach the client mux', async () => {
|
|
const received: { method: string; params: Record<string, unknown> }[] = []
|
|
mux.onNotification((method, params) => {
|
|
received.push({ method, params })
|
|
})
|
|
|
|
// Trigger a fs operation that causes the relay to send notifications
|
|
// (e.g., write a file — no notification expected for this, so we
|
|
// test notification plumbing directly via the relay dispatcher)
|
|
dispatcher.notify('custom.event', { key: 'value' })
|
|
|
|
// Wait for the async delivery through setImmediate
|
|
await new Promise((r) => setTimeout(r, 50))
|
|
|
|
expect(received.length).toBe(1)
|
|
expect(received[0].method).toBe('custom.event')
|
|
expect(received[0].params).toEqual({ key: 'value' })
|
|
})
|
|
})
|
|
})
|