import { describe, it, expect, beforeEach, vi } from 'vitest' // Taro.request 由各用例设置返回值(vi.hoisted 保证 mock 提升后仍可引用) const mockRequest = vi.hoisted(() => vi.fn()) vi.mock('@tarojs/taro', () => ({ default: { request: mockRequest } })) // client 依赖 store 取 token;此处只读,不触发 401 流程 vi.mock('@/store', () => ({ store: { getState: () => ({ auth: { token: 'unit-token' } }), dispatch: vi.fn() } })) import { request } from '@/http/client' import { ApiError, BusinessError } from '@/http/errors' const okBody = (content: T) => ({ statusCode: 200, data: { Code: '200', Message: '', Content: content, RecordCount: 0, PageIndex: 0, PageSize: 0 } }) beforeEach(() => { mockRequest.mockReset() }) describe('client envelope 解析', () => { it('Code=200 时解包返回 Content', async () => { mockRequest.mockResolvedValue(okBody({ name: '张三' })) const res = await request.get<{ name: string }>('/demo') expect(res).toEqual({ name: '张三' }) // GET 参数经 data 透传(Taro 运行时序列化为 query string) expect(mockRequest.mock.calls[0][0].data).toBeUndefined() }) it('GET 参数放入 data 字段', async () => { mockRequest.mockResolvedValue(okBody(null)) await request.get('/demo', { page: 1 }) expect(mockRequest.mock.calls[0][0].data).toEqual({ page: 1 }) }) it("Code='500' 抛 BusinessError(message + 字符串 code)", async () => { mockRequest.mockResolvedValue({ statusCode: 200, data: { Code: '500', Message: '手机号已绑定', Content: null } }) await expect(request.get('/demo')).rejects.toMatchObject({ name: 'BusinessError', message: '手机号已绑定', code: '500' }) await expect(request.get('/demo')).rejects.toBeInstanceOf(BusinessError) }) it('HTTP 404 抛 ApiError 并带 statusCode', async () => { mockRequest.mockResolvedValue({ statusCode: 404, data: 'Not Found' }) await expect(request.get('/demo')).rejects.toMatchObject({ name: 'ApiError', message: 'HTTP 404', statusCode: 404 }) }) it('Taro.request 异常(断网等)抛 ApiError', async () => { mockRequest.mockRejectedValue(new Error('request:fail timeout')) await expect(request.get('/demo')).rejects.toMatchObject({ name: 'ApiError', message: 'request:fail timeout' }) }) it('需鉴权请求注入 Bearer token 头', async () => { mockRequest.mockResolvedValue(okBody(null)) await request.get('/demo') expect(mockRequest.mock.calls[0][0].header.Authorization).toBe('Bearer unit-token') }) it('needAuth: false 时不注入 Authorization', async () => { mockRequest.mockResolvedValue(okBody(null)) await request.post('/demo', {}, { needAuth: false }) expect(mockRequest.mock.calls[0][0].header.Authorization).toBeUndefined() }) it('BASE_URL 拼接:请求 url = BASE_URL + path', async () => { mockRequest.mockResolvedValue(okBody(null)) await request.get('/thirdPlatform/wxDevLogin/miniapp/login') expect(mockRequest.mock.calls[0][0].url).toMatch(/\/api\/biz\/thirdPlatform\/wxDevLogin\/miniapp\/login$/) }) })