mirror of
https://github.com/stablyai/orca.git
synced 2026-10-31 00:57:45 +00:00
* fix(wsl): start host-side wsl.exe probes through process-host so they never open a console wsl --list, wsl --status, the WSL home and directory probes, and WSL UNC deletes were started with node:child_process directly. The synchronous ones omitted windowsHide, so each spawn from the GUI-subsystem main process flashed a conhost window that could take keyboard focus. Route them through @orca/process-host, which pins windowsHide, and keep the execFile-shaped exit errors the availability cache classifies. Refs #27390 * build(tc): list wsl-exe-process.ts in the web typecheck project wsl-availability.ts is in the explicit web file list and now imports it. * refactor(wsl): drop the unused sync availability probe; classify ProcessResult directly No production code calls the sync isWslAvailable (the IPC and RPC paths already assert they never do), so delete it and its sync guest-probe twin instead of porting them. The async probe now reads the --status ProcessResult itself: an exit code is definitive, a timeout or signal kill is retryable, ENOENT on spawn is definitive. The WSL2-missing-kernel exit codes live in one named set. Availability tests now drive the async probe. * refactor(wsl): shrink wsl-exe-process to a spec builder and stdout reader With the availability cache classifying ProcessResult itself, nothing reads the execFile-shaped code/status error any more. wslExeFailure returns a plain reason string; wslExeStdout throws it; the UNC delete folds it into its own error. The home probe builds its argv with buildWslExecArgs. * test(wsl): mock process-host in the empty running-list case from main * docs(wsl): the availability probe no longer blocks; drop the stale word * build(ratchet): prune the wsl.exe sync launches this branch removed --------- Co-authored-by: Orca Worker <orca-worker@localhost>
89 lines
2.9 KiB
TypeScript
89 lines
2.9 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import { runProcess } from '@orca/process-host'
|
|
import type { ProcessResult, ProcessSpec } from '@orca/process-host/process-spec'
|
|
import { _resetWslAvailabilityCacheForTests, isWslAvailableAsync } from './wsl-availability'
|
|
|
|
vi.mock('@orca/process-host', () => ({
|
|
runProcess: vi.fn()
|
|
}))
|
|
vi.mock('./wsl-interop-spawn-directory', () => ({
|
|
resolveWslInteropSpawnCwd: () => 'C:\\Windows'
|
|
}))
|
|
|
|
const originalPlatform = process.platform
|
|
const success: ProcessResult = { code: 0, signal: null, stdout: '', stderr: '', timedOut: false }
|
|
|
|
describe('WSL1 availability without WSL2 kernel', () => {
|
|
const guestCalls = () =>
|
|
vi.mocked(runProcess).mock.calls.filter(([spec]) => spec.args?.[0] === '--exec')
|
|
let statusCode = 0
|
|
let guest: () => ProcessResult = () => success
|
|
|
|
function failStatus(code: number): void {
|
|
statusCode = code
|
|
}
|
|
function guestResult(result: ProcessResult): void {
|
|
guest = () => result
|
|
}
|
|
function respond(spec: ProcessSpec): ProcessResult {
|
|
return spec.args?.[0] === '--status' ? { ...success, code: statusCode } : guest()
|
|
}
|
|
|
|
beforeEach(() => {
|
|
vi.resetAllMocks()
|
|
Object.defineProperty(process, 'platform', { value: 'win32' })
|
|
_resetWslAvailabilityCacheForTests()
|
|
statusCode = 0
|
|
guest = () => success
|
|
vi.mocked(runProcess).mockImplementation(async (spec) => respond(spec))
|
|
})
|
|
afterEach(() => {
|
|
Object.defineProperty(process, 'platform', { value: originalPlatform })
|
|
_resetWslAvailabilityCacheForTests()
|
|
})
|
|
|
|
// Node reports the Windows DWORD; the console prints its signed equivalent.
|
|
for (const status of [-444, 4_294_966_852]) {
|
|
it(`requires guest execution and caches its success for ${status}`, async () => {
|
|
failStatus(status)
|
|
guestResult(success)
|
|
expect(await isWslAvailableAsync()).toBe(true)
|
|
expect(await isWslAvailableAsync()).toBe(true)
|
|
expect(guestCalls()).toHaveLength(1)
|
|
expect(runProcess).toHaveBeenCalledWith(
|
|
expect.objectContaining({
|
|
program: 'wsl.exe',
|
|
args: ['--exec', '/bin/true'],
|
|
timeoutMs: 5000,
|
|
cwd: 'C:\\Windows'
|
|
})
|
|
)
|
|
})
|
|
}
|
|
|
|
for (const result of [
|
|
{ ...success, code: 1 },
|
|
{ ...success, code: null, timedOut: true }
|
|
]) {
|
|
it(`keeps a failed guest unavailable: ${JSON.stringify(result)}`, async () => {
|
|
failStatus(-444)
|
|
guestResult(result)
|
|
expect(await isWslAvailableAsync()).toBe(false)
|
|
})
|
|
}
|
|
|
|
it('stays unavailable when the guest probe cannot be spawned', async () => {
|
|
failStatus(-444)
|
|
guest = () => {
|
|
throw new Error('EPERM')
|
|
}
|
|
expect(await isWslAvailableAsync()).toBe(false)
|
|
})
|
|
|
|
it('does not probe a guest for unrelated status failures', async () => {
|
|
failStatus(1)
|
|
expect(await isWslAvailableAsync()).toBe(false)
|
|
expect(guestCalls()).toHaveLength(0)
|
|
})
|
|
})
|