Files
goodbuddy/src/main/agent/runtime-discovery.test.ts
T
mesalogo c1848a9dc4
GoodBuddy Agents / Validate Agent source (push) Canceled after 0s
GoodBuddy Agents / Build darwin arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux arm64 Agent (push) Canceled after 0s
GoodBuddy Agents / Build linux x64 Agent (push) Canceled after 0s
Cross-platform packages / Validate source (push) Canceled after 0s
Cross-platform packages / Authorize desktop packaging (push) Canceled after 0s
Cross-platform packages / linux arm64 (push) Canceled after 0s
Cross-platform packages / macos arm64 (push) Canceled after 0s
Cross-platform packages / windows arm64 (push) Canceled after 0s
Cross-platform packages / linux x64 (push) Canceled after 0s
Cross-platform packages / macos x64 (push) Canceled after 0s
Cross-platform packages / windows x64 (push) Canceled after 0s
Cross-platform packages / Publish GitHub and OSS release (push) Canceled after 0s
test: align runtime fixtures across platforms
Account for Electron-as-Node when probing the current executable and update the Linux workspace registry scenario to validate the writable handle behavior shipped with remote file management.
2026-09-10 07:01:48 +08:00

275 lines
7.8 KiB
TypeScript

import { EventEmitter } from 'node:events'
import { realpath } from 'node:fs/promises'
import { basename, dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
import { afterEach, describe, expect, it, vi } from 'vitest'
import {
detectAgentRuntimes,
detectRuntimeBinary,
validateRuntimeVersion,
type RuntimeVersionProcess
} from './runtime-discovery'
const originalPath = process.env.PATH
const originalPathCase = process.env.Path
afterEach(() => {
if (originalPath === undefined) {
delete process.env.PATH
} else {
process.env.PATH = originalPath
}
if (originalPathCase === undefined) {
delete process.env.Path
} else {
process.env.Path = originalPathCase
}
})
describe('runtime discovery', () => {
it('launches a detached POSIX version probe and awaits bounded tree cleanup', async () => {
const child =
new EventEmitter() as RuntimeVersionProcess & EventEmitter
child.exitCode = null
child.pid = 2718
child.kill = vi.fn()
child.stdout = new EventEmitter()
child.stderr = new EventEmitter()
const spawnProcess = vi.fn(() => child)
let releaseCleanup!: () => void
const cleanupBlocked = new Promise<void>((resolve) => {
releaseCleanup = resolve
})
const terminateProcessTree = vi.fn(async () => {
await cleanupBlocked
})
const validation = validateRuntimeVersion('runtime', {
platform: 'linux',
spawnProcess,
terminateProcessTree,
outputLimit: 4,
terminationWaitMs: 25
})
;(child.stdout as EventEmitter).emit('data', '12345')
let completed = false
void validation.then(() => {
completed = true
})
await Promise.resolve()
expect(completed).toBe(false)
expect(spawnProcess).toHaveBeenCalledWith(
'runtime',
['--version'],
expect.objectContaining({ detached: true })
)
expect(terminateProcessTree).toHaveBeenCalledWith(child, {
platform: 'linux',
processGroup: true,
signal: 'SIGKILL',
waitMs: 25
})
releaseCleanup()
await expect(validation).resolves.toEqual({ valid: false })
})
it('canonicalizes and validates a configured ordinary file first', async () => {
process.env.PATH = ''
process.env.Path = ''
const detection = await detectRuntimeBinary({
binaryPath: process.execPath,
binaryNames: ['binary-that-does-not-exist'],
label: 'Test CLI'
})
expect(detection).toMatchObject({
available: true,
path: await realpath(process.execPath),
source: 'configured'
})
if (process.versions.electron) {
expect(detection.version).toBeUndefined()
} else {
expect(detection.version).toMatch(/^\d+\.\d+\.\d+/u)
}
})
it('trusts a configured execution path without reading or launching it', async () => {
process.env.PATH = ''
process.env.Path = ''
const candidate = resolve(
dirname(fileURLToPath(import.meta.url)),
'runtime-that-does-not-exist'
)
const detection = await detectRuntimeBinary({
binaryPath: candidate,
validation: 'path-only',
binaryNames: ['binary-that-does-not-exist'],
label: 'Managed Runtime'
})
expect(detection).toEqual({
available: true,
path: candidate,
source: 'configured',
detail: 'Managed Runtime 已就绪'
})
})
it('rejects relative configured paths without resolving them from cwd', async () => {
process.env.PATH = ''
process.env.Path = ''
await expect(
detectRuntimeBinary({
binaryPath: 'relative/runtime',
binaryNames: ['goodbuddy-runtime-that-does-not-exist'],
label: 'Test CLI'
})
).resolves.toEqual({
available: false,
detail: expect.stringContaining('必须为绝对路径')
})
})
it('finds executable names from absolute PATH directories', async () => {
process.env.PATH = dirname(process.execPath)
process.env.Path = dirname(process.execPath)
const detection = await detectRuntimeBinary({
binaryPath: '',
binaryNames: [basename(process.execPath)],
label: 'Test CLI'
})
expect(detection).toMatchObject({
available: true,
path: await realpath(process.execPath),
source: 'automatic'
})
})
it('prefers a configured binary over the bundled runtime', async () => {
const detection = await detectRuntimeBinary({
binaryPath: process.execPath,
bundledPath: process.execPath,
binaryNames: ['goodbuddy-runtime-that-does-not-exist'],
label: 'Test CLI'
})
expect(detection).toMatchObject({
available: true,
path: await realpath(process.execPath),
source: 'configured'
})
expect(detection.detail).not.toContain('内置')
})
it('prefers a bundled runtime over PATH discovery', async () => {
process.env.PATH = dirname(process.execPath)
process.env.Path = dirname(process.execPath)
const detection = await detectRuntimeBinary({
binaryPath: '',
bundledPath: process.execPath,
binaryNames: [basename(process.execPath)],
label: 'Test CLI'
})
expect(detection).toMatchObject({
available: true,
path: await realpath(process.execPath),
source: 'bundled'
})
expect(detection.detail).toContain('内置')
})
it('allows a bundled script to defer execution validation to its host adapter', async () => {
process.env.PATH = ''
process.env.Path = ''
const bundledScript = fileURLToPath(import.meta.url)
const detection = await detectRuntimeBinary({
binaryPath: '',
bundledPath: bundledScript,
bundledValidation: 'canonical-file',
bundledVersion: '1.5.47',
binaryNames: ['goodbuddy-runtime-that-does-not-exist'],
label: 'Script Runtime'
})
expect(detection).toMatchObject({
available: true,
path: await realpath(bundledScript),
version: '1.5.47',
source: 'bundled'
})
expect(detection.detail).toBe(
'内置 Script Runtime 1.5.47 已就绪'
)
})
it('accepts a controlled bundled harness when no custom host is configured', async () => {
const bundledScript = fileURLToPath(import.meta.url)
const detection = await detectRuntimeBinary({
binaryPath: '',
bundledPath: bundledScript,
bundledValidation: 'canonical-file',
bundledVersion: '0.1.2-rc.1',
binaryNames: [],
label: 'GoodBuddy DeepSeek Harness Host'
})
expect(detection).toMatchObject({
available: true,
path: await realpath(bundledScript),
version: '0.1.2-rc.1',
source: 'bundled'
})
expect(detection.detail).toContain('内置')
})
it('does not discover arbitrary DeepSeek Harness hosts from PATH', async () => {
process.env.PATH = dirname(process.execPath)
process.env.Path = dirname(process.execPath)
await expect(
detectRuntimeBinary({
binaryPath: '',
allowAutomaticDiscovery: false,
binaryNames: [basename(process.execPath)],
label: 'GoodBuddy DeepSeek Harness Host'
})
).resolves.toEqual({
available: false,
detail: expect.stringContaining('未自动检测到')
})
})
it('returns both runtime detections without exposing PATH contents', async () => {
const privatePathValue = `${dirname(process.execPath)}-private-path-value`
process.env.PATH = privatePathValue
process.env.Path = privatePathValue
const result = await detectAgentRuntimes({
opencodeBinaryPath: process.execPath,
continueBinaryPath: process.execPath
})
expect(result.opencode).toMatchObject({
available: true,
path: await realpath(process.execPath)
})
expect(result.continue).toMatchObject({
available: true,
path: await realpath(process.execPath)
})
expect(result.deepseekHarness.available).toBe(false)
expect(JSON.stringify(result)).not.toContain(privatePathValue)
})
})