import { beforeEach, describe, expect, it, mock } from 'bun:test'; import { SimpleProxyManager } from '../src/simple-proxy-manager'; import type { ProxyInfo } from '../src/types'; describe('ProxyManager', () => { let manager: SimpleProxyManager; const getMockProxies = (): ProxyInfo[] => [ { id: 'proxy1', host: '1.2.3.4', port: 8080, protocol: 'http', username: 'user1', password: 'pass1', active: true, }, { id: 'proxy2', host: '5.6.7.8', port: 8080, protocol: 'http', username: 'user2', password: 'pass2', active: true, }, { id: 'proxy3', host: '9.10.11.12', port: 8080, protocol: 'socks5', active: false, }, ]; beforeEach(() => { manager = new SimpleProxyManager(); }); describe('proxy management', () => { it('should add proxies', () => { const mockProxies = getMockProxies(); manager.addProxy(mockProxies[0]); manager.addProxy(mockProxies[1]); const proxies = manager.getProxies(); expect(proxies).toHaveLength(2); expect(proxies[0].id).toBe('proxy1'); expect(proxies[1].id).toBe('proxy2'); }); it('should remove proxy by id', () => { const mockProxies = getMockProxies(); manager.addProxy(mockProxies[0]); manager.addProxy(mockProxies[1]); manager.removeProxy('proxy1'); const proxies = manager.getProxies(); expect(proxies).toHaveLength(1); expect(proxies[0].id).toBe('proxy2'); }); it('should update proxy status', () => { const mockProxies = getMockProxies(); manager.addProxy(mockProxies[0]); manager.updateProxyStatus('proxy1', false); const proxies = manager.getProxies(); expect(proxies[0].active).toBe(false); }); it('should get only active proxies', () => { const mockProxies = getMockProxies(); mockProxies.forEach(proxy => manager.addProxy(proxy)); const activeProxies = manager.getActiveProxies(); expect(activeProxies).toHaveLength(2); expect(activeProxies.every(p => p.active)).toBe(true); }); }); describe('proxy rotation', () => { it('should rotate through proxies', () => { const mockProxies = getMockProxies(); manager.addProxy(mockProxies[0]); manager.addProxy(mockProxies[1]); const proxy1 = manager.getNextProxy(); const proxy2 = manager.getNextProxy(); const proxy3 = manager.getNextProxy(); expect(proxy1?.id).toBe('proxy1'); expect(proxy2?.id).toBe('proxy2'); expect(proxy3?.id).toBe('proxy1'); // Back to first }); it('should skip inactive proxies', () => { const mockProxies = getMockProxies(); mockProxies.forEach(proxy => manager.addProxy(proxy)); const proxy1 = manager.getNextProxy(); const proxy2 = manager.getNextProxy(); const proxy3 = manager.getNextProxy(); expect(proxy1?.id).toBe('proxy1'); expect(proxy2?.id).toBe('proxy2'); expect(proxy3?.id).toBe('proxy1'); // Skips proxy3 (inactive) }); it('should return null when no active proxies', () => { const mockProxies = getMockProxies(); manager.addProxy({ ...mockProxies[0], active: false }); const proxy = manager.getNextProxy(); expect(proxy).toBeNull(); }); }); describe('proxy configuration', () => { it('should get proxy config for HTTP proxy', () => { const mockProxies = getMockProxies(); manager.addProxy(mockProxies[0]); const proxy = manager.getNextProxy(); const config = manager.getProxyConfig(proxy!); expect(config).toEqual({ protocol: 'http', host: '1.2.3.4', port: 8080, auth: { username: 'user1', password: 'pass1', }, }); }); it('should get proxy config without auth', () => { const mockProxies = getMockProxies(); manager.addProxy(mockProxies[2]); manager.updateProxyStatus('proxy3', true); // Make it active const proxy = manager.getNextProxy(); const config = manager.getProxyConfig(proxy!); expect(config).toEqual({ protocol: 'socks5', host: '9.10.11.12', port: 8080, }); }); it('should format proxy URL', () => { const mockProxies = getMockProxies(); const url1 = manager.formatProxyUrl(mockProxies[0]); expect(url1).toBe('http://user1:pass1@1.2.3.4:8080'); const url2 = manager.formatProxyUrl(mockProxies[2]); expect(url2).toBe('socks5://9.10.11.12:8080'); }); }); describe('proxy validation', () => { it('should validate proxy connectivity', async () => { const mockProxies = getMockProxies(); // Mock fetch for validation const mockFetch = mock(() => Promise.resolve({ ok: true })); global.fetch = mockFetch as any; manager.addProxy(mockProxies[0]); const isValid = await manager.validateProxy('proxy1'); expect(mockFetch).toHaveBeenCalled(); expect(isValid).toBe(true); }); it('should handle validation failure', async () => { const mockProxies = getMockProxies(); const mockFetch = mock(() => Promise.reject(new Error('Connection failed'))); global.fetch = mockFetch as any; manager.addProxy(mockProxies[0]); const isValid = await manager.validateProxy('proxy1'); expect(isValid).toBe(false); }); it('should validate all proxies', async () => { const mockProxies = getMockProxies(); // Mock fetch to return different results for each proxy let callCount = 0; const mockFetch = mock(() => { callCount++; // First call succeeds, second fails if (callCount === 1) { return Promise.resolve({ ok: true }); } else { return Promise.reject(new Error('Failed')); } }); global.fetch = mockFetch as any; manager.addProxy(mockProxies[0]); manager.addProxy(mockProxies[1]); const results = await manager.validateAllProxies(); expect(results['proxy1']).toBe(true); expect(results['proxy2']).toBe(false); // Should disable failed proxy const activeProxies = manager.getActiveProxies(); expect(activeProxies).toHaveLength(1); expect(activeProxies[0].id).toBe('proxy1'); }); }); describe('statistics', () => { it('should track proxy statistics', () => { const mockProxies = getMockProxies(); mockProxies.forEach(proxy => manager.addProxy(proxy)); const stats = manager.getStatistics(); expect(stats).toEqual({ total: 3, active: 2, inactive: 1, byProtocol: { http: 2, socks5: 1, }, }); }); it('should clear all proxies', () => { const mockProxies = getMockProxies(); manager.addProxy(mockProxies[0]); manager.addProxy(mockProxies[1]); manager.clear(); const proxies = manager.getProxies(); expect(proxies).toHaveLength(0); }); }); });