const { createRequester, RequestError } = require('../services/request/index') describe('createRequester', () => { test('normalizes successful responses', async () => { const requester = createRequester({ requestAdapter: jest.fn().mockResolvedValue({ statusCode: 200, data: { code: 0, data: { id: 1, name: 'Xuanzhi' }, message: 'ok' } }) }) await expect( requester({ url: '/users/me', method: 'GET' }) ).resolves.toEqual({ code: 0, data: { id: 1, name: 'Xuanzhi' }, message: 'ok' }) }) test('retries retryable transport failures once', async () => { const requestAdapter = jest .fn() .mockRejectedValueOnce(new Error('timeout')) .mockResolvedValueOnce({ statusCode: 200, data: { code: 0, data: { ok: true }, message: 'ok' } }) const requester = createRequester({ requestAdapter, shouldRetry: error => error.message === 'timeout' }) await expect( requester({ url: '/reports/slow-task', retry: 1 }) ).resolves.toEqual({ code: 0, data: { ok: true }, message: 'ok' }) expect(requestAdapter).toHaveBeenCalledTimes(2) }) test('rejects business errors with a normalized error instance', async () => { const requester = createRequester({ requestAdapter: jest.fn().mockResolvedValue({ statusCode: 200, data: { code: 50001, data: null, message: 'invalid payload' } }) }) await expect( requester({ url: '/orders', method: 'POST', data: { name: '' } }) ).rejects.toEqual( expect.objectContaining({ name: 'RequestError', code: 50001, message: 'invalid payload' }) ) expect(RequestError).toBeDefined() }) test('clears auth state and notifies unauthorized handler when token expires', async () => { const onUnauthorized = jest.fn() const sessionStore = { clearSession: jest.fn() } const requester = createRequester({ requestAdapter: jest.fn().mockResolvedValue({ statusCode: 200, data: { code: 401, data: null, message: 'token expired' } }), authExpiredCodes: [401], onUnauthorized, sessionStore }) await expect(requester({ url: '/users/me' })).rejects.toEqual( expect.objectContaining({ code: 401, message: 'token expired' }) ) expect(sessionStore.clearSession).toHaveBeenCalledTimes(1) expect(onUnauthorized).toHaveBeenCalledWith( expect.objectContaining({ code: 401, message: 'token expired' }) ) }) test('deduplicates in-flight requests when dedupe is enabled', async () => { let resolveRequest const requestAdapter = jest.fn( () => new Promise(resolve => { resolveRequest = resolve }) ) const requester = createRequester({ requestAdapter }) const firstPromise = requester({ url: '/orders/submit', method: 'POST', data: { skuId: 1 }, dedupe: true }) const secondPromise = requester({ url: '/orders/submit', method: 'POST', data: { skuId: 1 }, dedupe: true }) resolveRequest({ statusCode: 200, data: { code: 0, data: { orderId: 'A1001' }, message: 'ok' } }) await expect(Promise.all([firstPromise, secondPromise])).resolves.toEqual([ { code: 0, data: { orderId: 'A1001' }, message: 'ok' }, { code: 0, data: { orderId: 'A1001' }, message: 'ok' } ]) expect(requestAdapter).toHaveBeenCalledTimes(1) }) })