/** * API integration tests for the overview endpoint. * Tests the response structure and data shape of GET /api/overview. * * Uses a minimal mock app since the real overview route requires * database access. Validates that the API contract (response schema) * matches what the frontend expects. */ import { describe, it, expect } from 'bun:test'; import { Hono } from 'hono'; function createOverviewTestApp() { const app = new Hono(); app.get('/api/overview', async (c) => { // Return mock data matching the OverviewData type contract return c.json({ code: 0, data: { sprintDelivery: { cycles: [ { name: 'Sprint 1', plannedPoints: 40, completedPoints: 32, deliveryRate: 80 }, { name: 'Sprint 2', plannedPoints: 45, completedPoints: 38, deliveryRate: 84.4 }, { name: 'Sprint 3', plannedPoints: 50, completedPoints: 42, deliveryRate: 84 }, ], }, taskDistribution: { todo: 12, inProgress: 8, review: 5, done: 35, }, projectProgress: [ { projectId: 'proj-001', name: 'Avatar Platform', identifier: 'AVATAR', currentCycleProgress: 72, totalPoints: 50, completedPoints: 36, }, ], weeklyCodeActivity: { weeks: [ { weekStart: '2026-04-01', members: [ { userId: 'user-001', name: 'Alice', commits: 12, prs: 3 }, ], }, ], }, okrProgress: [ { id: 'okr-001', title: 'Improve delivery rate', ownerName: 'Bob', progress: 65, keyResults: [ { title: 'Sprint delivery > 80%', current: 80, target: 100, unit: '%' }, ], }, ], prMergeTime: { weeks: [ { weekStart: '2026-04-01', avgHours: 24.5, prCount: 8 }, { weekStart: '2026-03-25', avgHours: 36.2, prCount: 5 }, ], }, }, message: 'success', }); }); return app; } describe('GET /api/overview', () => { const app = createOverviewTestApp(); it('should return 200 with complete overview data', async () => { const res = await app.request('/api/overview'); expect(res.status).toBe(200); const body = await res.json(); expect(body.code).toBe(0); expect(body.message).toBe('success'); const data = body.data; expect(data).toBeDefined(); }); it('should contain sprintDelivery with cycles array', async () => { const res = await app.request('/api/overview'); const body = await res.json(); const sd = body.data.sprintDelivery; expect(sd).toBeDefined(); expect(Array.isArray(sd.cycles)).toBe(true); expect(sd.cycles.length).toBeGreaterThan(0); const cycle = sd.cycles[0]; expect(typeof cycle.name).toBe('string'); expect(typeof cycle.plannedPoints).toBe('number'); expect(typeof cycle.completedPoints).toBe('number'); expect(typeof cycle.deliveryRate).toBe('number'); }); it('should contain taskDistribution with all status counts', async () => { const res = await app.request('/api/overview'); const body = await res.json(); const td = body.data.taskDistribution; expect(typeof td.todo).toBe('number'); expect(typeof td.inProgress).toBe('number'); expect(typeof td.review).toBe('number'); expect(typeof td.done).toBe('number'); }); it('should contain projectProgress array', async () => { const res = await app.request('/api/overview'); const body = await res.json(); const pp = body.data.projectProgress; expect(Array.isArray(pp)).toBe(true); expect(pp.length).toBeGreaterThan(0); const proj = pp[0]; expect(typeof proj.projectId).toBe('string'); expect(typeof proj.name).toBe('string'); expect(typeof proj.currentCycleProgress).toBe('number'); }); it('should contain weeklyCodeActivity with weeks', async () => { const res = await app.request('/api/overview'); const body = await res.json(); const wca = body.data.weeklyCodeActivity; expect(Array.isArray(wca.weeks)).toBe(true); expect(wca.weeks.length).toBeGreaterThan(0); const week = wca.weeks[0]; expect(typeof week.weekStart).toBe('string'); expect(Array.isArray(week.members)).toBe(true); }); it('should contain okrProgress array', async () => { const res = await app.request('/api/overview'); const body = await res.json(); const okr = body.data.okrProgress; expect(Array.isArray(okr)).toBe(true); expect(okr.length).toBeGreaterThan(0); const obj = okr[0]; expect(typeof obj.id).toBe('string'); expect(typeof obj.title).toBe('string'); expect(typeof obj.progress).toBe('number'); expect(Array.isArray(obj.keyResults)).toBe(true); }); it('should contain prMergeTime with weeks', async () => { const res = await app.request('/api/overview'); const body = await res.json(); const pmt = body.data.prMergeTime; expect(Array.isArray(pmt.weeks)).toBe(true); expect(pmt.weeks.length).toBeGreaterThan(0); const week = pmt.weeks[0]; expect(typeof week.weekStart).toBe('string'); expect(typeof week.avgHours).toBe('number'); expect(typeof week.prCount).toBe('number'); }); });