mission-control/tests/agents-crud.spec.ts

198 lines
6.5 KiB
TypeScript

import { test, expect } from '@playwright/test'
import { API_KEY_HEADER, createTestAgent, deleteTestAgent } from './helpers'
test.describe('Agents CRUD', () => {
const cleanup: number[] = []
test.afterEach(async ({ request }) => {
for (const id of cleanup) {
await deleteTestAgent(request, id).catch(() => {})
}
cleanup.length = 0
})
// ── POST /api/agents ─────────────────────────
test('POST creates agent with name and role', async ({ request }) => {
const { id, res, body } = await createTestAgent(request)
cleanup.push(id)
expect(res.status()).toBe(201)
expect(body.agent).toBeDefined()
expect(body.agent.name).toContain('e2e-agent-')
expect(body.agent.role).toBe('tester')
expect(body.agent.status).toBe('offline')
})
test('POST rejects missing name', async ({ request }) => {
const res = await request.post('/api/agents', {
headers: API_KEY_HEADER,
data: { role: 'tester' },
})
expect(res.status()).toBe(400)
})
test('POST rejects duplicate name', async ({ request }) => {
const { id, body: first } = await createTestAgent(request)
cleanup.push(id)
const res = await request.post('/api/agents', {
headers: API_KEY_HEADER,
data: { name: first.agent.name, role: 'duplicate' },
})
expect(res.status()).toBe(409)
})
// ── GET /api/agents ──────────────────────────
test('GET list returns agents with pagination and taskStats', async ({ request }) => {
const { id } = await createTestAgent(request)
cleanup.push(id)
const res = await request.get('/api/agents', { headers: API_KEY_HEADER })
expect(res.status()).toBe(200)
const body = await res.json()
expect(body).toHaveProperty('agents')
expect(body).toHaveProperty('total')
expect(body).toHaveProperty('page')
expect(body).toHaveProperty('limit')
expect(Array.isArray(body.agents)).toBe(true)
// Every agent should have taskStats
for (const a of body.agents) {
expect(a.taskStats).toBeDefined()
expect(a.taskStats).toHaveProperty('total')
}
})
// ── GET /api/agents/[id] ─────────────────────
test('GET single by numeric id', async ({ request }) => {
const { id } = await createTestAgent(request)
cleanup.push(id)
const res = await request.get(`/api/agents/${id}`, { headers: API_KEY_HEADER })
expect(res.status()).toBe(200)
const body = await res.json()
expect(body.agent.id).toBe(id)
})
test('GET single by name', async ({ request }) => {
const { id, name } = await createTestAgent(request)
cleanup.push(id)
const res = await request.get(`/api/agents/${name}`, { headers: API_KEY_HEADER })
expect(res.status()).toBe(200)
const body = await res.json()
expect(body.agent.name).toBe(name)
})
test('GET single returns 404 for missing', async ({ request }) => {
const res = await request.get('/api/agents/999999', { headers: API_KEY_HEADER })
expect(res.status()).toBe(404)
})
// ── PUT /api/agents/[id] ─────────────────────
test('PUT by id updates role', async ({ request }) => {
const { id } = await createTestAgent(request)
cleanup.push(id)
const res = await request.put(`/api/agents/${id}`, {
headers: API_KEY_HEADER,
data: { role: 'reviewer' },
})
expect(res.status()).toBe(200)
const body = await res.json()
expect(body.agent.role).toBe('reviewer')
})
test('PUT by id returns 404 for missing', async ({ request }) => {
const res = await request.put('/api/agents/999999', {
headers: API_KEY_HEADER,
data: { role: 'reviewer' },
})
expect(res.status()).toBe(404)
})
// ── PUT /api/agents (bulk by name) ───────────
test('PUT by name updates status', async ({ request }) => {
const { id, name } = await createTestAgent(request)
cleanup.push(id)
const res = await request.put('/api/agents', {
headers: API_KEY_HEADER,
data: { name, status: 'online' },
})
expect(res.status()).toBe(200)
const body = await res.json()
expect(body.success).toBe(true)
})
test('PUT by name returns 404 for missing', async ({ request }) => {
const res = await request.put('/api/agents', {
headers: API_KEY_HEADER,
data: { name: 'nonexistent-agent-xyz', status: 'online' },
})
expect(res.status()).toBe(404)
})
test('PUT by name returns 400 when no fields provided', async ({ request }) => {
const { id, name } = await createTestAgent(request)
cleanup.push(id)
const res = await request.put('/api/agents', {
headers: API_KEY_HEADER,
data: { name },
})
expect(res.status()).toBe(400)
})
// ── DELETE /api/agents/[id] ──────────────────
test('DELETE removes agent (admin via API key)', async ({ request }) => {
const { id, name } = await createTestAgent(request)
const res = await request.delete(`/api/agents/${id}`, { headers: API_KEY_HEADER })
expect(res.status()).toBe(200)
const body = await res.json()
expect(body.success).toBe(true)
expect(body.deleted).toBe(name)
})
test('DELETE returns 404 for missing agent', async ({ request }) => {
const res = await request.delete('/api/agents/999999', { headers: API_KEY_HEADER })
expect(res.status()).toBe(404)
})
// ── Full lifecycle ───────────────────────────
test('full lifecycle: create → read → update → delete', async ({ request }) => {
// Create
const { id, name, res: createRes } = await createTestAgent(request, { role: 'builder' })
expect(createRes.status()).toBe(201)
// Read
const readRes = await request.get(`/api/agents/${id}`, { headers: API_KEY_HEADER })
expect(readRes.status()).toBe(200)
const readBody = await readRes.json()
expect(readBody.agent.role).toBe('builder')
// Update via [id]
const updateRes = await request.put(`/api/agents/${id}`, {
headers: API_KEY_HEADER,
data: { role: 'architect' },
})
expect(updateRes.status()).toBe(200)
// Delete
const deleteRes = await request.delete(`/api/agents/${id}`, { headers: API_KEY_HEADER })
expect(deleteRes.status()).toBe(200)
// Confirm gone
const goneRes = await request.get(`/api/agents/${name}`, { headers: API_KEY_HEADER })
expect(goneRes.status()).toBe(404)
})
})