import { afterEach, describe, expect, it, vi } from 'vitest' import { ApiClient } from './client' function response(body: unknown, status = 200, headers: Record = {}): Response { return new Response(JSON.stringify(body), { status, headers: { 'Content-Type': 'application/json', ...headers }, }) } afterEach(() => { vi.unstubAllGlobals() }) describe('ApiClient Egregoros compatibility', () => { it('refreshes an expired bearer token once and retries the request', async () => { const refreshToken = vi.fn().mockResolvedValue('fresh-token') const fetchMock = vi .fn() .mockResolvedValueOnce(response({ error: 'Invalid or expired token' }, 401)) .mockResolvedValueOnce(response({ id: 'alice' })) vi.stubGlobal('fetch', fetchMock) const client = new ApiClient('egregoros.example', 'expired-token', refreshToken) await expect(client.get('/api/v1/accounts/verify_credentials')).resolves.toEqual({ id: 'alice', }) expect(refreshToken).toHaveBeenCalledOnce() expect((fetchMock.mock.calls[0][1].headers as Headers).get('Authorization')).toBe( 'Bearer expired-token', ) expect((fetchMock.mock.calls[1][1].headers as Headers).get('Authorization')).toBe( 'Bearer fresh-token', ) }) it('does not invent a next page when the server supplied only a prev link', async () => { vi.stubGlobal( 'fetch', vi.fn().mockResolvedValue( response( [{ id: 'newest' }, { id: 'oldest' }], 200, { Link: '; rel="prev"', }, ), ), ) const page = await new ApiClient('egregoros.example').page<{ id: string }>( '/api/v1/timelines/public', { limit: 20 }, ) expect(page.links.sinceId).toBe('newest') expect(page.links.maxId).toBeUndefined() }) it('derives a next cursor only for a full page when Link was stripped entirely', async () => { vi.stubGlobal( 'fetch', vi.fn().mockResolvedValue(response([{ id: 'newest' }, { id: 'oldest' }])), ) const page = await new ApiClient('egregoros.example').page<{ id: string }>( '/api/v1/timelines/public', { limit: 2 }, ) expect(page.links.maxId).toBe('oldest') }) })