//! Test Server Management //! //! Manages isolated test server instance for UX testing use actix_web::{web, App, HttpServer, middleware::Logger}; use std::sync::Arc; use tokio::sync::Mutex; use std::time::Duration; /// Test server instance pub struct TestServer { port: u16, server_handle: Option, } impl TestServer { /// Start a new test server instance pub async fn start(port: u16) -> Result> { // Set environment variables for test mode std::env::set_var("TEST_MODE", "true"); std::env::set_var("TEST_PORT", port.to_string()); std::env::set_var("TEST_DATA_DIR", "user_data_test"); // Import the main app configuration let config = threefold_marketplace::config::get_config(); log::info!("Starting test server on port {}", port); // Create test server with the same configuration as main app let server = HttpServer::new(move || { // Initialize Tera templates let mut tera = match tera::Tera::new(&format!("{}/**/*.html", config.templates.dir)) { Ok(t) => t, Err(e) => { eprintln!("Tera initialization error: {}", e); std::process::exit(1); } }; // Register custom Tera functions threefold_marketplace::utils::register_tera_functions(&mut tera); App::new() .wrap(Logger::default()) .wrap(threefold_marketplace::middleware::RequestTimer) .wrap(threefold_marketplace::middleware::SecurityHeaders) .service(actix_files::Files::new("/static", "./src/static")) .app_data(web::Data::new(tera)) .configure(threefold_marketplace::routes::configure_routes) }) .workers(1) // Single worker for testing .bind(format!("127.0.0.1:{}", port))?; let server_handle = server.handle(); // Start server in background tokio::spawn(async move { if let Err(e) = server.run().await { eprintln!("Test server error: {}", e); } }); // Wait for server to start tokio::time::sleep(Duration::from_millis(500)).await; // Verify server is running let client = reqwest::Client::new(); let health_check_url = format!("http://127.0.0.1:{}/", port); for attempt in 1..=10 { match client.get(&health_check_url).send().await { Ok(response) if response.status().is_success() => { log::info!("Test server started successfully on port {}", port); return Ok(Self { port, server_handle: Some(server_handle), }); } Ok(_) | Err(_) => { if attempt == 10 { return Err("Failed to start test server after 10 attempts".into()); } tokio::time::sleep(Duration::from_millis(500)).await; } } } Ok(Self { port, server_handle: Some(server_handle), }) } /// Get the server URL pub fn url(&self) -> String { format!("http://127.0.0.1:{}", self.port) } /// Stop the test server pub async fn stop(&mut self) -> Result<(), Box> { if let Some(handle) = self.server_handle.take() { handle.stop(true).await; log::info!("Test server stopped"); } Ok(()) } } impl Drop for TestServer { fn drop(&mut self) { if let Some(handle) = self.server_handle.take() { // Best effort cleanup let _ = handle.stop(false); } } }