Spaces:
Running
Running
| import { afterEach, describe, expect, it, vi } from 'vitest'; | |
| import { fetchUsageEventFilterOptions, fetchUsageEvents, fetchUsageIdentities, triggerSync } from './api'; | |
| describe('fetchUsageEvents', () => { | |
| afterEach(() => { | |
| vi.restoreAllMocks(); | |
| vi.unstubAllGlobals(); | |
| }); | |
| it('loads stable filter options without query params', async () => { | |
| vi.stubGlobal('window', { __APP_BASE_PATH__: undefined }); | |
| const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValue({ | |
| ok: true, | |
| json: async () => ({ models: ['claude-sonnet'], sources: [{ value: 'source-a', label: 'Provider A' }] }), | |
| } as Response); | |
| const signal = new AbortController().signal; | |
| const response = await fetchUsageEventFilterOptions(signal); | |
| const [url, init] = fetchMock.mock.calls[0]; | |
| const parsed = new URL(String(url), 'http://localhost'); | |
| expect(response.models).toEqual(['claude-sonnet']); | |
| expect(parsed.pathname).toBe('/api/v1/usage/events/filters'); | |
| expect(parsed.search).toBe(''); | |
| expect(parsed.searchParams.get('range')).toBeNull(); | |
| expect(parsed.searchParams.get('start')).toBeNull(); | |
| expect(parsed.searchParams.get('end')).toBeNull(); | |
| expect(parsed.searchParams.get('page')).toBeNull(); | |
| expect(parsed.searchParams.get('page_size')).toBeNull(); | |
| expect(parsed.searchParams.get('model')).toBeNull(); | |
| expect(parsed.searchParams.get('source')).toBeNull(); | |
| expect(parsed.searchParams.get('result')).toBeNull(); | |
| expect(init).toMatchObject({ credentials: 'include', signal }); | |
| }); | |
| it('passes pagination and server-side filters as query params', async () => { | |
| vi.stubGlobal('window', { __APP_BASE_PATH__: undefined }); | |
| const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValue({ | |
| ok: true, | |
| json: async () => ({ events: [], models: [], sources: [], total_count: 0, page: 3, page_size: 100, total_pages: 0 }), | |
| } as Response); | |
| const signal = new AbortController().signal; | |
| await fetchUsageEvents('custom', '2026-04-20T00:00:00Z', '2026-04-21T00:00:00Z', signal, { | |
| page: 3, | |
| pageSize: 100, | |
| model: 'claude-sonnet', | |
| source: 'source-a', | |
| result: 'failed', | |
| }); | |
| const [url, init] = fetchMock.mock.calls[0]; | |
| const parsed = new URL(String(url), 'http://localhost'); | |
| expect(parsed.pathname).toBe('/api/v1/usage/events'); | |
| expect(parsed.searchParams.get('range')).toBe('custom'); | |
| expect(parsed.searchParams.get('start')).toBe('2026-04-20T00:00:00Z'); | |
| expect(parsed.searchParams.get('end')).toBe('2026-04-21T00:00:00Z'); | |
| expect(parsed.searchParams.get('page')).toBe('3'); | |
| expect(parsed.searchParams.get('page_size')).toBe('100'); | |
| expect(parsed.searchParams.get('model')).toBe('claude-sonnet'); | |
| expect(parsed.searchParams.get('source')).toBe('source-a'); | |
| expect(parsed.searchParams.get('result')).toBe('failed'); | |
| expect(parsed.searchParams.get('auth_index')).toBeNull(); | |
| expect(init).toMatchObject({ credentials: 'include', signal }); | |
| }); | |
| it('loads unified usage identities for credential stats', async () => { | |
| vi.stubGlobal('window', { __APP_BASE_PATH__: undefined }); | |
| const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValue({ | |
| ok: true, | |
| json: async () => ({ | |
| identities: [ | |
| { | |
| id: 1, | |
| name: 'Claude primary', | |
| auth_type: 2, | |
| auth_type_name: 'apikey', | |
| identity: 'sk-a***1234', | |
| type: 'claude', | |
| provider: 'anthropic', | |
| total_requests: 3, | |
| success_count: 2, | |
| failure_count: 1, | |
| input_tokens: 10, | |
| output_tokens: 20, | |
| reasoning_tokens: 0, | |
| cached_tokens: 0, | |
| total_tokens: 30, | |
| last_aggregated_usage_event_id: 9, | |
| is_deleted: false, | |
| created_at: '2026-05-04T00:00:00Z', | |
| updated_at: '2026-05-04T00:00:00Z', | |
| }, | |
| ], | |
| }), | |
| } as Response); | |
| const signal = new AbortController().signal; | |
| const response = await fetchUsageIdentities(signal); | |
| const [url, init] = fetchMock.mock.calls[0]; | |
| const parsed = new URL(String(url), 'http://localhost'); | |
| expect(response.identities[0].identity).toBe('sk-a***1234'); | |
| expect(response.identities[0].auth_type).toBe(2); | |
| expect(typeof response.identities[0].auth_type).toBe('number'); | |
| expect(parsed.pathname).toBe('/api/v1/usage/identities'); | |
| expect(parsed.search).toBe(''); | |
| expect(init).toMatchObject({ credentials: 'include', signal }); | |
| }); | |
| it('posts to the manual sync endpoint', async () => { | |
| vi.stubGlobal('window', { __APP_BASE_PATH__: undefined }); | |
| const fetchMock = vi.spyOn(globalThis, 'fetch').mockResolvedValue({ | |
| ok: true, | |
| json: async () => ({ running: true, sync_running: false, last_status: 'completed' }), | |
| } as Response); | |
| const signal = new AbortController().signal; | |
| const response = await triggerSync(signal); | |
| const [url, init] = fetchMock.mock.calls[0]; | |
| const parsed = new URL(String(url), 'http://localhost'); | |
| expect(response.last_status).toBe('completed'); | |
| expect(parsed.pathname).toBe('/api/v1/sync'); | |
| expect(init).toMatchObject({ credentials: 'include', method: 'POST', signal }); | |
| }); | |
| }); | |