import { describe, it, expect, vi, beforeEach } from 'vitest'; import * as client from '../../../src/api/client'; describe('api client', () => { beforeEach(() => { vi.stubGlobal('fetch', vi.fn()); }); describe('utility functions', () => { it('localHHMMToUTCHHMM converts correctly (assuming UTC+0 for simplicity in test, or handling offset)', () => { const result = client.localHHMMToUTCHHMM('12:00'); expect(result).toMatch(/^\d{2}:\d{2}$/); }); it('localToUTC converts to ISO string', () => { const dateStr = '2026-01-01T12:00'; const result = client.localToUTC(dateStr); expect(result).toBe(new Date(dateStr).toISOString()); }); }); describe('fetch functions', () => { describe('fetchSummary', () => { it('returns data on success', async () => { const mockData = { total: 10 }; vi.mocked(fetch).mockResolvedValue({ ok: true, json: async () => mockData, } as any); const result = await client.fetchSummary('2026-01-01', '2026-01-02'); expect(result).toEqual(mockData); expect(fetch).toHaveBeenCalledWith(expect.stringContaining('/api/stats/summary')); }); it('throws error on non-ok response', async () => { vi.mocked(fetch).mockResolvedValue({ ok: false, } as any); await expect(client.fetchSummary('2026-01-01', '2026-01-02')).rejects.toThrow('Failed to fetch summary'); }); }); describe('fetchDailySummary', () => { it('returns data on success and verifies query params', async () => { const mockData = { some: 'data' } as any; vi.mocked(fetch).mockResolvedValue({ ok: true, json: async () => mockData, } as any); const from_date = '2026-01-01'; const to_date = '2026-01-02'; const day_start = '22:00'; const day_end = '08:00'; const timezone_offset = -360; const result = await client.fetchDailySummary(from_date, to_date, day_start, day_end, timezone_offset); expect(result).toEqual(mockData); const params = new URLSearchParams({ from_date, to_date, day_start, day_end, timezone_offset: timezone_offset.toString() }); const expectedUrl = `/api/stats/daily-summary?${params.toString()}`; expect(fetch).toHaveBeenCalledWith(expect.stringContaining(expectedUrl)); }); it('throws error on non-ok response', async () => { vi.mocked(fetch).mockResolvedValue({ ok: false, } as any); await expect(client.fetchDailySummary('2026-01-01', '2026-01-02', '22:00', '08:00')).rejects.toThrow('Failed to fetch daily-summary'); }); }); describe('fetchTimetable', () => { it('returns data on success and verifies query params', async () => { const mockData = { some: 'timetable' } as any; vi.mocked(fetch).mockResolvedValue({ ok: true, json: async () => mockData, } as any); const from_date = '2026-01-01'; const to_date = '2026-01-02'; const day_start = '22:00'; const day_end = '08:00'; const timezone_offset = 0; const result = await client.fetchTimetable(from_date, to_date, day_start, day_end, timezone_offset); expect(result).toEqual(mockData); const params = new URLSearchParams({ from_date, to_date, day_start, day_end, timezone_offset: timezone_offset.toString() }); const expectedUrl = `/api/stats/timetable?${params.toString()}`; expect(fetch).toHaveBeenCalledWith(expect.stringContaining(expectedUrl)); }); it('throws error on non-ok response', async () => { vi.mocked(fetch).mockResolvedValue({ ok: false, } as any); await expect(client.fetchTimetable('2026-01-01', '2026-01-02', '22:00', '08:00')).rejects.toThrow('Failed to fetch timetable'); }); }); describe('fetchWakeSleepTimes', () => { it('returns data on success and transforms time correctly', async () => { const mockData = { wakeup_ref: '06:30', bedtime_ref: '21:00' }; vi.mocked(fetch).mockResolvedValue({ ok: true, json: async () => mockData, } as any); const offsetSpy = vi.spyOn(Date.prototype, 'getTimezoneOffset').mockReturnValue(-60); try { const result = await client.fetchWakeSleepTimes('2026-01-01', '2026-01-02', '07:30', '22:00', 0); expect(result).toEqual(mockData); expect(fetch).toHaveBeenCalledWith( expect.stringContaining('from_date=2026-01-01&to_date=2026-01-02&wakeup_ref=06%3A30&bedtime_ref=21%3A00&timezone_offset=0') ); } finally { offsetSpy.mockRestore(); } }); it('throws error on failure', async () => { vi.mocked(fetch).mockResolvedValue({ ok: false, } as any); await expect(client.fetchWakeSleepTimes('2026-01-01', '2026-01-02', '07:30', '22:00')).rejects.toThrow('Failed to fetch wake-sleep-times'); }); }); describe('fetchActiveSession', () => { it('returns session on success', async () => { const mockSession = { id: 1, start_time: '2026-01-01T12:00', end_time: null }; vi.mocked(fetch).mockResolvedValue({ ok: true, json: async () => mockSession, } as any); const result = await client.fetchActiveSession(); expect(result).toEqual(mockSession); }); it('returns null on 404/not-ok', async () => { vi.mocked(fetch).mockResolvedValue({ ok: false, } as any); const result = await client.fetchActiveSession(); expect(result).toBeNull(); }); }); describe('fetchSessions', () => { it('returns session list on success', async () => { const mockResponse = { sessions: [{ id: 1, start_time: '2026-01-01T12:00' }] }; vi.mocked(fetch).mockResolvedValue({ ok: true, json: async () => mockResponse, } as any); const result = await client.fetchSessions(); expect(result).toEqual(mockResponse); expect(fetch).toHaveBeenCalledWith(expect.stringContaining('/api/sessions')); }); it('throws error on non-ok response', async () => { vi.mocked(fetch).mockResolvedValue({ ok: false, } as any); await expect(client.fetchSessions()).rejects.toThrow('Failed to fetch sessions'); }); }); describe('Session mutations', () => { it('createSession sends POST request with body', async () => { const payload = { start_time: '2026-01-01T12:00' }; const mockResponse = { id: 1, ...payload }; vi.mocked(fetch).mockResolvedValue({ ok: true, json: async () => mockResponse, } as any); const result = await client.createSession(payload as any); expect(result).toEqual(mockResponse); expect(fetch).toHaveBeenCalledWith( expect.stringContaining('/api/sessions'), expect.objectContaining({ method: 'POST', body: JSON.stringify(payload), }) ); }); it('createSession throws error on non-ok response', async () => { vi.mocked(fetch).mockResolvedValue({ ok: false, } as any); await expect(client.createSession({ start_time: '2026-01-01T12:00' } as any)).rejects.toThrow('Failed to create session'); }); it('endSession sends PATCH request', async () => { const sessionId = 1; const payload = { end_time: '2026-01-01T14:00' }; const mockResponse = { id: 1, ...payload }; vi.mocked(fetch).mockResolvedValue({ ok: true, json: async () => mockResponse, } as any); const result = await client.endSession(sessionId, payload as any); expect(result).toEqual(mockResponse); expect(fetch).toHaveBeenCalledWith( expect.stringContaining(`/api/sessions/${sessionId}/end`), expect.objectContaining({ method: 'PATCH', body: JSON.stringify(payload), }) ); }); it('endSession throws error on non-ok response', async () => { vi.mocked(fetch).mockResolvedValue({ ok: false, } as any); await expect(client.endSession(1, { end_time: '2026-01-01T14:00' } as any)).rejects.toThrow('Failed to end session'); }); it('deleteSession sends DELETE request', async () => { const sessionId = 1; vi.mocked(fetch).mockResolvedValue({ ok: true, } as any); await client.deleteSession(sessionId); expect(fetch).toHaveBeenCalledWith( expect.stringContaining(`/api/sessions/${sessionId}`), expect.objectContaining({ method: 'DELETE' }) ); }); it('deleteSession throws error on non-ok response', async () => { vi.mocked(fetch).mockResolvedValue({ ok: false, } as any); await expect(client.deleteSession(1)).rejects.toThrow('Failed to delete session'); }); it('updateSession sends PATCH request', async () => { const sessionId = 1; const payload = { start_time: '2026-01-01T10:00' }; const mockResponse = { id: 1, ...payload }; vi.mocked(fetch).mockResolvedValue({ ok: true, json: async () => mockResponse, } as any); const result = await client.updateSession(sessionId, payload as any); expect(result).toEqual(mockResponse); expect(fetch).toHaveBeenCalledWith( expect.stringContaining(`/api/sessions/${sessionId}`), expect.objectContaining({ method: 'PATCH', body: JSON.stringify(payload), }) ); }); it('updateSession throws error on non-ok response', async () => { vi.mocked(fetch).mockResolvedValue({ ok: false, } as any); await expect(client.updateSession(1, { start_time: '2026-01-01T10:00' } as any)).rejects.toThrow('Failed to update session'); }); }); }); });