Files
hermes-web-ui/tests/server/chat-run-api-controller.test.ts
ekko 83e37bac02 feat: add outbound relay and MCU login flow (#1580)
* add global agent chat relay

* add model run MCP auth

* avoid codex prompt inflation

* guide codex to use hermes lan tools

* truncate long tool call previews

* write codex MCP env inline

* log codex developer prompt

* remove codex prompt debug logging

* fix codex chat tool history

* log codex chat proxy requests

* write codex proxy debug to server log

* log codex upstream tool requests

* expand codex mcp namespaces for custom providers

* remove codex proxy debug logging

* add hermes mcp openapi relay

* fix: improve mcp docs and chat run cleanup

* feat: add mcu relay login flow

* fix ci test expectations

* fix group chat deeplink e2e expectation

---------

Co-authored-by: Codex <codex@openai.com>
2026-06-16 09:16:26 +08:00

153 lines
4.6 KiB
TypeScript

import { EventEmitter } from 'events'
import { beforeEach, describe, expect, it, vi } from 'vitest'
const ioMock = vi.hoisted(() => vi.fn())
vi.mock('socket.io-client', () => ({
io: ioMock,
}))
function makeSocket() {
const emitter = new EventEmitter() as EventEmitter & {
emit: ReturnType<typeof vi.fn>
disconnect: ReturnType<typeof vi.fn>
emitNative: (event: string, payload?: unknown) => boolean
}
const nativeEmit = EventEmitter.prototype.emit.bind(emitter)
emitter.emitNative = nativeEmit
emitter.emit = vi.fn((event: string, payload?: unknown) => {
if (event === 'run') {
process.nextTick(() => {
nativeEmit('run.started', { event: 'run.started', run_id: 'run-1' })
nativeEmit('message.delta', { event: 'message.delta', run_id: 'run-1', delta: 'hello' })
nativeEmit('run.completed', { event: 'run.completed', run_id: 'run-1' })
})
}
return true
}) as any
emitter.disconnect = vi.fn()
return emitter
}
describe('chat-run HTTP API controller', () => {
beforeEach(() => {
vi.clearAllMocks()
})
it('runs chat-run through Socket.IO and returns a completed HTTP response', async () => {
const socket = makeSocket()
ioMock.mockReturnValue(socket)
const { runOnce } = await import('../../packages/server/src/controllers/chat-run')
const ctx = {
get: vi.fn((name: string) => name.toLowerCase() === 'authorization' ? 'Bearer token-1' : ''),
state: { profile: { name: 'default' } },
request: {
body: {
session_id: 'session-1',
input: 'hello',
include_events: true,
},
},
status: 200,
body: undefined as any,
}
const pending = runOnce(ctx as any)
socket.emitNative('connect')
await pending
expect(ioMock).toHaveBeenCalledWith(expect.stringContaining('/chat-run'), expect.objectContaining({
auth: { token: 'token-1' },
query: { profile: 'default' },
}))
expect(socket.emit).toHaveBeenCalledWith('run', expect.objectContaining({
session_id: 'session-1',
input: 'hello',
profile: 'default',
}))
expect(ctx.status).toBe(200)
expect(ctx.body).toMatchObject({
ok: true,
status: 'completed',
session_id: 'session-1',
run_id: 'run-1',
output: 'hello',
})
expect(ctx.body.events).toHaveLength(3)
})
it('generates a session id when none is provided', async () => {
const socket = makeSocket()
ioMock.mockReturnValue(socket)
const { runOnce } = await import('../../packages/server/src/controllers/chat-run')
const ctx = {
get: vi.fn((name: string) => name.toLowerCase() === 'authorization' ? 'Bearer token-1' : ''),
state: { profile: { name: 'default' } },
request: {
body: {
source: 'cli',
input: 'start a new chat',
},
},
status: 200,
body: undefined as any,
}
const pending = runOnce(ctx as any)
socket.emitNative('connect')
await pending
const emittedPayload = socket.emit.mock.calls.find(call => call[0] === 'run')?.[1] as Record<string, unknown>
expect(emittedPayload.session_id).toEqual(expect.stringMatching(/^[0-9a-f-]{36}$/))
expect(emittedPayload).toMatchObject({
source: 'cli',
input: 'start a new chat',
profile: 'default',
})
expect(ctx.status).toBe(200)
expect(ctx.body).toMatchObject({
ok: true,
status: 'completed',
session_id: emittedPayload.session_id,
})
})
it('generates a session id for global-agent runs when none is provided', async () => {
const socket = makeSocket()
ioMock.mockReturnValue(socket)
const { runOnce } = await import('../../packages/server/src/controllers/chat-run')
const ctx = {
get: vi.fn((name: string) => name.toLowerCase() === 'authorization' ? 'Bearer token-1' : ''),
state: { profile: { name: 'default' } },
request: {
body: {
source: 'global_agent',
input: 'start a global run',
},
},
status: 200,
body: undefined as any,
}
const pending = runOnce(ctx as any)
socket.emitNative('connect')
await pending
const emittedPayload = socket.emit.mock.calls.find(call => call[0] === 'run')?.[1] as Record<string, unknown>
expect(emittedPayload.session_id).toEqual(expect.stringMatching(/^[0-9a-f-]{36}$/))
expect(emittedPayload).toMatchObject({
source: 'global_agent',
input: 'start a global run',
profile: 'default',
})
expect(ctx.body).toMatchObject({
ok: true,
status: 'completed',
session_id: emittedPayload.session_id,
})
})
})