mirror of
https://github.com/EKKOLearnAI/hermes-web-ui.git
synced 2026-07-11 11:10:21 +00:00
168 lines
5.8 KiB
TypeScript
168 lines
5.8 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
const managerMock = vi.hoisted(() => ({
|
|
get: vi.fn(),
|
|
deleteRun: vi.fn(),
|
|
rerunFromNode: vi.fn(),
|
|
runNow: vi.fn(),
|
|
stopRun: vi.fn(),
|
|
}))
|
|
const listWorkflowRunsMock = vi.hoisted(() => vi.fn())
|
|
const listWorkflowRunNodeSessionsMock = vi.hoisted(() => vi.fn())
|
|
const listUserProfilesMock = vi.hoisted(() => vi.fn())
|
|
|
|
vi.mock('../../packages/server/src/services/workflow-manager', () => ({
|
|
getWorkflowManager: () => managerMock,
|
|
}))
|
|
|
|
vi.mock('../../packages/server/src/db/hermes/users-store', () => ({
|
|
listUserProfiles: listUserProfilesMock,
|
|
}))
|
|
|
|
vi.mock('../../packages/server/src/db/hermes/workflow-run-store', () => ({
|
|
listWorkflowRunNodeSessions: listWorkflowRunNodeSessionsMock,
|
|
listWorkflowRuns: listWorkflowRunsMock,
|
|
}))
|
|
|
|
function ctx(overrides: Record<string, any> = {}) {
|
|
return {
|
|
params: {},
|
|
query: {},
|
|
request: { body: {} },
|
|
state: {},
|
|
status: 200,
|
|
body: undefined,
|
|
...overrides,
|
|
} as any
|
|
}
|
|
|
|
describe('workflow controller', () => {
|
|
beforeEach(() => {
|
|
managerMock.get.mockReset()
|
|
managerMock.deleteRun.mockReset()
|
|
managerMock.rerunFromNode.mockReset()
|
|
managerMock.runNow.mockReset()
|
|
managerMock.stopRun.mockReset()
|
|
listWorkflowRunNodeSessionsMock.mockReset()
|
|
listWorkflowRunsMock.mockReset()
|
|
listUserProfilesMock.mockReset()
|
|
listUserProfilesMock.mockReturnValue([])
|
|
})
|
|
|
|
it('lists run records for a workflow', async () => {
|
|
managerMock.get.mockReturnValue({ id: 'workflow-1', profile: 'default' })
|
|
listWorkflowRunsMock.mockReturnValue([{ id: 'run-1', workflow_id: 'workflow-1', status: 'completed' }])
|
|
listWorkflowRunNodeSessionsMock.mockReturnValue([{ id: 'node-session-1', node_id: 'node-1', status: 'completed' }])
|
|
|
|
const mod = await import('../../packages/server/src/controllers/hermes/workflows')
|
|
const c = ctx({ params: { id: 'workflow-1' }, query: { limit: '25' } })
|
|
|
|
await mod.listRuns(c)
|
|
|
|
expect(listWorkflowRunsMock).toHaveBeenCalledWith('workflow-1', 25)
|
|
expect(listWorkflowRunNodeSessionsMock).toHaveBeenCalledWith('run-1')
|
|
expect(c.body).toEqual({
|
|
runs: [{
|
|
id: 'run-1',
|
|
workflow_id: 'workflow-1',
|
|
status: 'completed',
|
|
node_sessions: [{ id: 'node-session-1', node_id: 'node-1', status: 'completed' }],
|
|
}],
|
|
})
|
|
})
|
|
|
|
it('runs a workflow through the workflow manager', async () => {
|
|
const user = { id: 'user-1', role: 'super_admin' }
|
|
managerMock.get.mockReturnValue({ id: 'workflow-1', profile: 'default' })
|
|
managerMock.runNow.mockResolvedValue({ run: { id: 'run-1', status: 'completed' }, nodeSessions: [] })
|
|
|
|
const mod = await import('../../packages/server/src/controllers/hermes/workflows')
|
|
const c = ctx({
|
|
params: { id: 'workflow-1' },
|
|
request: { body: { start_node_ids: ['node-1', 12, 'node-2'], input: 'go', timeout_ms: '1000' } },
|
|
state: { user },
|
|
})
|
|
|
|
await mod.runNow(c)
|
|
|
|
expect(managerMock.runNow).toHaveBeenCalledWith('workflow-1', {
|
|
profile: 'default',
|
|
user,
|
|
startNodeIds: ['node-1', 'node-2'],
|
|
input: 'go',
|
|
timeoutMs: 1000,
|
|
})
|
|
expect(c.status).toBe(202)
|
|
expect(c.body).toEqual({ ok: true, status: 'accepted' })
|
|
})
|
|
|
|
it('stops a workflow run through the workflow manager', async () => {
|
|
managerMock.get.mockReturnValue({ id: 'workflow-1', profile: 'default' })
|
|
managerMock.stopRun.mockResolvedValue({ id: 'run-1', workflow_id: 'workflow-1', status: 'canceled' })
|
|
|
|
const mod = await import('../../packages/server/src/controllers/hermes/workflows')
|
|
const c = ctx({ params: { id: 'workflow-1', runId: 'run-1' } })
|
|
|
|
await mod.stopRun(c)
|
|
|
|
expect(managerMock.stopRun).toHaveBeenCalledWith('workflow-1', 'run-1', 'Workflow run canceled by user')
|
|
expect(c.body).toEqual({
|
|
ok: true,
|
|
run: { id: 'run-1', workflow_id: 'workflow-1', status: 'canceled' },
|
|
})
|
|
})
|
|
|
|
it('reruns a workflow run from a node through the workflow manager', async () => {
|
|
const user = { id: 'user-1', role: 'super_admin' }
|
|
managerMock.get.mockReturnValue({ id: 'workflow-1', profile: 'default' })
|
|
managerMock.rerunFromNode.mockResolvedValue({ run: { id: 'run-1', status: 'completed' }, nodeSessions: [] })
|
|
|
|
const mod = await import('../../packages/server/src/controllers/hermes/workflows')
|
|
const c = ctx({
|
|
params: { id: 'workflow-1', runId: 'run-1' },
|
|
request: { body: { node_id: 'node-2', preserve_start_node: true, timeout_ms: '1000' } },
|
|
state: { user },
|
|
})
|
|
|
|
await mod.rerunFromNode(c)
|
|
|
|
expect(managerMock.rerunFromNode).toHaveBeenCalledWith('workflow-1', 'run-1', 'node-2', {
|
|
profile: 'default',
|
|
user,
|
|
preserveStartNode: true,
|
|
timeoutMs: 1000,
|
|
})
|
|
expect(c.status).toBe(202)
|
|
expect(c.body).toEqual({ ok: true, status: 'accepted' })
|
|
})
|
|
|
|
it('deletes a workflow run through the workflow manager', async () => {
|
|
managerMock.get.mockReturnValue({ id: 'workflow-1', profile: 'default' })
|
|
managerMock.deleteRun.mockResolvedValue(true)
|
|
|
|
const mod = await import('../../packages/server/src/controllers/hermes/workflows')
|
|
const c = ctx({ params: { id: 'workflow-1', runId: 'run-1' } })
|
|
|
|
await mod.deleteRun(c)
|
|
|
|
expect(managerMock.deleteRun).toHaveBeenCalledWith('workflow-1', 'run-1')
|
|
expect(c.body).toEqual({ ok: true })
|
|
})
|
|
|
|
it('rejects workflow runs for unavailable profiles', async () => {
|
|
managerMock.get.mockReturnValue({ id: 'workflow-1', profile: 'secret' })
|
|
listUserProfilesMock.mockReturnValue([{ profile_name: 'default' }])
|
|
|
|
const mod = await import('../../packages/server/src/controllers/hermes/workflows')
|
|
const c = ctx({
|
|
params: { id: 'workflow-1' },
|
|
state: { user: { id: 'user-1', role: 'user' } },
|
|
})
|
|
|
|
await mod.runNow(c)
|
|
|
|
expect(c.status).toBe(403)
|
|
expect(managerMock.runNow).not.toHaveBeenCalled()
|
|
})
|
|
})
|