Spaces:
Runtime error
Runtime error
| import { test, expect } from '@playwright/test' | |
| import { API_KEY_HEADER } from './helpers' | |
| test.describe('Agent Costs API', () => { | |
| test('GET action=stats includes agents field', async ({ request }) => { | |
| const res = await request.get('/api/tokens?action=stats&timeframe=all', { | |
| headers: API_KEY_HEADER, | |
| }) | |
| expect(res.status()).toBe(200) | |
| const body = await res.json() | |
| expect(body).toHaveProperty('summary') | |
| expect(body).toHaveProperty('models') | |
| expect(body).toHaveProperty('sessions') | |
| expect(body).toHaveProperty('agents') | |
| expect(body).toHaveProperty('timeframe') | |
| expect(body).toHaveProperty('recordCount') | |
| expect(typeof body.agents).toBe('object') | |
| }) | |
| test('GET action=agent-costs returns per-agent breakdown', async ({ request }) => { | |
| const res = await request.get('/api/tokens?action=agent-costs&timeframe=all', { | |
| headers: API_KEY_HEADER, | |
| }) | |
| expect(res.status()).toBe(200) | |
| const body = await res.json() | |
| expect(body).toHaveProperty('agents') | |
| expect(body).toHaveProperty('timeframe') | |
| expect(body).toHaveProperty('recordCount') | |
| expect(typeof body.agents).toBe('object') | |
| // If there are agents, verify structure | |
| for (const [, agentData] of Object.entries(body.agents) as [string, any][]) { | |
| expect(agentData).toHaveProperty('stats') | |
| expect(agentData).toHaveProperty('models') | |
| expect(agentData).toHaveProperty('sessions') | |
| expect(agentData).toHaveProperty('timeline') | |
| expect(agentData.stats).toHaveProperty('totalTokens') | |
| expect(agentData.stats).toHaveProperty('totalCost') | |
| expect(agentData.stats).toHaveProperty('requestCount') | |
| expect(Array.isArray(agentData.sessions)).toBe(true) | |
| expect(Array.isArray(agentData.timeline)).toBe(true) | |
| } | |
| }) | |
| test('GET action=agent-costs respects timeframe filtering', async ({ request }) => { | |
| // Create a token record to ensure data exists | |
| await request.post('/api/tokens', { | |
| headers: API_KEY_HEADER, | |
| data: { | |
| model: 'claude-sonnet-4', | |
| sessionId: 'e2e-cost-agent:chat', | |
| inputTokens: 100, | |
| outputTokens: 50, | |
| }, | |
| }) | |
| const resHour = await request.get('/api/tokens?action=agent-costs&timeframe=hour', { | |
| headers: API_KEY_HEADER, | |
| }) | |
| expect(resHour.status()).toBe(200) | |
| const hourData = await resHour.json() | |
| expect(hourData.timeframe).toBe('hour') | |
| const resMonth = await request.get('/api/tokens?action=agent-costs&timeframe=month', { | |
| headers: API_KEY_HEADER, | |
| }) | |
| expect(resMonth.status()).toBe(200) | |
| const monthData = await resMonth.json() | |
| expect(monthData.timeframe).toBe('month') | |
| // Month should include at least as many records as hour | |
| expect(monthData.recordCount).toBeGreaterThanOrEqual(hourData.recordCount) | |
| }) | |
| test('POST /api/tokens records data that appears in agent-costs', async ({ request }) => { | |
| const agentName = `e2e-costtest-${Date.now()}` | |
| const postRes = await request.post('/api/tokens', { | |
| headers: API_KEY_HEADER, | |
| data: { | |
| model: 'claude-sonnet-4', | |
| sessionId: `${agentName}:chat`, | |
| inputTokens: 500, | |
| outputTokens: 200, | |
| }, | |
| }) | |
| expect(postRes.status()).toBe(200) | |
| const res = await request.get('/api/tokens?action=agent-costs&timeframe=hour', { | |
| headers: API_KEY_HEADER, | |
| }) | |
| const body = await res.json() | |
| expect(body.agents).toHaveProperty(agentName) | |
| expect(body.agents[agentName].stats.totalTokens).toBe(700) | |
| expect(body.agents[agentName].stats.requestCount).toBe(1) | |
| }) | |
| test('GET action=agent-costs requires auth', async ({ request }) => { | |
| const res = await request.get('/api/tokens?action=agent-costs&timeframe=all') | |
| expect(res.status()).toBe(401) | |
| }) | |
| test('GET action=task-costs returns task-level attribution and unattributed rollup', async ({ request }) => { | |
| const agentName = `e2e-taskcost-agent-${Date.now()}` | |
| const createTaskRes = await request.post('/api/tasks', { | |
| headers: API_KEY_HEADER, | |
| data: { | |
| title: `E2E Task Cost ${Date.now()}`, | |
| description: 'Task cost attribution test', | |
| assigned_to: agentName, | |
| }, | |
| }) | |
| expect(createTaskRes.status()).toBe(201) | |
| const createdTask = await createTaskRes.json() | |
| const taskId = createdTask.task.id as number | |
| const postAttributed = await request.post('/api/tokens', { | |
| headers: API_KEY_HEADER, | |
| data: { | |
| model: 'claude-sonnet-4', | |
| sessionId: `${agentName}:chat`, | |
| inputTokens: 300, | |
| outputTokens: 100, | |
| taskId, | |
| }, | |
| }) | |
| expect(postAttributed.status()).toBe(200) | |
| const postUnattributed = await request.post('/api/tokens', { | |
| headers: API_KEY_HEADER, | |
| data: { | |
| model: 'claude-haiku-3.5', | |
| sessionId: `${agentName}:chat`, | |
| inputTokens: 50, | |
| outputTokens: 50, | |
| }, | |
| }) | |
| expect(postUnattributed.status()).toBe(200) | |
| const res = await request.get('/api/tokens?action=task-costs&timeframe=hour', { | |
| headers: API_KEY_HEADER, | |
| }) | |
| const responseText = await res.text() | |
| expect(res.status(), responseText).toBe(200) | |
| const body = JSON.parse(responseText) | |
| expect(body).toHaveProperty('summary') | |
| expect(body).toHaveProperty('tasks') | |
| expect(body).toHaveProperty('agents') | |
| expect(body).toHaveProperty('projects') | |
| expect(body).toHaveProperty('unattributed') | |
| expect(Array.isArray(body.tasks)).toBe(true) | |
| const matchingTask = body.tasks.find((task: any) => task.taskId === taskId) | |
| expect(matchingTask).toBeTruthy() | |
| expect(matchingTask.title).toBe(createdTask.task.title) | |
| expect(matchingTask.stats.totalTokens).toBe(400) | |
| expect(matchingTask.stats.requestCount).toBeGreaterThanOrEqual(1) | |
| expect(body.agents[agentName].taskIds).toContain(taskId) | |
| expect(body.unattributed.requestCount).toBeGreaterThanOrEqual(1) | |
| }) | |
| }) | |