fixed format issues

This commit is contained in:
Boki 2025-06-26 16:12:27 -04:00
parent a700818a06
commit 08f713d98b
55 changed files with 5680 additions and 5533 deletions

View file

@ -1,286 +1,284 @@
import { afterEach, beforeEach, describe, expect, it, mock } from 'bun:test';
import { fetch } from '../src/fetch';
describe('Enhanced Fetch', () => {
let originalFetch: typeof globalThis.fetch;
let mockFetch: any;
let mockLogger: any;
beforeEach(() => {
originalFetch = globalThis.fetch;
mockFetch = mock(() => Promise.resolve(new Response('test')));
globalThis.fetch = mockFetch;
mockLogger = {
debug: mock(() => {}),
info: mock(() => {}),
error: mock(() => {}),
};
});
afterEach(() => {
globalThis.fetch = originalFetch;
});
describe('basic fetch', () => {
it('should make simple GET request', async () => {
const mockResponse = new Response('test data', { status: 200 });
mockFetch.mockResolvedValue(mockResponse);
const response = await fetch('https://api.example.com/data');
expect(mockFetch).toHaveBeenCalledWith('https://api.example.com/data', {
method: 'GET',
headers: {},
});
expect(response).toBe(mockResponse);
});
it('should make POST request with body', async () => {
const mockResponse = new Response('created', { status: 201 });
mockFetch.mockResolvedValue(mockResponse);
const body = JSON.stringify({ name: 'test' });
const response = await fetch('https://api.example.com/data', {
method: 'POST',
body,
headers: { 'Content-Type': 'application/json' },
});
expect(mockFetch).toHaveBeenCalledWith('https://api.example.com/data', {
method: 'POST',
body,
headers: { 'Content-Type': 'application/json' },
});
expect(response).toBe(mockResponse);
});
it('should handle URL objects', async () => {
const mockResponse = new Response('test');
mockFetch.mockResolvedValue(mockResponse);
const url = new URL('https://api.example.com/data');
await fetch(url);
expect(mockFetch).toHaveBeenCalledWith(url, expect.any(Object));
});
it('should handle Request objects', async () => {
const mockResponse = new Response('test');
mockFetch.mockResolvedValue(mockResponse);
const request = new Request('https://api.example.com/data', {
method: 'PUT',
});
await fetch(request);
expect(mockFetch).toHaveBeenCalledWith(request, expect.any(Object));
});
});
describe('proxy support', () => {
it('should add proxy to request options', async () => {
const mockResponse = new Response('proxy test');
mockFetch.mockResolvedValue(mockResponse);
await fetch('https://api.example.com/data', {
proxy: 'http://proxy.example.com:8080',
});
expect(mockFetch).toHaveBeenCalledWith(
'https://api.example.com/data',
expect.objectContaining({
proxy: 'http://proxy.example.com:8080',
})
);
});
it('should handle null proxy', async () => {
const mockResponse = new Response('no proxy');
mockFetch.mockResolvedValue(mockResponse);
await fetch('https://api.example.com/data', {
proxy: null,
});
expect(mockFetch).toHaveBeenCalledWith(
'https://api.example.com/data',
expect.not.objectContaining({
proxy: expect.anything(),
})
);
});
});
describe('timeout support', () => {
it('should handle timeout', async () => {
mockFetch.mockImplementation((url, options) => {
return new Promise((resolve, reject) => {
const timeoutId = setTimeout(() => resolve(new Response('delayed')), 100);
// Listen for abort signal
if (options?.signal) {
options.signal.addEventListener('abort', () => {
clearTimeout(timeoutId);
reject(new DOMException('The operation was aborted', 'AbortError'));
});
}
});
});
await expect(
fetch('https://api.example.com/data', { timeout: 50 })
).rejects.toThrow('The operation was aborted');
});
it('should clear timeout on success', async () => {
const mockResponse = new Response('quick response');
mockFetch.mockResolvedValue(mockResponse);
const response = await fetch('https://api.example.com/data', {
timeout: 1000,
});
expect(response).toBe(mockResponse);
});
it('should clear timeout on error', async () => {
mockFetch.mockRejectedValue(new Error('Network error'));
await expect(
fetch('https://api.example.com/data', { timeout: 1000 })
).rejects.toThrow('Network error');
});
});
describe('logging', () => {
it('should log request details', async () => {
const mockResponse = new Response('test', {
status: 200,
statusText: 'OK',
headers: new Headers({ 'content-type': 'text/plain' }),
});
mockFetch.mockResolvedValue(mockResponse);
await fetch('https://api.example.com/data', {
logger: mockLogger,
method: 'POST',
headers: { Authorization: 'Bearer token' },
});
expect(mockLogger.debug).toHaveBeenCalledWith('HTTP request', {
method: 'POST',
url: 'https://api.example.com/data',
headers: { Authorization: 'Bearer token' },
proxy: null,
});
expect(mockLogger.debug).toHaveBeenCalledWith('HTTP response', {
url: 'https://api.example.com/data',
status: 200,
statusText: 'OK',
ok: true,
headers: { 'content-type': 'text/plain' },
});
});
it('should log errors', async () => {
const error = new Error('Connection failed');
mockFetch.mockRejectedValue(error);
await expect(
fetch('https://api.example.com/data', { logger: mockLogger })
).rejects.toThrow('Connection failed');
expect(mockLogger.debug).toHaveBeenCalledWith('HTTP error', {
url: 'https://api.example.com/data',
error: 'Connection failed',
name: 'Error',
});
});
it('should use console as default logger', async () => {
const consoleSpy = mock(console.debug);
console.debug = consoleSpy;
const mockResponse = new Response('test');
mockFetch.mockResolvedValue(mockResponse);
await fetch('https://api.example.com/data');
expect(consoleSpy).toHaveBeenCalledTimes(2); // Request and response
console.debug = originalFetch as any;
});
});
describe('request options', () => {
it('should forward all standard RequestInit options', async () => {
const mockResponse = new Response('test');
mockFetch.mockResolvedValue(mockResponse);
const controller = new AbortController();
const options = {
method: 'PATCH' as const,
headers: { 'X-Custom': 'value' },
body: 'data',
signal: controller.signal,
credentials: 'include' as const,
cache: 'no-store' as const,
redirect: 'manual' as const,
referrer: 'https://referrer.com',
referrerPolicy: 'no-referrer' as const,
integrity: 'sha256-hash',
keepalive: true,
mode: 'cors' as const,
};
await fetch('https://api.example.com/data', options);
expect(mockFetch).toHaveBeenCalledWith(
'https://api.example.com/data',
expect.objectContaining(options)
);
});
it('should handle undefined options', async () => {
const mockResponse = new Response('test');
mockFetch.mockResolvedValue(mockResponse);
await fetch('https://api.example.com/data', undefined);
expect(mockFetch).toHaveBeenCalledWith(
'https://api.example.com/data',
expect.objectContaining({
method: 'GET',
headers: {},
})
);
});
});
describe('error handling', () => {
it('should propagate fetch errors', async () => {
const error = new TypeError('Failed to fetch');
mockFetch.mockRejectedValue(error);
await expect(fetch('https://api.example.com/data')).rejects.toThrow(
'Failed to fetch'
);
});
it('should handle non-Error objects', async () => {
mockFetch.mockRejectedValue('string error');
await expect(
fetch('https://api.example.com/data', { logger: mockLogger })
).rejects.toBe('string error');
expect(mockLogger.debug).toHaveBeenCalledWith('HTTP error', {
url: 'https://api.example.com/data',
error: 'string error',
name: 'Unknown',
});
});
});
});
import { afterEach, beforeEach, describe, expect, it, mock } from 'bun:test';
import { fetch } from '../src/fetch';
describe('Enhanced Fetch', () => {
let originalFetch: typeof globalThis.fetch;
let mockFetch: any;
let mockLogger: any;
beforeEach(() => {
originalFetch = globalThis.fetch;
mockFetch = mock(() => Promise.resolve(new Response('test')));
globalThis.fetch = mockFetch;
mockLogger = {
debug: mock(() => {}),
info: mock(() => {}),
error: mock(() => {}),
};
});
afterEach(() => {
globalThis.fetch = originalFetch;
});
describe('basic fetch', () => {
it('should make simple GET request', async () => {
const mockResponse = new Response('test data', { status: 200 });
mockFetch.mockResolvedValue(mockResponse);
const response = await fetch('https://api.example.com/data');
expect(mockFetch).toHaveBeenCalledWith('https://api.example.com/data', {
method: 'GET',
headers: {},
});
expect(response).toBe(mockResponse);
});
it('should make POST request with body', async () => {
const mockResponse = new Response('created', { status: 201 });
mockFetch.mockResolvedValue(mockResponse);
const body = JSON.stringify({ name: 'test' });
const response = await fetch('https://api.example.com/data', {
method: 'POST',
body,
headers: { 'Content-Type': 'application/json' },
});
expect(mockFetch).toHaveBeenCalledWith('https://api.example.com/data', {
method: 'POST',
body,
headers: { 'Content-Type': 'application/json' },
});
expect(response).toBe(mockResponse);
});
it('should handle URL objects', async () => {
const mockResponse = new Response('test');
mockFetch.mockResolvedValue(mockResponse);
const url = new URL('https://api.example.com/data');
await fetch(url);
expect(mockFetch).toHaveBeenCalledWith(url, expect.any(Object));
});
it('should handle Request objects', async () => {
const mockResponse = new Response('test');
mockFetch.mockResolvedValue(mockResponse);
const request = new Request('https://api.example.com/data', {
method: 'PUT',
});
await fetch(request);
expect(mockFetch).toHaveBeenCalledWith(request, expect.any(Object));
});
});
describe('proxy support', () => {
it('should add proxy to request options', async () => {
const mockResponse = new Response('proxy test');
mockFetch.mockResolvedValue(mockResponse);
await fetch('https://api.example.com/data', {
proxy: 'http://proxy.example.com:8080',
});
expect(mockFetch).toHaveBeenCalledWith(
'https://api.example.com/data',
expect.objectContaining({
proxy: 'http://proxy.example.com:8080',
})
);
});
it('should handle null proxy', async () => {
const mockResponse = new Response('no proxy');
mockFetch.mockResolvedValue(mockResponse);
await fetch('https://api.example.com/data', {
proxy: null,
});
expect(mockFetch).toHaveBeenCalledWith(
'https://api.example.com/data',
expect.not.objectContaining({
proxy: expect.anything(),
})
);
});
});
describe('timeout support', () => {
it('should handle timeout', async () => {
mockFetch.mockImplementation((url, options) => {
return new Promise((resolve, reject) => {
const timeoutId = setTimeout(() => resolve(new Response('delayed')), 100);
// Listen for abort signal
if (options?.signal) {
options.signal.addEventListener('abort', () => {
clearTimeout(timeoutId);
reject(new DOMException('The operation was aborted', 'AbortError'));
});
}
});
});
await expect(fetch('https://api.example.com/data', { timeout: 50 })).rejects.toThrow(
'The operation was aborted'
);
});
it('should clear timeout on success', async () => {
const mockResponse = new Response('quick response');
mockFetch.mockResolvedValue(mockResponse);
const response = await fetch('https://api.example.com/data', {
timeout: 1000,
});
expect(response).toBe(mockResponse);
});
it('should clear timeout on error', async () => {
mockFetch.mockRejectedValue(new Error('Network error'));
await expect(fetch('https://api.example.com/data', { timeout: 1000 })).rejects.toThrow(
'Network error'
);
});
});
describe('logging', () => {
it('should log request details', async () => {
const mockResponse = new Response('test', {
status: 200,
statusText: 'OK',
headers: new Headers({ 'content-type': 'text/plain' }),
});
mockFetch.mockResolvedValue(mockResponse);
await fetch('https://api.example.com/data', {
logger: mockLogger,
method: 'POST',
headers: { Authorization: 'Bearer token' },
});
expect(mockLogger.debug).toHaveBeenCalledWith('HTTP request', {
method: 'POST',
url: 'https://api.example.com/data',
headers: { Authorization: 'Bearer token' },
proxy: null,
});
expect(mockLogger.debug).toHaveBeenCalledWith('HTTP response', {
url: 'https://api.example.com/data',
status: 200,
statusText: 'OK',
ok: true,
headers: { 'content-type': 'text/plain' },
});
});
it('should log errors', async () => {
const error = new Error('Connection failed');
mockFetch.mockRejectedValue(error);
await expect(fetch('https://api.example.com/data', { logger: mockLogger })).rejects.toThrow(
'Connection failed'
);
expect(mockLogger.debug).toHaveBeenCalledWith('HTTP error', {
url: 'https://api.example.com/data',
error: 'Connection failed',
name: 'Error',
});
});
it('should use console as default logger', async () => {
const consoleSpy = mock(console.debug);
console.debug = consoleSpy;
const mockResponse = new Response('test');
mockFetch.mockResolvedValue(mockResponse);
await fetch('https://api.example.com/data');
expect(consoleSpy).toHaveBeenCalledTimes(2); // Request and response
console.debug = originalFetch as any;
});
});
describe('request options', () => {
it('should forward all standard RequestInit options', async () => {
const mockResponse = new Response('test');
mockFetch.mockResolvedValue(mockResponse);
const controller = new AbortController();
const options = {
method: 'PATCH' as const,
headers: { 'X-Custom': 'value' },
body: 'data',
signal: controller.signal,
credentials: 'include' as const,
cache: 'no-store' as const,
redirect: 'manual' as const,
referrer: 'https://referrer.com',
referrerPolicy: 'no-referrer' as const,
integrity: 'sha256-hash',
keepalive: true,
mode: 'cors' as const,
};
await fetch('https://api.example.com/data', options);
expect(mockFetch).toHaveBeenCalledWith(
'https://api.example.com/data',
expect.objectContaining(options)
);
});
it('should handle undefined options', async () => {
const mockResponse = new Response('test');
mockFetch.mockResolvedValue(mockResponse);
await fetch('https://api.example.com/data', undefined);
expect(mockFetch).toHaveBeenCalledWith(
'https://api.example.com/data',
expect.objectContaining({
method: 'GET',
headers: {},
})
);
});
});
describe('error handling', () => {
it('should propagate fetch errors', async () => {
const error = new TypeError('Failed to fetch');
mockFetch.mockRejectedValue(error);
await expect(fetch('https://api.example.com/data')).rejects.toThrow('Failed to fetch');
});
it('should handle non-Error objects', async () => {
mockFetch.mockRejectedValue('string error');
await expect(fetch('https://api.example.com/data', { logger: mockLogger })).rejects.toBe(
'string error'
);
expect(mockLogger.debug).toHaveBeenCalledWith('HTTP error', {
url: 'https://api.example.com/data',
error: 'string error',
name: 'Unknown',
});
});
});
});

