capabable spawning multiple openvpn instances

This commit is contained in:
2025-12-11 00:36:46 +01:00
parent c9da56e8e9
commit 470f0922ed
17 changed files with 3000 additions and 104 deletions

View File

@@ -0,0 +1,379 @@
// tests/vpn_integration_tests.rs
//! Integration tests for VPN rotation system
#[cfg(test)]
mod vpn_tests {
use event_backtest_engine::{
scraper::{
webdriver::ChromeDriverPool,
vpn_manager::{VpnInstance, VpnPool},
},
util::{directories::DataPaths, opnv},
};
use std::path::PathBuf;
use std::sync::Arc;
/// Helper to create a test VPN instance without connecting
fn create_test_vpn_instance() -> VpnInstance {
VpnInstance::new(
PathBuf::from("test.ovpn"),
"testuser".to_string(),
"testpass".to_string(),
)
.expect("Failed to create test VPN instance")
}
#[test]
fn test_vpn_instance_creation() {
let vpn = create_test_vpn_instance();
assert_eq!(vpn.hostname(), "test");
assert!(!vpn.is_healthy());
assert!(vpn.external_ip().is_none());
}
#[test]
fn test_vpn_task_counting() {
let mut vpn = create_test_vpn_instance();
// Should not rotate initially
assert!(!vpn.increment_task_count(10));
// Increment tasks
for i in 1..10 {
assert!(!vpn.increment_task_count(10), "Should not rotate at task {}", i);
}
// Should rotate at threshold
assert!(vpn.increment_task_count(10), "Should rotate at task 10");
// Reset and verify
vpn.reset_task_count();
assert!(!vpn.increment_task_count(10), "Should not rotate after reset");
}
#[test]
fn test_vpn_task_counting_zero_threshold() {
let mut vpn = create_test_vpn_instance();
// With threshold=0, should never auto-rotate
for _ in 0..100 {
assert!(!vpn.increment_task_count(0));
}
}
#[tokio::test]
async fn test_chromedriver_pool_creation_no_vpn() {
let result = ChromeDriverPool::new(2).await;
match result {
Ok(pool) => {
assert_eq!(pool.get_number_of_instances(), 2);
assert!(!pool.is_vpn_enabled());
}
Err(e) => {
eprintln!("ChromeDriver pool creation failed (expected if chromedriver not installed): {}", e);
}
}
}
#[test]
fn test_data_paths_creation() {
let paths = DataPaths::new("./test_data").expect("Failed to create paths");
assert!(paths.data_dir().exists());
assert!(paths.cache_dir().exists());
assert!(paths.logs_dir().exists());
assert!(paths.cache_openvpn_dir().exists());
// Cleanup
let _ = std::fs::remove_dir_all("./test_data");
}
#[tokio::test]
#[ignore] // This test requires actual network access and VPNBook availability
async fn test_fetch_vpnbook_configs() {
let paths = DataPaths::new(".").expect("Failed to create paths");
// This test requires a ChromeDriver pool
let pool_result = ChromeDriverPool::new(1).await;
if pool_result.is_err() {
eprintln!("Skipping VPNBook fetch test: ChromeDriver not available");
return;
}
let pool = Arc::new(pool_result.unwrap());
let result = opnv::fetch_vpnbook_configs(&pool, paths.cache_dir()).await;
match result {
Ok((username, password, files)) => {
assert!(!username.is_empty(), "Username should not be empty");
assert!(!password.is_empty(), "Password should not be empty");
assert!(!files.is_empty(), "Should fetch at least one config file");
println!("Fetched {} VPN configs", files.len());
for file in &files {
assert!(file.exists(), "Config file should exist: {:?}", file);
assert_eq!(file.extension().and_then(|s| s.to_str()), Some("ovpn"));
}
}
Err(e) => {
eprintln!("VPNBook fetch failed (may be temporary): {}", e);
}
}
}
#[tokio::test]
#[ignore] // Requires actual VPN configs and OpenVPN installation
async fn test_vpn_pool_creation() {
let paths = DataPaths::new(".").expect("Failed to create paths");
// First fetch configs
let pool_result = ChromeDriverPool::new(1).await;
if pool_result.is_err() {
eprintln!("Skipping VPN pool test: ChromeDriver not available");
return;
}
let temp_pool = Arc::new(pool_result.unwrap());
let fetch_result = opnv::fetch_vpnbook_configs(&temp_pool, paths.cache_dir()).await;
if fetch_result.is_err() {
eprintln!("Skipping VPN pool test: Could not fetch configs");
return;
}
let (username, password, _) = fetch_result.unwrap();
// Create VPN pool
let vpn_pool_result = VpnPool::new(
paths.cache_openvpn_dir(),
username,
password,
false,
0,
).await;
match vpn_pool_result {
Ok(vpn_pool) => {
assert!(vpn_pool.len() > 0, "VPN pool should have at least one instance");
println!("Created VPN pool with {} instances", vpn_pool.len());
}
Err(e) => {
eprintln!("VPN pool creation failed: {}", e);
}
}
}
#[tokio::test]
#[ignore] // Full integration test - requires all components
async fn test_full_vpn_integration() {
let paths = DataPaths::new(".").expect("Failed to create paths");
// Step 1: Create temp ChromeDriver pool for fetching
let temp_pool = match ChromeDriverPool::new(1).await {
Ok(p) => Arc::new(p),
Err(e) => {
eprintln!("Skipping integration test: ChromeDriver not available - {}", e);
return;
}
};
// Step 2: Fetch VPNBook configs
let (username, password, files) = match opnv::fetch_vpnbook_configs(
&temp_pool,
paths.cache_dir()
).await {
Ok(result) => result,
Err(e) => {
eprintln!("Skipping integration test: Config fetch failed - {}", e);
return;
}
};
assert!(!files.is_empty(), "Should have fetched configs");
// Step 3: Create VPN pool
let vpn_pool = match VpnPool::new(
paths.cache_openvpn_dir(),
username,
password,
true,
5,
).await {
Ok(pool) => Arc::new(pool),
Err(e) => {
eprintln!("Skipping integration test: VPN pool creation failed - {}", e);
return;
}
};
// Step 4: Connect one VPN
let vpn_instance = vpn_pool.acquire().await.expect("Failed to acquire VPN");
let connect_result = {
let mut vpn = vpn_instance.lock().await;
vpn.connect().await
};
match connect_result {
Ok(_) => {
let vpn = vpn_instance.lock().await;
println!("✓ VPN connected: {} ({})",
vpn.hostname(),
vpn.external_ip().unwrap_or("unknown")
);
assert!(vpn.is_healthy());
assert!(vpn.external_ip().is_some());
}
Err(e) => {
eprintln!("VPN connection failed: {}", e);
}
}
// Step 5: Create ChromeDriver pool with VPN
let driver_pool_result = ChromeDriverPool::new_with_vpn(
1,
Some(vpn_pool.clone())
).await;
match driver_pool_result {
Ok(driver_pool) => {
assert!(driver_pool.is_vpn_enabled());
println!("✓ ChromeDriver pool created with VPN binding");
}
Err(e) => {
eprintln!("ChromeDriver pool creation failed: {}", e);
}
}
// Step 6: Cleanup
vpn_pool.disconnect_all().await.expect("Failed to disconnect VPNs");
println!("✓ Integration test complete");
}
#[test]
fn test_hostname_extraction() {
// Test the hostname extraction logic
let test_cases = vec![
("test/ca149.vpnbook.com/config.ovpn", "ca149.vpnbook.com"),
("test/us1.vpnbook.com/config.ovpn", "us1.vpnbook.com"),
("test/de4.vpnbook.com/config.ovpn", "de4.vpnbook.com"),
];
for (path, expected_hostname) in test_cases {
let pb = PathBuf::from(path);
let hostname = pb.parent()
.and_then(|p| p.file_name())
.and_then(|n| n.to_str())
.unwrap_or("unknown");
assert_eq!(hostname, expected_hostname);
}
}
#[cfg(target_os = "windows")]
#[test]
fn test_forcebindip_manager_creation() {
use event_backtest_engine::ForceBindIpManager;
match ForceBindIpManager::new() {
Ok(manager) => {
println!("✓ ForceBindIP found at: {:?}", manager.path());
assert!(manager.path().exists());
}
Err(e) => {
eprintln!("ForceBindIP not found (expected in dev): {}", e);
}
}
}
#[cfg(target_os = "windows")]
#[test]
fn test_forcebindip_command_creation() {
use event_backtest_engine::ForceBindIpManager;
use std::path::Path;
if let Ok(manager) = ForceBindIpManager::new() {
let cmd = manager.create_bound_command(
"192.168.1.100",
Path::new("test.exe"),
&["--arg1", "value1"],
);
let cmd_str = format!("{:?}", cmd);
assert!(cmd_str.contains("192.168.1.100"));
assert!(cmd_str.contains("test.exe"));
println!("✓ ForceBindIP command created successfully");
}
}
#[test]
fn test_config_defaults() {
use event_backtest_engine::Config;
let config = Config::default();
assert_eq!(config.economic_start_date, "2007-02-13");
assert_eq!(config.corporate_start_date, "2010-01-01");
assert_eq!(config.economic_lookahead_months, 3);
assert_eq!(config.max_parallel_instances, 10);
assert!(!config.enable_vpn_rotation);
assert_eq!(config.tasks_per_vpn_session, 0);
}
}
#[cfg(test)]
mod benchmark_tests {
use super::*;
#[tokio::test]
#[ignore] // Performance test
async fn benchmark_vpn_rotation_overhead() {
use std::time::Instant;
// This test measures the overhead of VPN rotation
let start = Instant::now();
// Simulate rotation cycle
// 1. Disconnect (instant)
// 2. Wait 2 seconds
// 3. Connect (5-10 seconds)
// 4. Verify IP (1-2 seconds)
tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
let elapsed = start.elapsed();
println!("Rotation cycle took: {:?}", elapsed);
// Typical rotation should complete in under 15 seconds
assert!(elapsed.as_secs() < 15);
}
#[tokio::test]
#[ignore] // Performance test
async fn benchmark_parallel_scraping() {
// This test measures throughput with different parallelism levels
// Results help tune MAX_PARALLEL_INSTANCES
let configs = vec![1, 2, 3, 5, 10];
for &pool_size in &configs {
println!("Testing with {} parallel instances...", pool_size);
// Would need actual scraping implementation here
// For now, just verify pool creation time
let start = std::time::Instant::now();
let pool_result = event_backtest_engine::ChromeDriverPool::new(pool_size).await;
if let Ok(_pool) = pool_result {
let elapsed = start.elapsed();
println!(" Pool initialization: {:?}", elapsed);
// Pool creation should be fast (< 5 seconds per instance)
assert!(elapsed.as_secs() < pool_size as u64 * 5);
} else {
eprintln!(" Skipped - ChromeDriver not available");
}
}
}
}