Files
xuanzhi-wx/tests/request.test.js
2026-04-22 18:54:52 +08:00

176 lines
3.8 KiB
JavaScript

const { createRequester, RequestError } = require('../services/request')
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)
})
})