import { describe, it, expect, vi, beforeEach } from 'vitest' import { configureHttp, http } from '../../../src/core/http' describe('core/http', () => { beforeEach(() => { configureHttp('https://api.test.com', () => 'test_token') vi.restoreAllMocks() }) it('should configure base URL and token getter', async () => { globalThis.fetch = vi.fn().mockResolvedValue({ json: vi.fn().mockResolvedValue({ code: 200, data: { id: 1 }, message: 'ok' }), } as unknown as Response) await http.get('/api/test') expect(fetch).toHaveBeenCalledWith( 'https://api.test.com/api/test', expect.objectContaining({ method: 'GET', headers: expect.objectContaining({ Authorization: 'Bearer test_token', 'Content-Type': 'application/json', }), }) ) }) it('should build URL with query parameters', async () => { globalThis.fetch = vi.fn().mockResolvedValue({ json: vi.fn().mockResolvedValue({ code: 200, data: [], message: 'ok' }), } as unknown as Response) await http.get('/api/list', { page: 0, size: 20, keyword: 'hello' }) const url = (fetch as ReturnType).mock.calls[0][0] as string expect(url).toContain('page=0') expect(url).toContain('size=20') expect(url).toContain('keyword=hello') }) it('should omit null, undefined and empty query values', async () => { globalThis.fetch = vi.fn().mockResolvedValue({ json: vi.fn().mockResolvedValue({ code: 200, data: [], message: 'ok' }), } as unknown as Response) await http.get('/api/list', { a: null, b: undefined, c: '', d: 'value' }) const url = (fetch as ReturnType).mock.calls[0][0] as string expect(url).not.toContain('a=') expect(url).not.toContain('b=') expect(url).not.toContain('c=') expect(url).toContain('d=value') }) it('should send POST with JSON body', async () => { globalThis.fetch = vi.fn().mockResolvedValue({ json: vi.fn().mockResolvedValue({ code: 200, data: { id: 1 }, message: 'ok' }), } as unknown as Response) const body = { name: 'test' } await http.post('/api/create', body) expect(fetch).toHaveBeenCalledWith( 'https://api.test.com/api/create', expect.objectContaining({ method: 'POST', body: JSON.stringify(body), }) ) }) it('should throw when response code is not 200', async () => { globalThis.fetch = vi.fn().mockResolvedValue({ json: vi.fn().mockResolvedValue({ code: 500, data: null, message: 'Server Error' }), } as unknown as Response) await expect(http.get('/api/error')).rejects.toThrow('Server Error') }) it('should handle Date query parameters as ISO string', async () => { globalThis.fetch = vi.fn().mockResolvedValue({ json: vi.fn().mockResolvedValue({ code: 200, data: [], message: 'ok' }), } as unknown as Response) const date = new Date('2026-04-30T00:00:00.000Z') await http.get('/api/list', { createdAt: date }) const url = (fetch as ReturnType).mock.calls[0][0] as string expect(decodeURIComponent(url)).toContain('createdAt=2026-04-30T00:00:00.000Z') }) it('should strip trailing slash from base URL', async () => { configureHttp('https://api.test.com/', () => 'token') globalThis.fetch = vi.fn().mockResolvedValue({ json: vi.fn().mockResolvedValue({ code: 200, data: [], message: 'ok' }), } as unknown as Response) await http.get('/api/test') const url = (fetch as ReturnType).mock.calls[0][0] as string expect(url).toBe('https://api.test.com/api/test') }) it('should handle PUT and DELETE methods', async () => { globalThis.fetch = vi.fn().mockResolvedValue({ json: vi.fn().mockResolvedValue({ code: 200, data: null, message: 'ok' }), } as unknown as Response) await http.put('/api/update', { name: 'new' }) expect(fetch).toHaveBeenCalledWith( expect.stringContaining('/api/update'), expect.objectContaining({ method: 'PUT' }) ) await http.delete('/api/delete', { id: 1 }) expect(fetch).toHaveBeenCalledWith( expect.stringContaining('/api/delete'), expect.objectContaining({ method: 'DELETE' }) ) }) })