View file

@ -1,60 +1,60 @@
import { describe, expect, it } from 'bun:test';
import { getRandomUserAgent } from '../src/user-agent';
describe('User Agent', () => {
describe('getRandomUserAgent', () => {
it('should return a user agent string', () => {
const userAgent = getRandomUserAgent();
expect(typeof userAgent).toBe('string');
expect(userAgent.length).toBeGreaterThan(0);
});
it('should return a valid user agent containing Mozilla', () => {
const userAgent = getRandomUserAgent();
expect(userAgent).toContain('Mozilla');
});
it('should return different user agents on multiple calls', () => {
const userAgents = new Set();
// Get 20 user agents
for (let i = 0; i < 20; i++) {
userAgents.add(getRandomUserAgent());
}
// Should have at least 2 different user agents
expect(userAgents.size).toBeGreaterThan(1);
});
it('should return user agents with browser identifiers', () => {
const userAgent = getRandomUserAgent();
const hasBrowser =
userAgent.includes('Chrome') ||
userAgent.includes('Firefox') ||
userAgent.includes('Safari') ||
userAgent.includes('Edg');
expect(hasBrowser).toBe(true);
});
it('should return user agents with OS identifiers', () => {
const userAgent = getRandomUserAgent();
const hasOS =
userAgent.includes('Windows') ||
userAgent.includes('Macintosh') ||
userAgent.includes('Mac OS X');
expect(hasOS).toBe(true);
});
it('should handle multiple concurrent calls', () => {
const promises = Array(10)
.fill(null)
.map(() => Promise.resolve(getRandomUserAgent()));
return Promise.all(promises).then(userAgents => {
expect(userAgents).toHaveLength(10);
userAgents.forEach(ua => {
expect(typeof ua).toBe('string');
expect(ua.length).toBeGreaterThan(0);
});
});
});
});
});
import { describe, expect, it } from 'bun:test';
import { getRandomUserAgent } from '../src/user-agent';
describe('User Agent', () => {
describe('getRandomUserAgent', () => {
it('should return a user agent string', () => {
const userAgent = getRandomUserAgent();
expect(typeof userAgent).toBe('string');
expect(userAgent.length).toBeGreaterThan(0);
});
it('should return a valid user agent containing Mozilla', () => {
const userAgent = getRandomUserAgent();
expect(userAgent).toContain('Mozilla');
});
it('should return different user agents on multiple calls', () => {
const userAgents = new Set();
// Get 20 user agents
for (let i = 0; i < 20; i++) {
userAgents.add(getRandomUserAgent());
}
// Should have at least 2 different user agents
expect(userAgents.size).toBeGreaterThan(1);
});
it('should return user agents with browser identifiers', () => {
const userAgent = getRandomUserAgent();
const hasBrowser =
userAgent.includes('Chrome') ||
userAgent.includes('Firefox') ||
userAgent.includes('Safari') ||
userAgent.includes('Edg');
expect(hasBrowser).toBe(true);
});
it('should return user agents with OS identifiers', () => {
const userAgent = getRandomUserAgent();
const hasOS =
userAgent.includes('Windows') ||
userAgent.includes('Macintosh') ||
userAgent.includes('Mac OS X');
expect(hasOS).toBe(true);
});
it('should handle multiple concurrent calls', () => {
const promises = Array(10)
.fill(null)
.map(() => Promise.resolve(getRandomUserAgent()));
return Promise.all(promises).then(userAgents => {
expect(userAgents).toHaveLength(10);
userAgents.forEach(ua => {
expect(typeof ua).toBe('string');
expect(ua.length).toBeGreaterThan(0);
});
});
});
});
});