68 lines
3.0 KiB
TypeScript
68 lines
3.0 KiB
TypeScript
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
const mockExecFileAsync = vi.hoisted(() => vi.fn())
|
|
const mockLoggerError = vi.hoisted(() => vi.fn())
|
|
|
|
vi.mock('util', () => ({
|
|
promisify: () => mockExecFileAsync,
|
|
}))
|
|
|
|
vi.mock('../../packages/server/src/services/logger', () => ({
|
|
logger: {
|
|
error: mockLoggerError,
|
|
},
|
|
}))
|
|
|
|
import * as service from '../../packages/server/src/services/hermes/hermes-kanban'
|
|
|
|
describe('hermes kanban service', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
it('builds list/create/stats CLI calls correctly', async () => {
|
|
mockExecFileAsync
|
|
.mockResolvedValueOnce({ stdout: JSON.stringify([{ id: 'task-1' }]) })
|
|
.mockResolvedValueOnce({ stdout: JSON.stringify({ id: 'task-2' }) })
|
|
.mockResolvedValueOnce({ stdout: JSON.stringify({ total: 1, by_status: {}, by_assignee: {} }) })
|
|
|
|
await expect(service.listTasks({ status: 'todo', assignee: 'alice', tenant: 'ops' })).resolves.toEqual([{ id: 'task-1' }])
|
|
await expect(service.createTask('Ship', { body: 'write', assignee: 'alice', priority: 3, tenant: 'ops' })).resolves.toEqual({ id: 'task-2' })
|
|
await expect(service.getStats()).resolves.toEqual({ total: 1, by_status: {}, by_assignee: {} })
|
|
|
|
expect(mockExecFileAsync.mock.calls[0][1]).toEqual(['kanban', 'list', '--json', '--status', 'todo', '--assignee', 'alice', '--tenant', 'ops'])
|
|
expect(mockExecFileAsync.mock.calls[1][1]).toEqual(['kanban', 'create', 'Ship', '--json', '--body', 'write', '--assignee', 'alice', '--priority', '3', '--tenant', 'ops'])
|
|
expect(mockExecFileAsync.mock.calls[2][1]).toEqual(['kanban', 'stats', '--json'])
|
|
})
|
|
|
|
it('builds action CLI calls and maps not-found show to null', async () => {
|
|
mockExecFileAsync
|
|
.mockRejectedValueOnce({ code: 1 })
|
|
.mockResolvedValueOnce({})
|
|
.mockResolvedValueOnce({})
|
|
.mockResolvedValueOnce({})
|
|
.mockResolvedValueOnce({})
|
|
.mockResolvedValueOnce({ stdout: JSON.stringify([{ name: 'alice' }]) })
|
|
|
|
await expect(service.getTask('missing')).resolves.toBeNull()
|
|
await service.completeTasks(['task-1'], 'done')
|
|
await service.blockTask('task-1', 'wait')
|
|
await service.unblockTasks(['task-1'])
|
|
await service.assignTask('task-1', 'alice')
|
|
await expect(service.getAssignees()).resolves.toEqual([{ name: 'alice' }])
|
|
|
|
expect(mockExecFileAsync.mock.calls[1][1]).toEqual(['kanban', 'complete', 'task-1', '--summary', 'done'])
|
|
expect(mockExecFileAsync.mock.calls[2][1]).toEqual(['kanban', 'block', 'task-1', 'wait'])
|
|
expect(mockExecFileAsync.mock.calls[3][1]).toEqual(['kanban', 'unblock', 'task-1'])
|
|
expect(mockExecFileAsync.mock.calls[4][1]).toEqual(['kanban', 'assign', 'task-1', 'alice'])
|
|
expect(mockExecFileAsync.mock.calls[5][1]).toEqual(['kanban', 'assignees', '--json'])
|
|
})
|
|
|
|
it('wraps CLI failures with service-specific errors', async () => {
|
|
mockExecFileAsync.mockRejectedValue(new Error('boom'))
|
|
|
|
await expect(service.listTasks()).rejects.toThrow('Failed to list kanban tasks: boom')
|
|
expect(mockLoggerError).toHaveBeenCalled()
|
|
})
|
|
})
|