import { tap, expect } from '@git.zone/tstest/tapbundle'; import { SmartRequest } from '../ts/client/index.js'; tap.test('client: should request a html document over https', async () => { const response = await SmartRequest.create() .url('https://encrypted.google.com/') .get(); expect(response).not.toBeNull(); expect(response).toHaveProperty('status'); expect(response.status).toBeGreaterThan(0); const text = await response.text(); expect(text.length).toBeGreaterThan(0); }); tap.test('client: should request a JSON document over https', async () => { const response = await SmartRequest.create() .url('https://jsonplaceholder.typicode.com/posts/1') .get(); const body = await response.json(); expect(body).toHaveProperty('id'); expect(body.id).toEqual(1); }); tap.test('client: should post a JSON document over http', async () => { const testData = { title: 'example_text', body: 'test body', userId: 1 }; const response = await SmartRequest.create() .url('https://jsonplaceholder.typicode.com/posts') .json(testData) .post(); const body = await response.json(); expect(body).toHaveProperty('title'); expect(body.title).toEqual('example_text'); expect(body).toHaveProperty('id'); // jsonplaceholder returns an id for created posts }); tap.test('client: should set headers correctly', async () => { const customHeader = 'X-Custom-Header'; const headerValue = 'test-value'; const response = await SmartRequest.create() .url('https://echo.zuplo.io/') .header(customHeader, headerValue) .get(); const body = await response.json(); expect(body).toHaveProperty('headers'); // Check if the header exists (headers might be lowercase) const headers = body.headers; const headerFound = headers[customHeader] || headers[customHeader.toLowerCase()] || headers['x-custom-header']; expect(headerFound).toEqual(headerValue); }); tap.test('client: should handle query parameters', async () => { const params = { userId: '1' }; const response = await SmartRequest.create() .url('https://jsonplaceholder.typicode.com/posts') .query(params) .get(); const body = await response.json(); expect(Array.isArray(body)).toBeTrue(); // Check that we got posts for userId 1 if (body.length > 0) { expect(body[0]).toHaveProperty('userId'); expect(body[0].userId).toEqual(1); } }); tap.test('client: should handle timeout configuration', async () => { // This test just verifies that the timeout method doesn't throw const client = SmartRequest.create() .url('https://jsonplaceholder.typicode.com/posts/1') .timeout(5000); const response = await client.get(); expect(response).toHaveProperty('ok'); expect(response.ok).toBeTrue(); }); tap.test('client: should handle retry configuration', async () => { // This test just verifies that the retry method doesn't throw const client = SmartRequest.create() .url('https://jsonplaceholder.typicode.com/posts/1') .retry(1); const response = await client.get(); expect(response).toHaveProperty('ok'); expect(response.ok).toBeTrue(); }); tap.test('client: should support keepAlive option for connection reuse', async () => { // Test basic keepAlive functionality const responses = []; // Make multiple requests with keepAlive enabled for (let i = 0; i < 3; i++) { const response = await SmartRequest.create() .url('https://jsonplaceholder.typicode.com/posts/1') .options({ keepAlive: true }) .header('X-Request-Number', String(i)) .get(); expect(response.ok).toBeTrue(); responses.push(response); } // Verify all requests succeeded expect(responses).toHaveLength(3); // Also test that keepAlive: false works const responseNoKeepAlive = await SmartRequest.create() .url('https://jsonplaceholder.typicode.com/posts/2') .options({ keepAlive: false }) .get(); expect(responseNoKeepAlive.ok).toBeTrue(); // Verify we can parse the responses const data = await responses[0].json(); expect(data).toHaveProperty('id'); expect(data.id).toEqual(1); }); tap.test('client: should handle 429 rate limiting with default config', async () => { // Test that handle429Backoff can be configured without errors const client = SmartRequest.create() .url('https://jsonplaceholder.typicode.com/posts/1') .handle429Backoff(); const response = await client.get(); expect(response.status).toEqual(200); }); tap.test('client: should handle 429 with custom config', async () => { let rateLimitCallbackCalled = false; let attemptCount = 0; let waitTimeReceived = 0; const client = SmartRequest.create() .url('https://jsonplaceholder.typicode.com/posts/1') .handle429Backoff({ maxRetries: 2, fallbackDelay: 500, maxWaitTime: 5000, onRateLimit: (attempt, waitTime) => { rateLimitCallbackCalled = true; attemptCount = attempt; waitTimeReceived = waitTime; } }); const response = await client.get(); expect(response.status).toEqual(200); // The callback should not have been called for a 200 response expect(rateLimitCallbackCalled).toBeFalse(); }); tap.test('client: should respect Retry-After header format (seconds)', async () => { // Test the configuration works - actual 429 testing would require a mock server const client = SmartRequest.create() .url('https://jsonplaceholder.typicode.com/posts/1') .handle429Backoff({ maxRetries: 1, respectRetryAfter: true }); const response = await client.get(); expect(response.ok).toBeTrue(); }); tap.test('client: should handle rate limiting with exponential backoff', async () => { // Test exponential backoff configuration const client = SmartRequest.create() .url('https://jsonplaceholder.typicode.com/posts/1') .handle429Backoff({ maxRetries: 3, fallbackDelay: 100, backoffFactor: 2, maxWaitTime: 1000 }); const response = await client.get(); expect(response.status).toEqual(200); }); tap.test('client: should not retry non-429 errors with rate limit handler', async () => { // Test that 404 errors are not retried by rate limit handler const client = SmartRequest.create() .url('https://jsonplaceholder.typicode.com/posts/999999') .handle429Backoff(); const response = await client.get(); expect(response.status).toEqual(404); expect(response.ok).toBeFalse(); }); tap.start();