274 lines
10 KiB
TypeScript
274 lines
10 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
import { API_KEY_HEADER, createTestProject, deleteTestProject, createTestAgent, deleteTestAgent } from './helpers'
|
|
|
|
test.describe('Project Agent Assignments', () => {
|
|
const projectCleanup: number[] = []
|
|
const agentCleanup: number[] = []
|
|
|
|
test.afterEach(async ({ request }) => {
|
|
for (const id of projectCleanup) {
|
|
await deleteTestProject(request, id).catch(() => {})
|
|
}
|
|
for (const id of agentCleanup) {
|
|
await deleteTestAgent(request, id).catch(() => {})
|
|
}
|
|
projectCleanup.length = 0
|
|
agentCleanup.length = 0
|
|
})
|
|
|
|
// ── POST /api/projects/[id]/agents ──────────────
|
|
|
|
test('POST assigns agent to project', async ({ request }) => {
|
|
const { id: projectId } = await createTestProject(request)
|
|
projectCleanup.push(projectId)
|
|
const { id: agentId, name: agentName } = await createTestAgent(request)
|
|
agentCleanup.push(agentId)
|
|
|
|
const res = await request.post(`/api/projects/${projectId}/agents`, {
|
|
headers: API_KEY_HEADER,
|
|
data: { agent_name: agentName },
|
|
})
|
|
expect(res.status()).toBe(201)
|
|
const body = await res.json()
|
|
expect(body.success).toBe(true)
|
|
})
|
|
|
|
test('POST assigns agent with custom role', async ({ request }) => {
|
|
const { id: projectId } = await createTestProject(request)
|
|
projectCleanup.push(projectId)
|
|
const { id: agentId, name: agentName } = await createTestAgent(request)
|
|
agentCleanup.push(agentId)
|
|
|
|
const res = await request.post(`/api/projects/${projectId}/agents`, {
|
|
headers: API_KEY_HEADER,
|
|
data: { agent_name: agentName, role: 'lead' },
|
|
})
|
|
expect(res.status()).toBe(201)
|
|
})
|
|
|
|
test('POST is idempotent for same agent', async ({ request }) => {
|
|
const { id: projectId } = await createTestProject(request)
|
|
projectCleanup.push(projectId)
|
|
const { id: agentId, name: agentName } = await createTestAgent(request)
|
|
agentCleanup.push(agentId)
|
|
|
|
// First assign
|
|
const res1 = await request.post(`/api/projects/${projectId}/agents`, {
|
|
headers: API_KEY_HEADER,
|
|
data: { agent_name: agentName },
|
|
})
|
|
expect(res1.status()).toBe(201)
|
|
|
|
// Second assign (should not fail due to INSERT OR IGNORE)
|
|
const res2 = await request.post(`/api/projects/${projectId}/agents`, {
|
|
headers: API_KEY_HEADER,
|
|
data: { agent_name: agentName },
|
|
})
|
|
expect(res2.status()).toBe(201)
|
|
})
|
|
|
|
test('POST rejects empty agent_name', async ({ request }) => {
|
|
const { id: projectId } = await createTestProject(request)
|
|
projectCleanup.push(projectId)
|
|
|
|
const res = await request.post(`/api/projects/${projectId}/agents`, {
|
|
headers: API_KEY_HEADER,
|
|
data: { agent_name: '' },
|
|
})
|
|
expect(res.status()).toBe(400)
|
|
})
|
|
|
|
test('POST returns 404 for missing project', async ({ request }) => {
|
|
const res = await request.post('/api/projects/999999/agents', {
|
|
headers: API_KEY_HEADER,
|
|
data: { agent_name: 'ghost-agent' },
|
|
})
|
|
expect(res.status()).toBe(404)
|
|
})
|
|
|
|
// ── GET /api/projects/[id]/agents ───────────────
|
|
|
|
test('GET lists assigned agents', async ({ request }) => {
|
|
const { id: projectId } = await createTestProject(request)
|
|
projectCleanup.push(projectId)
|
|
const { id: agentId, name: agentName } = await createTestAgent(request)
|
|
agentCleanup.push(agentId)
|
|
|
|
// Assign agent
|
|
await request.post(`/api/projects/${projectId}/agents`, {
|
|
headers: API_KEY_HEADER,
|
|
data: { agent_name: agentName, role: 'reviewer' },
|
|
})
|
|
|
|
// List
|
|
const res = await request.get(`/api/projects/${projectId}/agents`, { headers: API_KEY_HEADER })
|
|
expect(res.status()).toBe(200)
|
|
const body = await res.json()
|
|
expect(body).toHaveProperty('assignments')
|
|
expect(Array.isArray(body.assignments)).toBe(true)
|
|
|
|
const found = body.assignments.find((a: any) => a.agent_name === agentName)
|
|
expect(found).toBeDefined()
|
|
expect(found.role).toBe('reviewer')
|
|
expect(found.project_id).toBe(projectId)
|
|
})
|
|
|
|
test('GET returns empty array for project with no agents', async ({ request }) => {
|
|
const { id: projectId } = await createTestProject(request)
|
|
projectCleanup.push(projectId)
|
|
|
|
const res = await request.get(`/api/projects/${projectId}/agents`, { headers: API_KEY_HEADER })
|
|
expect(res.status()).toBe(200)
|
|
const body = await res.json()
|
|
expect(body.assignments).toEqual([])
|
|
})
|
|
|
|
test('GET returns 404 for missing project', async ({ request }) => {
|
|
const res = await request.get('/api/projects/999999/agents', { headers: API_KEY_HEADER })
|
|
expect(res.status()).toBe(404)
|
|
})
|
|
|
|
// ── DELETE /api/projects/[id]/agents ────────────
|
|
|
|
test('DELETE unassigns agent from project', async ({ request }) => {
|
|
const { id: projectId } = await createTestProject(request)
|
|
projectCleanup.push(projectId)
|
|
const { id: agentId, name: agentName } = await createTestAgent(request)
|
|
agentCleanup.push(agentId)
|
|
|
|
// Assign
|
|
await request.post(`/api/projects/${projectId}/agents`, {
|
|
headers: API_KEY_HEADER,
|
|
data: { agent_name: agentName },
|
|
})
|
|
|
|
// Unassign
|
|
const res = await request.delete(
|
|
`/api/projects/${projectId}/agents?agent_name=${encodeURIComponent(agentName)}`,
|
|
{ headers: API_KEY_HEADER }
|
|
)
|
|
expect(res.status()).toBe(200)
|
|
const body = await res.json()
|
|
expect(body.success).toBe(true)
|
|
|
|
// Verify gone
|
|
const listRes = await request.get(`/api/projects/${projectId}/agents`, { headers: API_KEY_HEADER })
|
|
const listBody = await listRes.json()
|
|
const found = listBody.assignments.find((a: any) => a.agent_name === agentName)
|
|
expect(found).toBeUndefined()
|
|
})
|
|
|
|
test('DELETE without agent_name returns 400', async ({ request }) => {
|
|
const { id: projectId } = await createTestProject(request)
|
|
projectCleanup.push(projectId)
|
|
|
|
const res = await request.delete(`/api/projects/${projectId}/agents`, { headers: API_KEY_HEADER })
|
|
expect(res.status()).toBe(400)
|
|
})
|
|
|
|
test('DELETE returns 404 for missing project', async ({ request }) => {
|
|
const res = await request.delete('/api/projects/999999/agents?agent_name=x', { headers: API_KEY_HEADER })
|
|
expect(res.status()).toBe(404)
|
|
})
|
|
|
|
// ── assigned_agents in project response ─────────
|
|
|
|
test('assigned_agents appears in project list response after assignment', async ({ request }) => {
|
|
const { id: projectId } = await createTestProject(request)
|
|
projectCleanup.push(projectId)
|
|
const { id: agentId, name: agentName } = await createTestAgent(request)
|
|
agentCleanup.push(agentId)
|
|
|
|
// Assign
|
|
await request.post(`/api/projects/${projectId}/agents`, {
|
|
headers: API_KEY_HEADER,
|
|
data: { agent_name: agentName },
|
|
})
|
|
|
|
// Check project list
|
|
const listRes = await request.get('/api/projects', { headers: API_KEY_HEADER })
|
|
const listBody = await listRes.json()
|
|
const project = listBody.projects.find((p: any) => p.id === projectId)
|
|
expect(project).toBeDefined()
|
|
expect(project.assigned_agents).toContain(agentName)
|
|
})
|
|
|
|
test('assigned_agents appears in single project response', async ({ request }) => {
|
|
const { id: projectId } = await createTestProject(request)
|
|
projectCleanup.push(projectId)
|
|
const { id: agentId, name: agentName } = await createTestAgent(request)
|
|
agentCleanup.push(agentId)
|
|
|
|
// Assign
|
|
await request.post(`/api/projects/${projectId}/agents`, {
|
|
headers: API_KEY_HEADER,
|
|
data: { agent_name: agentName },
|
|
})
|
|
|
|
// Check single project
|
|
const getRes = await request.get(`/api/projects/${projectId}`, { headers: API_KEY_HEADER })
|
|
const getBody = await getRes.json()
|
|
expect(getBody.project.assigned_agents).toContain(agentName)
|
|
})
|
|
|
|
// ── Full lifecycle ───────────────────────────
|
|
|
|
test('full lifecycle: assign multiple → list → unassign one → verify', async ({ request }) => {
|
|
const { id: projectId } = await createTestProject(request)
|
|
projectCleanup.push(projectId)
|
|
const { id: agent1Id, name: agent1Name } = await createTestAgent(request)
|
|
agentCleanup.push(agent1Id)
|
|
const { id: agent2Id, name: agent2Name } = await createTestAgent(request)
|
|
agentCleanup.push(agent2Id)
|
|
|
|
// Assign both
|
|
await request.post(`/api/projects/${projectId}/agents`, {
|
|
headers: API_KEY_HEADER,
|
|
data: { agent_name: agent1Name, role: 'developer' },
|
|
})
|
|
await request.post(`/api/projects/${projectId}/agents`, {
|
|
headers: API_KEY_HEADER,
|
|
data: { agent_name: agent2Name, role: 'reviewer' },
|
|
})
|
|
|
|
// List — both present
|
|
const listRes1 = await request.get(`/api/projects/${projectId}/agents`, { headers: API_KEY_HEADER })
|
|
const list1 = await listRes1.json()
|
|
expect(list1.assignments.length).toBe(2)
|
|
|
|
// Unassign first
|
|
await request.delete(
|
|
`/api/projects/${projectId}/agents?agent_name=${encodeURIComponent(agent1Name)}`,
|
|
{ headers: API_KEY_HEADER }
|
|
)
|
|
|
|
// List — only second remains
|
|
const listRes2 = await request.get(`/api/projects/${projectId}/agents`, { headers: API_KEY_HEADER })
|
|
const list2 = await listRes2.json()
|
|
expect(list2.assignments.length).toBe(1)
|
|
expect(list2.assignments[0].agent_name).toBe(agent2Name)
|
|
expect(list2.assignments[0].role).toBe('reviewer')
|
|
})
|
|
|
|
// ── Cascade deletion ───────────────────────────
|
|
|
|
test('deleting a project cascades to remove agent assignments', async ({ request }) => {
|
|
const { id: projectId } = await createTestProject(request)
|
|
const { id: agentId, name: agentName } = await createTestAgent(request)
|
|
agentCleanup.push(agentId)
|
|
|
|
// Assign
|
|
await request.post(`/api/projects/${projectId}/agents`, {
|
|
headers: API_KEY_HEADER,
|
|
data: { agent_name: agentName },
|
|
})
|
|
|
|
// Delete project permanently
|
|
await request.delete(`/api/projects/${projectId}?mode=delete`, { headers: API_KEY_HEADER })
|
|
|
|
// Agent assignments should be gone (project is gone, can't query assignments)
|
|
const res = await request.get(`/api/projects/${projectId}/agents`, { headers: API_KEY_HEADER })
|
|
expect(res.status()).toBe(404)
|
|
})
|
|
})
|