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 = { text: 'example_text' }; const response = await SmartRequest.create() .url('https://httpbin.org/post') .json(testData) .post(); const body = await response.json(); expect(body).toHaveProperty('json'); expect(body.json).toHaveProperty('text'); expect(body.json.text).toEqual('example_text'); }); tap.test('client: should set headers correctly', async () => { const customHeader = 'X-Custom-Header'; const headerValue = 'test-value'; const response = await SmartRequest.create() .url('https://httpbin.org/headers') .header(customHeader, headerValue) .get(); const body = await response.json(); expect(body).toHaveProperty('headers'); // Check if the header exists (case-sensitive) expect(body.headers).toHaveProperty(customHeader); expect(body.headers[customHeader]).toEqual(headerValue); }); tap.test('client: should handle query parameters', async () => { const params = { param1: 'value1', param2: 'value2' }; const response = await SmartRequest.create() .url('https://httpbin.org/get') .query(params) .get(); const body = await response.json(); expect(body).toHaveProperty('args'); expect(body.args).toHaveProperty('param1'); expect(body.args.param1).toEqual('value1'); expect(body.args).toHaveProperty('param2'); expect(body.args.param2).toEqual('value2'); }); 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();