/** * HTTP client unit tests */ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { makeRequest } from '../../src/http'; import { IyzicoResponseError, IyzicoRequestError, IyzicoError, } from '../../src/errors'; describe('makeRequest', () => { const apiKey = 'test-api-key'; const secretKey = 'test-secret-key'; const baseUrl = 'https://api.test.com'; // Store original fetch to restore it later const originalFetch = globalThis.fetch; const mockFetch = vi.fn(); beforeEach(() => { // Mock fetch before each test globalThis.fetch = mockFetch as any; mockFetch.mockClear(); }); afterEach(() => { // Restore original fetch after each test globalThis.fetch = originalFetch; vi.clearAllMocks(); }); it('should make a successful POST request', async () => { const mockResponse = { status: 'success', data: 'test data', }; mockFetch.mockResolvedValueOnce({ ok: true, json: async () => mockResponse, }); const result = await makeRequest({ method: 'POST', path: '/test', body: { test: 'value' }, apiKey, secretKey, baseUrl, }); expect(result).toEqual(mockResponse); expect(mockFetch).toHaveBeenCalledTimes(1); const callArgs = mockFetch.mock.calls[0]; expect(callArgs[0]).toBe('https://api.test.com/test'); expect(callArgs[1].method).toBe('POST'); expect(callArgs[1].headers).toHaveProperty('Authorization'); expect(callArgs[1].headers).toHaveProperty('Content-Type', 'application/json'); expect(callArgs[1].headers).toHaveProperty('x-iyzi-rnd'); }); it('should make a successful GET request without body', async () => { const mockResponse = { status: 'success', data: 'test data', }; mockFetch.mockResolvedValueOnce({ ok: true, json: async () => mockResponse, }); const result = await makeRequest({ method: 'GET', path: '/test', apiKey, secretKey, baseUrl, }); expect(result).toEqual(mockResponse); expect(mockFetch).toHaveBeenCalledTimes(1); const callArgs = mockFetch.mock.calls[0]; expect(callArgs[1].body).toBeUndefined(); }); it('should throw IyzicoResponseError for failure responses', async () => { const errorResponse = { status: 'failure', errorCode: 'TEST_ERROR', errorMessage: 'Test error message', }; mockFetch.mockResolvedValueOnce({ ok: false, json: async () => errorResponse, }); await expect( makeRequest({ method: 'POST', path: '/test', body: { test: 'value' }, apiKey, secretKey, baseUrl, }), ).rejects.toThrow(IyzicoResponseError); }); it('should throw IyzicoResponseError for failure status in response', async () => { const errorResponse = { status: 'failure', errorCode: 'TEST_ERROR', errorMessage: 'Test error message', }; mockFetch.mockResolvedValueOnce({ ok: true, json: async () => errorResponse, }); await expect( makeRequest({ method: 'POST', path: '/test', body: { test: 'value' }, apiKey, secretKey, baseUrl, }), ).rejects.toThrow(IyzicoResponseError); }); it('should use fallback message when errorMessage is missing', async () => { const errorResponse = { status: 'failure', errorCode: 'TEST_ERROR', // errorMessage is missing }; mockFetch.mockResolvedValueOnce({ ok: true, json: async () => errorResponse, }); await expect( makeRequest({ method: 'POST', path: '/test', body: { test: 'value' }, apiKey, secretKey, baseUrl, }), ).rejects.toThrow(IyzicoResponseError); }); it('should handle network errors with fetch in message', async () => { const networkError = new TypeError('Failed to fetch'); mockFetch.mockRejectedValueOnce(networkError); await expect( makeRequest({ method: 'POST', path: '/test', body: { test: 'value' }, apiKey, secretKey, baseUrl, }), ).rejects.toThrow(IyzicoRequestError); }); it('should rethrow IyzicoResponseError', async () => { const errorResponse = { status: 'failure', errorCode: 'TEST_ERROR', errorMessage: 'Test error', } as const; const responseError = new IyzicoResponseError('Test error', errorResponse); mockFetch.mockRejectedValueOnce(responseError); await expect( makeRequest({ method: 'POST', path: '/test', body: { test: 'value' }, apiKey, secretKey, baseUrl, }), ).rejects.toThrow(IyzicoResponseError); }); it('should rethrow IyzicoRequestError', async () => { const requestError = new IyzicoRequestError('Request failed'); mockFetch.mockRejectedValueOnce(requestError); await expect( makeRequest({ method: 'POST', path: '/test', body: { test: 'value' }, apiKey, secretKey, baseUrl, }), ).rejects.toThrow(IyzicoRequestError); }); it('should rethrow IyzicoError', async () => { const iyzicoError = new IyzicoError('Iyzico error'); mockFetch.mockRejectedValueOnce(iyzicoError); await expect( makeRequest({ method: 'POST', path: '/test', body: { test: 'value' }, apiKey, secretKey, baseUrl, }), ).rejects.toThrow(IyzicoError); }); it('should wrap unknown errors in IyzicoError', async () => { const unknownError = new Error('Unknown error'); mockFetch.mockRejectedValueOnce(unknownError); await expect( makeRequest({ method: 'POST', path: '/test', body: { test: 'value' }, apiKey, secretKey, baseUrl, }), ).rejects.toThrow(IyzicoError); }); it('should wrap non-Error objects in IyzicoError', async () => { mockFetch.mockRejectedValueOnce('String error'); await expect( makeRequest({ method: 'POST', path: '/test', body: { test: 'value' }, apiKey, secretKey, baseUrl, }), ).rejects.toThrow(IyzicoError); }); it('should handle DELETE request with body', async () => { const mockResponse = { status: 'success', data: 'deleted', }; mockFetch.mockResolvedValueOnce({ ok: true, json: async () => mockResponse, }); const result = await makeRequest({ method: 'DELETE', path: '/test', body: { id: '123' }, apiKey, secretKey, baseUrl, }); expect(result).toEqual(mockResponse); const callArgs = mockFetch.mock.calls[0]; expect(callArgs[1].method).toBe('DELETE'); expect(callArgs[1].body).toBe(JSON.stringify({ id: '123' })); }); it('should handle GET request with query string', async () => { const mockResponse = { status: 'success', data: 'test data', }; mockFetch.mockResolvedValueOnce({ ok: true, json: async () => mockResponse, }); const result = await makeRequest({ method: 'GET', path: '/test?param=value', apiKey, secretKey, baseUrl, }); expect(result).toEqual(mockResponse); const callArgs = mockFetch.mock.calls[0]; expect(callArgs[0]).toBe('https://api.test.com/test?param=value'); }); });