159 lines
5.2 KiB
TypeScript
159 lines
5.2 KiB
TypeScript
import { test, expect } from '@playwright/test'
|
|
import { API_KEY_HEADER } from './helpers'
|
|
|
|
test.describe('Direct CLI Integration', () => {
|
|
const createdConnectionIds: string[] = []
|
|
const createdAgentIds: number[] = []
|
|
|
|
test.afterEach(async ({ request }) => {
|
|
// Clean up connections
|
|
for (const connId of createdConnectionIds) {
|
|
await request.delete('/api/connect', {
|
|
headers: API_KEY_HEADER,
|
|
data: { connection_id: connId },
|
|
})
|
|
}
|
|
createdConnectionIds.length = 0
|
|
|
|
// Clean up auto-created agents
|
|
for (const agentId of createdAgentIds) {
|
|
await request.delete(`/api/agents/${agentId}`, { headers: API_KEY_HEADER })
|
|
}
|
|
createdAgentIds.length = 0
|
|
})
|
|
|
|
test('POST /api/connect creates connection and auto-creates agent', async ({ request }) => {
|
|
const agentName = `e2e-cli-${Date.now()}`
|
|
const res = await request.post('/api/connect', {
|
|
headers: API_KEY_HEADER,
|
|
data: {
|
|
tool_name: 'claude-code',
|
|
tool_version: '1.0.0',
|
|
agent_name: agentName,
|
|
agent_role: 'developer',
|
|
},
|
|
})
|
|
expect(res.status()).toBe(200)
|
|
const body = await res.json()
|
|
|
|
expect(body.connection_id).toBeDefined()
|
|
expect(body.agent_id).toBeDefined()
|
|
expect(body.agent_name).toBe(agentName)
|
|
expect(body.status).toBe('connected')
|
|
expect(body.sse_url).toBe('/api/events')
|
|
expect(body.heartbeat_url).toContain('/api/agents/')
|
|
expect(body.token_report_url).toBe('/api/tokens')
|
|
|
|
createdConnectionIds.push(body.connection_id)
|
|
createdAgentIds.push(body.agent_id)
|
|
|
|
// Verify agent was created
|
|
const agentRes = await request.get(`/api/agents/${body.agent_id}`, {
|
|
headers: API_KEY_HEADER,
|
|
})
|
|
expect(agentRes.status()).toBe(200)
|
|
const agentBody = await agentRes.json()
|
|
expect(agentBody.agent.name).toBe(agentName)
|
|
expect(agentBody.agent.status).toBe('online')
|
|
})
|
|
|
|
test('GET /api/connect lists connections', async ({ request }) => {
|
|
const agentName = `e2e-cli-list-${Date.now()}`
|
|
const postRes = await request.post('/api/connect', {
|
|
headers: API_KEY_HEADER,
|
|
data: {
|
|
tool_name: 'codex',
|
|
agent_name: agentName,
|
|
},
|
|
})
|
|
const postBody = await postRes.json()
|
|
createdConnectionIds.push(postBody.connection_id)
|
|
createdAgentIds.push(postBody.agent_id)
|
|
|
|
const res = await request.get('/api/connect', { headers: API_KEY_HEADER })
|
|
expect(res.status()).toBe(200)
|
|
const body = await res.json()
|
|
expect(Array.isArray(body.connections)).toBe(true)
|
|
const found = body.connections.find((c: any) => c.connection_id === postBody.connection_id)
|
|
expect(found).toBeDefined()
|
|
expect(found.agent_name).toBe(agentName)
|
|
expect(found.tool_name).toBe('codex')
|
|
})
|
|
|
|
test('POST heartbeat with inline token_usage', async ({ request }) => {
|
|
const agentName = `e2e-cli-hb-${Date.now()}`
|
|
const postRes = await request.post('/api/connect', {
|
|
headers: API_KEY_HEADER,
|
|
data: {
|
|
tool_name: 'claude-code',
|
|
agent_name: agentName,
|
|
},
|
|
})
|
|
const postBody = await postRes.json()
|
|
createdConnectionIds.push(postBody.connection_id)
|
|
createdAgentIds.push(postBody.agent_id)
|
|
|
|
const hbRes = await request.post(`/api/agents/${postBody.agent_id}/heartbeat`, {
|
|
headers: API_KEY_HEADER,
|
|
data: {
|
|
connection_id: postBody.connection_id,
|
|
token_usage: {
|
|
model: 'claude-sonnet-4',
|
|
inputTokens: 1000,
|
|
outputTokens: 500,
|
|
},
|
|
},
|
|
})
|
|
expect(hbRes.status()).toBe(200)
|
|
const hbBody = await hbRes.json()
|
|
expect(hbBody.token_recorded).toBe(true)
|
|
expect(hbBody.agent).toBe(agentName)
|
|
|
|
const costsRes = await request.get('/api/tokens?action=agent-costs&timeframe=hour', {
|
|
headers: API_KEY_HEADER,
|
|
})
|
|
expect(costsRes.status()).toBe(200)
|
|
const costsBody = await costsRes.json()
|
|
expect(costsBody.agents).toHaveProperty(agentName)
|
|
expect(costsBody.agents[agentName].stats.totalTokens).toBeGreaterThanOrEqual(1500)
|
|
})
|
|
|
|
test('DELETE /api/connect disconnects and sets agent offline', async ({ request }) => {
|
|
const agentName = `e2e-cli-del-${Date.now()}`
|
|
const postRes = await request.post('/api/connect', {
|
|
headers: API_KEY_HEADER,
|
|
data: {
|
|
tool_name: 'claude-code',
|
|
agent_name: agentName,
|
|
},
|
|
})
|
|
const postBody = await postRes.json()
|
|
createdAgentIds.push(postBody.agent_id)
|
|
|
|
const delRes = await request.delete('/api/connect', {
|
|
headers: API_KEY_HEADER,
|
|
data: { connection_id: postBody.connection_id },
|
|
})
|
|
expect(delRes.status()).toBe(200)
|
|
const delBody = await delRes.json()
|
|
expect(delBody.status).toBe('disconnected')
|
|
|
|
// Agent should be offline
|
|
const agentRes = await request.get(`/api/agents/${postBody.agent_id}`, {
|
|
headers: API_KEY_HEADER,
|
|
})
|
|
const agentBody = await agentRes.json()
|
|
expect(agentBody.agent.status).toBe('offline')
|
|
})
|
|
|
|
test('POST /api/connect requires auth', async ({ request }) => {
|
|
const res = await request.post('/api/connect', {
|
|
data: {
|
|
tool_name: 'claude-code',
|
|
agent_name: 'unauthorized-agent',
|
|
},
|
|
})
|
|
expect(res.status()).toBe(401)
|
|
})
|
|
})
|