removed old tests, created new ones and format
This commit is contained in:
parent
7579afa3c3
commit
b03231b849
57 changed files with 4092 additions and 5901 deletions
254
libs/services/proxy/src/proxy.test.ts
Normal file
254
libs/services/proxy/src/proxy.test.ts
Normal file
|
|
@ -0,0 +1,254 @@
|
|||
import { beforeEach, describe, expect, it, mock } from 'bun:test';
|
||||
import { SimpleProxyManager } from './simple-proxy-manager';
|
||||
import type { ProxyConfig, ProxyInfo } from './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);
|
||||
});
|
||||
});
|
||||
});
|
||||
123
libs/services/proxy/src/simple-proxy-manager.ts
Normal file
123
libs/services/proxy/src/simple-proxy-manager.ts
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
import type { ProxyInfo, ProxyConfig } from './types';
|
||||
|
||||
/**
|
||||
* Simple proxy manager for testing
|
||||
*/
|
||||
export class SimpleProxyManager {
|
||||
private proxies: ProxyInfo[] = [];
|
||||
private currentIndex = 0;
|
||||
private activeProxyIndex = 0;
|
||||
|
||||
addProxy(proxy: ProxyInfo): void {
|
||||
this.proxies.push(proxy);
|
||||
}
|
||||
|
||||
removeProxy(id: string): void {
|
||||
this.proxies = this.proxies.filter(p => p.id !== id);
|
||||
}
|
||||
|
||||
updateProxyStatus(id: string, active: boolean): void {
|
||||
const proxy = this.proxies.find(p => p.id === id);
|
||||
if (proxy) {
|
||||
proxy.active = active;
|
||||
}
|
||||
}
|
||||
|
||||
getProxies(): ProxyInfo[] {
|
||||
return [...this.proxies];
|
||||
}
|
||||
|
||||
getActiveProxies(): ProxyInfo[] {
|
||||
return this.proxies.filter(p => p.active);
|
||||
}
|
||||
|
||||
getNextProxy(): ProxyInfo | null {
|
||||
const activeProxies = this.getActiveProxies();
|
||||
if (activeProxies.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const proxy = activeProxies[this.activeProxyIndex % activeProxies.length];
|
||||
this.activeProxyIndex++;
|
||||
return proxy;
|
||||
}
|
||||
|
||||
getProxyConfig(proxy: ProxyInfo): ProxyConfig {
|
||||
const config: ProxyConfig = {
|
||||
protocol: proxy.protocol,
|
||||
host: proxy.host,
|
||||
port: proxy.port,
|
||||
};
|
||||
|
||||
if (proxy.username && proxy.password) {
|
||||
config.auth = {
|
||||
username: proxy.username,
|
||||
password: proxy.password,
|
||||
};
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
formatProxyUrl(proxy: ProxyInfo): string {
|
||||
let url = `${proxy.protocol}://`;
|
||||
if (proxy.username && proxy.password) {
|
||||
url += `${proxy.username}:${proxy.password}@`;
|
||||
}
|
||||
url += `${proxy.host}:${proxy.port}`;
|
||||
return url;
|
||||
}
|
||||
|
||||
async validateProxy(id: string): Promise<boolean> {
|
||||
const proxy = this.proxies.find(p => p.id === id);
|
||||
if (!proxy) return false;
|
||||
|
||||
try {
|
||||
const proxyUrl = this.formatProxyUrl(proxy);
|
||||
const response = await fetch('https://httpbin.org/ip', {
|
||||
// @ts-ignore - proxy option might not be in types
|
||||
proxy: proxyUrl,
|
||||
signal: AbortSignal.timeout(5000),
|
||||
});
|
||||
return response.ok;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async validateAllProxies(): Promise<Record<string, boolean>> {
|
||||
const results: Record<string, boolean> = {};
|
||||
|
||||
for (const proxy of this.proxies) {
|
||||
const isValid = await this.validateProxy(proxy.id);
|
||||
results[proxy.id] = isValid;
|
||||
|
||||
// Disable invalid proxies
|
||||
if (!isValid) {
|
||||
this.updateProxyStatus(proxy.id, false);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
getStatistics() {
|
||||
const stats = {
|
||||
total: this.proxies.length,
|
||||
active: this.proxies.filter(p => p.active).length,
|
||||
inactive: this.proxies.filter(p => !p.active).length,
|
||||
byProtocol: {} as Record<string, number>,
|
||||
};
|
||||
|
||||
this.proxies.forEach(proxy => {
|
||||
stats.byProtocol[proxy.protocol] = (stats.byProtocol[proxy.protocol] || 0) + 1;
|
||||
});
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.proxies = [];
|
||||
this.currentIndex = 0;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue