import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import MockAdapter from 'axios-mock-adapter'; import { apiClient, getToken, registerUnauthorizedHandler, setToken } from '@/api/client'; describe('apiClient interceptors', () => { let mock: MockAdapter; beforeEach(() => { mock = new MockAdapter(apiClient); setToken(null); }); afterEach(() => { mock.restore(); setToken(null); }); it('attaches Bearer token from storage', async () => { setToken('abc123'); mock.onGet('/auth/me').reply((config) => { expect(config.headers?.Authorization).toBe('Bearer abc123'); return [200, { id: 1, username: 'alice', role: 'admin', created_at: '2026-01-01' }]; }); const resp = await apiClient.get('/auth/me'); expect(resp.status).toBe(200); }); it('purges token and calls the registered handler on 401', async () => { setToken('expired'); const handler = vi.fn(); registerUnauthorizedHandler(handler); mock.onGet('/engagements').reply(401, { error: 'token expired' }); await expect(apiClient.get('/engagements')).rejects.toMatchObject({ response: { status: 401 }, }); expect(getToken()).toBeNull(); expect(handler).toHaveBeenCalledTimes(1); // Reset to avoid leaking into other tests. registerUnauthorizedHandler(() => {}); }); it('leaves the token intact on non-401 errors', async () => { setToken('still-valid'); mock.onGet('/users').reply(403, { error: 'forbidden' }); await expect(apiClient.get('/users')).rejects.toMatchObject({ response: { status: 403 }, }); expect(getToken()).toBe('still-valid'); }); });