Files
hermes-ui/tests/server/profile-delete-managed-gateway.test.ts
toor a1fff482bf make the test suite and openapi generator platform-independent
Windows checkouts turned LF into CRLF (core.autocrlf=true), and several
tests plus the openapi generator hardcoded LF or POSIX path separators.
The result was a suite that was green on Linux CI but red on Windows, and
an openapi.json whose /api/hermes/* routes silently disappeared when
generated on Windows.

Pin the repository to LF via .gitattributes, strip shebangs before
Vite's SSR transform, and key route files by their posix path. Add a
manually triggered macOS verification workflow so the same checks run
on a self-hosted macOS runner.
2026-10-20 11:17:34 +08:00

83 lines
2.8 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { EventEmitter } from 'events'
import { mkdir, mkdtemp, rm, writeFile } from 'fs/promises'
import { tmpdir } from 'os'
import { join } from 'path'
import { afterEach, describe, expect, it, vi } from 'vitest'
const originalEnv = { ...process.env }
const originalPlatform = Object.getOwnPropertyDescriptor(process, 'platform')
class FakeChild extends EventEmitter {
pid: number
killSignals: string[] = []
constructor(pid: number) {
super()
this.pid = pid
}
unref() { /* no-op */ }
kill(signal?: string) {
this.killSignals.push(signal || 'SIGTERM')
return true
}
}
let fakeChildren: FakeChild[] = []
vi.mock('../../packages/server/src/services/hermes/hermes-process', () => ({
execHermesWithBin: vi.fn().mockResolvedValue({ stdout: '', stderr: '' }),
spawnHermesWithBin: vi.fn(() => {
const child = new FakeChild(20000 + fakeChildren.length)
fakeChildren.push(child)
return child
}),
}))
afterEach(() => {
vi.useRealTimers()
vi.restoreAllMocks()
vi.resetModules()
process.env = { ...originalEnv }
if (originalPlatform) Object.defineProperty(process, 'platform', originalPlatform)
fakeChildren = []
})
describe('profile delete managed gateway lifecycle', () => {
it('reproduces #1633 and proves delete prep suppresses managed respawn', async () => {
vi.useFakeTimers()
vi.resetModules()
// 固定 platform:停止网关的手段随平台不同(POSIX 走 child.kill('SIGTERM'),
// Windows 走 taskkill 杀进程树)。不固定的话这个用例在 Windows 上会因为
// taskkill 是异步的而读不到 killSignals,而且会真的拿假 pid 去杀进程。
// Windows 分支由 gateway-respawn.test.ts 单独覆盖。
Object.defineProperty(process, 'platform', { value: 'linux' })
const home = await mkdtemp(join(tmpdir(), 'wui-1633-'))
process.env.HERMES_HOME = home
process.env.HERMES_BIN = '/usr/bin/hermes'
const profileDir = join(home, 'profiles', 'work')
await mkdir(profileDir, { recursive: true })
await writeFile(join(profileDir, 'config.yaml'), 'model:\n default: test\n', 'utf-8')
try {
const { startGatewayRunManaged } = await import('../../packages/server/src/services/hermes/gateway-runner')
const { prepareGatewayForProfileDelete } = await import('../../packages/server/src/services/hermes/gateway-autostart')
startGatewayRunManaged('/usr/bin/hermes', { profileDir })
expect(fakeChildren).toHaveLength(1)
const prep = prepareGatewayForProfileDelete('work')
expect(fakeChildren[0].killSignals).toEqual(['SIGTERM'])
fakeChildren[0].emit('exit', 0, 'SIGTERM')
await prep
await vi.advanceTimersByTimeAsync(6000)
expect(fakeChildren).toHaveLength(1)
} finally {
await rm(home, { recursive: true, force: true })
}
})
})