136 lines
4.6 KiB
TypeScript
136 lines
4.6 KiB
TypeScript
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
import { NextRequest } from 'next/server'
|
|
|
|
const requireRole = vi.fn()
|
|
const runOpenClaw = vi.fn()
|
|
const removeAgentFromConfig = vi.fn()
|
|
const prepare = vi.fn()
|
|
|
|
vi.mock('@/lib/auth', () => ({
|
|
requireRole,
|
|
}))
|
|
|
|
vi.mock('@/lib/command', () => ({
|
|
runOpenClaw,
|
|
}))
|
|
|
|
vi.mock('@/lib/agent-sync', () => ({
|
|
writeAgentToConfig: vi.fn(),
|
|
enrichAgentConfigFromWorkspace: vi.fn((value) => value),
|
|
removeAgentFromConfig,
|
|
}))
|
|
|
|
vi.mock('@/lib/db', () => ({
|
|
getDatabase: vi.fn(() => ({ prepare })),
|
|
db_helpers: {
|
|
logActivity: vi.fn(),
|
|
},
|
|
logAuditEvent: vi.fn(),
|
|
}))
|
|
|
|
vi.mock('@/lib/event-bus', () => ({
|
|
eventBus: {
|
|
broadcast: vi.fn(),
|
|
},
|
|
}))
|
|
|
|
vi.mock('@/lib/logger', () => ({
|
|
logger: {
|
|
error: vi.fn(),
|
|
warn: vi.fn(),
|
|
info: vi.fn(),
|
|
},
|
|
}))
|
|
|
|
describe('DELETE /api/agents/[id]', () => {
|
|
beforeEach(() => {
|
|
vi.resetModules()
|
|
requireRole.mockReturnValue({ user: { id: 1, username: 'admin', role: 'admin', workspace_id: 1 } })
|
|
runOpenClaw.mockReset()
|
|
removeAgentFromConfig.mockReset()
|
|
prepare.mockReset()
|
|
})
|
|
|
|
afterEach(() => {
|
|
vi.clearAllMocks()
|
|
})
|
|
|
|
it('removes the agent from OpenClaw config even when workspace deletion is disabled', async () => {
|
|
const agent = { id: 7, name: 'neo', role: 'tester', config: JSON.stringify({ openclawId: 'neo' }) }
|
|
const selectStmt = { get: vi.fn(() => agent) }
|
|
const deleteStmt = { run: vi.fn() }
|
|
prepare.mockImplementation((sql: string) => {
|
|
if (sql.startsWith('SELECT * FROM agents')) return selectStmt
|
|
if (sql.startsWith('DELETE FROM agents')) return deleteStmt
|
|
throw new Error(`Unexpected SQL: ${sql}`)
|
|
})
|
|
|
|
const { DELETE } = await import('@/app/api/agents/[id]/route')
|
|
const request = new NextRequest('http://localhost/api/agents/7', {
|
|
method: 'DELETE',
|
|
body: JSON.stringify({ remove_workspace: false }),
|
|
headers: { 'content-type': 'application/json' },
|
|
})
|
|
|
|
const response = await DELETE(request, { params: Promise.resolve({ id: '7' }) })
|
|
const body = await response.json()
|
|
|
|
expect(response.status).toBe(200)
|
|
expect(runOpenClaw).not.toHaveBeenCalled()
|
|
expect(removeAgentFromConfig).toHaveBeenCalledWith({ id: 'neo', name: 'neo' })
|
|
expect(deleteStmt.run).toHaveBeenCalledWith(7, 1)
|
|
expect(body.success).toBe(true)
|
|
})
|
|
|
|
it('removes workspace via OpenClaw and then removes the config entry', async () => {
|
|
const agent = { id: 8, name: 'adam', role: 'tester', config: JSON.stringify({ openclawId: 'adam' }) }
|
|
const selectStmt = { get: vi.fn(() => agent) }
|
|
const deleteStmt = { run: vi.fn() }
|
|
prepare.mockImplementation((sql: string) => {
|
|
if (sql.startsWith('SELECT * FROM agents')) return selectStmt
|
|
if (sql.startsWith('DELETE FROM agents')) return deleteStmt
|
|
throw new Error(`Unexpected SQL: ${sql}`)
|
|
})
|
|
|
|
const { DELETE } = await import('@/app/api/agents/[id]/route')
|
|
const request = new NextRequest('http://localhost/api/agents/8', {
|
|
method: 'DELETE',
|
|
body: JSON.stringify({ remove_workspace: true }),
|
|
headers: { 'content-type': 'application/json' },
|
|
})
|
|
|
|
const response = await DELETE(request, { params: Promise.resolve({ id: '8' }) })
|
|
|
|
expect(response.status).toBe(200)
|
|
expect(runOpenClaw).toHaveBeenCalledWith(['agents', 'delete', 'adam', '--force'], { timeoutMs: 30000 })
|
|
expect(removeAgentFromConfig).toHaveBeenCalledWith({ id: 'adam', name: 'adam' })
|
|
expect(deleteStmt.run).toHaveBeenCalledWith(8, 1)
|
|
})
|
|
|
|
it('still deletes the Mission Control agent when config cleanup fails', async () => {
|
|
const agent = { id: 9, name: 'trinity', role: 'tester', config: JSON.stringify({ openclawId: 'trinity' }) }
|
|
const selectStmt = { get: vi.fn(() => agent) }
|
|
const deleteStmt = { run: vi.fn() }
|
|
prepare.mockImplementation((sql: string) => {
|
|
if (sql.startsWith('SELECT * FROM agents')) return selectStmt
|
|
if (sql.startsWith('DELETE FROM agents')) return deleteStmt
|
|
throw new Error(`Unexpected SQL: ${sql}`)
|
|
})
|
|
removeAgentFromConfig.mockRejectedValue(new Error('OPENCLAW_CONFIG_PATH not configured'))
|
|
|
|
const { DELETE } = await import('@/app/api/agents/[id]/route')
|
|
const request = new NextRequest('http://localhost/api/agents/9', {
|
|
method: 'DELETE',
|
|
headers: { 'content-type': 'application/json' },
|
|
})
|
|
|
|
const response = await DELETE(request, { params: Promise.resolve({ id: '9' }) })
|
|
const body = await response.json()
|
|
|
|
expect(response.status).toBe(200)
|
|
expect(deleteStmt.run).toHaveBeenCalledWith(9, 1)
|
|
expect(body.success).toBe(true)
|
|
expect(body.warning).toContain('OpenClaw config cleanup skipped')
|
|
})
|
|
})
|