init projectmycelium
This commit is contained in:
		
							
								
								
									
										249
									
								
								tests/tests_archive/ux_suite/environment/api_client.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								tests/tests_archive/ux_suite/environment/api_client.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,249 @@
 | 
			
		||||
//! API Test Client
 | 
			
		||||
//! 
 | 
			
		||||
//! Validates API responses alongside UX interactions
 | 
			
		||||
 | 
			
		||||
use reqwest;
 | 
			
		||||
use serde_json::Value;
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
 | 
			
		||||
/// API test client for validating backend responses
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
pub struct APITestClient {
 | 
			
		||||
    client: reqwest::Client,
 | 
			
		||||
    base_url: String,
 | 
			
		||||
    session_cookies: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl APITestClient {
 | 
			
		||||
    /// Create a new API test client
 | 
			
		||||
    pub fn new(test_port: u16) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            client: reqwest::Client::builder()
 | 
			
		||||
                .cookie_store(true)
 | 
			
		||||
                .build()
 | 
			
		||||
                .expect("Failed to create HTTP client"),
 | 
			
		||||
            base_url: format!("http://localhost:{}", test_port),
 | 
			
		||||
            session_cookies: None,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Make a GET request to an API endpoint
 | 
			
		||||
    pub async fn get(&self, path: &str) -> Result<ApiResponse, Box<dyn std::error::Error>> {
 | 
			
		||||
        let url = format!("{}{}", self.base_url, path);
 | 
			
		||||
        log::info!("API GET: {}", url);
 | 
			
		||||
        
 | 
			
		||||
        let mut request = self.client.get(&url);
 | 
			
		||||
        
 | 
			
		||||
        if let Some(cookies) = &self.session_cookies {
 | 
			
		||||
            request = request.header("Cookie", cookies);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        let response = request.send().await?;
 | 
			
		||||
        let status = response.status();
 | 
			
		||||
        let headers = response.headers().clone();
 | 
			
		||||
        let body: Value = response.json().await?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(ApiResponse {
 | 
			
		||||
            status: status.as_u16(),
 | 
			
		||||
            headers: headers.iter().map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string())).collect(),
 | 
			
		||||
            body,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Make a POST request to an API endpoint
 | 
			
		||||
    pub async fn post(&self, path: &str, data: Value) -> Result<ApiResponse, Box<dyn std::error::Error>> {
 | 
			
		||||
        let url = format!("{}{}", self.base_url, path);
 | 
			
		||||
        log::info!("API POST: {}", url);
 | 
			
		||||
        
 | 
			
		||||
        let mut request = self.client.post(&url).json(&data);
 | 
			
		||||
        
 | 
			
		||||
        if let Some(cookies) = &self.session_cookies {
 | 
			
		||||
            request = request.header("Cookie", cookies);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        let response = request.send().await?;
 | 
			
		||||
        let status = response.status();
 | 
			
		||||
        let headers = response.headers().clone();
 | 
			
		||||
        let body: Value = response.json().await?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(ApiResponse {
 | 
			
		||||
            status: status.as_u16(),
 | 
			
		||||
            headers: headers.iter().map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string())).collect(),
 | 
			
		||||
            body,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Make a PUT request to an API endpoint
 | 
			
		||||
    pub async fn put(&self, path: &str, data: Value) -> Result<ApiResponse, Box<dyn std::error::Error>> {
 | 
			
		||||
        let url = format!("{}{}", self.base_url, path);
 | 
			
		||||
        log::info!("API PUT: {}", url);
 | 
			
		||||
        
 | 
			
		||||
        let mut request = self.client.put(&url).json(&data);
 | 
			
		||||
        
 | 
			
		||||
        if let Some(cookies) = &self.session_cookies {
 | 
			
		||||
            request = request.header("Cookie", cookies);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        let response = request.send().await?;
 | 
			
		||||
        let status = response.status();
 | 
			
		||||
        let headers = response.headers().clone();
 | 
			
		||||
        let body: Value = response.json().await?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(ApiResponse {
 | 
			
		||||
            status: status.as_u16(),
 | 
			
		||||
            headers: headers.iter().map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string())).collect(),
 | 
			
		||||
            body,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Make a DELETE request to an API endpoint
 | 
			
		||||
    pub async fn delete(&self, path: &str) -> Result<ApiResponse, Box<dyn std::error::Error>> {
 | 
			
		||||
        let url = format!("{}{}", self.base_url, path);
 | 
			
		||||
        log::info!("API DELETE: {}", url);
 | 
			
		||||
        
 | 
			
		||||
        let mut request = self.client.delete(&url);
 | 
			
		||||
        
 | 
			
		||||
        if let Some(cookies) = &self.session_cookies {
 | 
			
		||||
            request = request.header("Cookie", cookies);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        let response = request.send().await?;
 | 
			
		||||
        let status = response.status();
 | 
			
		||||
        let headers = response.headers().clone();
 | 
			
		||||
        let body: Value = response.json().await?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(ApiResponse {
 | 
			
		||||
            status: status.as_u16(),
 | 
			
		||||
            headers: headers.iter().map(|(k, v)| (k.to_string(), v.to_str().unwrap_or("").to_string())).collect(),
 | 
			
		||||
            body,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Validate authentication status
 | 
			
		||||
    pub async fn validate_auth_status(&self) -> Result<AuthStatus, Box<dyn std::error::Error>> {
 | 
			
		||||
        let response = self.get("/api/auth/status").await?;
 | 
			
		||||
        
 | 
			
		||||
        // Validate ResponseBuilder envelope format
 | 
			
		||||
        self.assert_response_envelope(&response.body)?;
 | 
			
		||||
        
 | 
			
		||||
        let authenticated = response.body["data"]["authenticated"].as_bool().unwrap_or(false);
 | 
			
		||||
        let user_email = response.body["data"]["user"]["email"].as_str().map(|s| s.to_string());
 | 
			
		||||
        
 | 
			
		||||
        Ok(AuthStatus {
 | 
			
		||||
            authenticated,
 | 
			
		||||
            user_email,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Validate cart state
 | 
			
		||||
    pub async fn validate_cart_state(&self) -> Result<CartState, Box<dyn std::error::Error>> {
 | 
			
		||||
        let response = self.get("/api/cart").await?;
 | 
			
		||||
        
 | 
			
		||||
        self.assert_response_envelope(&response.body)?;
 | 
			
		||||
        
 | 
			
		||||
        let items = response.body["data"]["items"].as_array()
 | 
			
		||||
            .map(|arr| arr.len())
 | 
			
		||||
            .unwrap_or(0);
 | 
			
		||||
        
 | 
			
		||||
        let total = response.body["data"]["total"]["amount"].as_f64().unwrap_or(0.0);
 | 
			
		||||
        
 | 
			
		||||
        Ok(CartState {
 | 
			
		||||
            item_count: items,
 | 
			
		||||
            total_amount: total,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Validate wallet balance
 | 
			
		||||
    pub async fn get_wallet_balance(&self) -> Result<WalletBalance, Box<dyn std::error::Error>> {
 | 
			
		||||
        let response = self.get("/api/wallet/balance").await?;
 | 
			
		||||
        
 | 
			
		||||
        self.assert_response_envelope(&response.body)?;
 | 
			
		||||
        
 | 
			
		||||
        let balance = response.body["data"]["balance"]["amount"].as_f64().unwrap_or(0.0);
 | 
			
		||||
        let currency = response.body["data"]["balance"]["currency"].as_str().unwrap_or("TFC").to_string();
 | 
			
		||||
        
 | 
			
		||||
        Ok(WalletBalance {
 | 
			
		||||
            balance,
 | 
			
		||||
            currency,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get user dashboard data
 | 
			
		||||
    pub async fn get_dashboard_data(&self, dashboard_type: &str) -> Result<Value, Box<dyn std::error::Error>> {
 | 
			
		||||
        let path = format!("/api/dashboard/{}-data", dashboard_type);
 | 
			
		||||
        let response = self.get(&path).await?;
 | 
			
		||||
        
 | 
			
		||||
        self.assert_response_envelope(&response.body)?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(response.body["data"].clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Validate products API
 | 
			
		||||
    pub async fn get_products(&self, category: Option<&str>) -> Result<Vec<Value>, Box<dyn std::error::Error>> {
 | 
			
		||||
        let path = if let Some(cat) = category {
 | 
			
		||||
            format!("/api/products?category={}", cat)
 | 
			
		||||
        } else {
 | 
			
		||||
            "/api/products".to_string()
 | 
			
		||||
        };
 | 
			
		||||
        
 | 
			
		||||
        let response = self.get(&path).await?;
 | 
			
		||||
        self.assert_response_envelope(&response.body)?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(response.body["data"]["products"].as_array().unwrap_or(&vec![]).clone())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Assert ResponseBuilder envelope format
 | 
			
		||||
    fn assert_response_envelope(&self, response: &Value) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        if !response.get("success").is_some() {
 | 
			
		||||
            return Err("Missing 'success' field in response envelope".into());
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        let success = response["success"].as_bool().unwrap_or(false);
 | 
			
		||||
        
 | 
			
		||||
        if success {
 | 
			
		||||
            if !response.get("data").is_some() {
 | 
			
		||||
                return Err("Missing 'data' field in successful response".into());
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            if !response.get("error").is_some() {
 | 
			
		||||
                return Err("Missing 'error' field in failed response".into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Set session cookies for authenticated requests
 | 
			
		||||
    pub fn set_session_cookies(&mut self, cookies: String) {
 | 
			
		||||
        self.session_cookies = Some(cookies);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// API response structure
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct ApiResponse {
 | 
			
		||||
    pub status: u16,
 | 
			
		||||
    pub headers: HashMap<String, String>,
 | 
			
		||||
    pub body: Value,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Authentication status
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct AuthStatus {
 | 
			
		||||
    pub authenticated: bool,
 | 
			
		||||
    pub user_email: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Cart state
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct CartState {
 | 
			
		||||
    pub item_count: usize,
 | 
			
		||||
    pub total_amount: f64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Wallet balance
 | 
			
		||||
#[derive(Debug)]
 | 
			
		||||
pub struct WalletBalance {
 | 
			
		||||
    pub balance: f64,
 | 
			
		||||
    pub currency: String,
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										230
									
								
								tests/tests_archive/ux_suite/environment/browser_manager.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										230
									
								
								tests/tests_archive/ux_suite/environment/browser_manager.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,230 @@
 | 
			
		||||
//! Browser Automation Manager
 | 
			
		||||
//! 
 | 
			
		||||
//! Handles browser lifecycle, navigation, and interaction for UX testing
 | 
			
		||||
 | 
			
		||||
use thirtyfour::prelude::*;
 | 
			
		||||
use std::path::PathBuf;
 | 
			
		||||
use std::time::Duration;
 | 
			
		||||
use tokio::time::timeout;
 | 
			
		||||
 | 
			
		||||
/// Supported browser types for testing
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub enum BrowserType {
 | 
			
		||||
    Chrome,
 | 
			
		||||
    Firefox,
 | 
			
		||||
    Safari,
 | 
			
		||||
    Edge,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Browser manager for UX testing
 | 
			
		||||
pub struct BrowserManager {
 | 
			
		||||
    driver: WebDriver,
 | 
			
		||||
    base_url: String,
 | 
			
		||||
    screenshot_dir: PathBuf,
 | 
			
		||||
    config: super::UXTestConfig,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Clone for BrowserManager {
 | 
			
		||||
    fn clone(&self) -> Self {
 | 
			
		||||
        // Note: WebDriver cannot be cloned, so this creates a reference to the same driver
 | 
			
		||||
        // In practice, we should avoid cloning BrowserManager and use references instead
 | 
			
		||||
        panic!("BrowserManager cannot be cloned due to WebDriver limitations. Use references instead.");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BrowserManager {
 | 
			
		||||
    /// Create a new browser manager
 | 
			
		||||
    pub async fn new(config: &super::UXTestConfig) -> Result<Self, Box<dyn std::error::Error>> {
 | 
			
		||||
        let capabilities = match config.browser_type {
 | 
			
		||||
            BrowserType::Chrome => {
 | 
			
		||||
                let mut caps = DesiredCapabilities::chrome();
 | 
			
		||||
                if config.headless {
 | 
			
		||||
                    caps.add_chrome_arg("--headless")?;
 | 
			
		||||
                }
 | 
			
		||||
                caps.add_chrome_arg("--no-sandbox")?;
 | 
			
		||||
                caps.add_chrome_arg("--disable-dev-shm-usage")?;
 | 
			
		||||
                caps.add_chrome_arg("--disable-gpu")?;
 | 
			
		||||
                caps.add_chrome_arg("--window-size=1920,1080")?;
 | 
			
		||||
                caps
 | 
			
		||||
            }
 | 
			
		||||
            BrowserType::Firefox => {
 | 
			
		||||
                let mut caps = DesiredCapabilities::firefox();
 | 
			
		||||
                if config.headless {
 | 
			
		||||
                    caps.add_firefox_arg("--headless")?;
 | 
			
		||||
                }
 | 
			
		||||
                caps
 | 
			
		||||
            }
 | 
			
		||||
            _ => {
 | 
			
		||||
                return Err("Browser type not yet implemented".into());
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Try to connect to existing WebDriver or start one
 | 
			
		||||
        let driver = match WebDriver::new("http://localhost:4444", capabilities.clone()).await {
 | 
			
		||||
            Ok(driver) => driver,
 | 
			
		||||
            Err(_) => {
 | 
			
		||||
                // If selenium server is not running, try local driver
 | 
			
		||||
                WebDriver::new("http://localhost:9515", capabilities).await?
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        // Configure browser
 | 
			
		||||
        driver.set_window_size(1920, 1080).await?;
 | 
			
		||||
        driver.implicitly_wait(Duration::from_secs(10)).await?;
 | 
			
		||||
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            driver,
 | 
			
		||||
            base_url: format!("http://localhost:{}", config.test_port),
 | 
			
		||||
            screenshot_dir: config.screenshot_dir.clone(),
 | 
			
		||||
            config: config.clone(),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Navigate to a path on the test server
 | 
			
		||||
    pub async fn navigate_to(&self, path: &str) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        let url = if path.starts_with("http") {
 | 
			
		||||
            path.to_string()
 | 
			
		||||
        } else {
 | 
			
		||||
            format!("{}{}", self.base_url, path)
 | 
			
		||||
        };
 | 
			
		||||
        
 | 
			
		||||
        log::info!("Navigating to: {}", url);
 | 
			
		||||
        
 | 
			
		||||
        timeout(
 | 
			
		||||
            Duration::from_secs(self.config.timeout_seconds),
 | 
			
		||||
            self.driver.goto(&url)
 | 
			
		||||
        ).await??;
 | 
			
		||||
        
 | 
			
		||||
        // Wait for page to load
 | 
			
		||||
        self.wait_for_page_load().await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Wait for page to be fully loaded
 | 
			
		||||
    pub async fn wait_for_page_load(&self) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        // Wait for document ready state
 | 
			
		||||
        self.driver.execute("return document.readyState", vec![]).await?;
 | 
			
		||||
        
 | 
			
		||||
        // Additional wait for any dynamic content
 | 
			
		||||
        tokio::time::sleep(Duration::from_millis(500)).await;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Take a screenshot with the given name
 | 
			
		||||
    pub async fn take_screenshot(&self, name: &str) -> Result<PathBuf, Box<dyn std::error::Error>> {
 | 
			
		||||
        let screenshot = self.driver.screenshot_as_png().await?;
 | 
			
		||||
        let timestamp = chrono::Utc::now().format("%Y%m%d_%H%M%S");
 | 
			
		||||
        let filename = format!("{}_{}.png", name, timestamp);
 | 
			
		||||
        let path = self.screenshot_dir.join(filename);
 | 
			
		||||
        
 | 
			
		||||
        std::fs::create_dir_all(&self.screenshot_dir)?;
 | 
			
		||||
        std::fs::write(&path, screenshot)?;
 | 
			
		||||
        
 | 
			
		||||
        log::info!("Screenshot saved: {:?}", path);
 | 
			
		||||
        Ok(path)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Find element by CSS selector
 | 
			
		||||
    pub async fn find_element(&self, selector: &str) -> Result<WebElement, Box<dyn std::error::Error>> {
 | 
			
		||||
        Ok(self.driver.find(By::Css(selector)).await?)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Find elements by CSS selector
 | 
			
		||||
    pub async fn find_elements(&self, selector: &str) -> Result<Vec<WebElement>, Box<dyn std::error::Error>> {
 | 
			
		||||
        Ok(self.driver.find_all(By::Css(selector)).await?)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Click element by CSS selector
 | 
			
		||||
    pub async fn click(&self, selector: &str) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        let element = self.find_element(selector).await?;
 | 
			
		||||
        element.scroll_into_view().await?;
 | 
			
		||||
        element.click().await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Type text into element
 | 
			
		||||
    pub async fn type_text(&self, selector: &str, text: &str) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        let element = self.find_element(selector).await?;
 | 
			
		||||
        element.clear().await?;
 | 
			
		||||
        element.send_keys(text).await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get text from element
 | 
			
		||||
    pub async fn get_text(&self, selector: &str) -> Result<String, Box<dyn std::error::Error>> {
 | 
			
		||||
        let element = self.find_element(selector).await?;
 | 
			
		||||
        Ok(element.text().await?)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Check if element exists
 | 
			
		||||
    pub async fn element_exists(&self, selector: &str) -> bool {
 | 
			
		||||
        self.driver.find(By::Css(selector)).await.is_ok()
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Wait for element to be visible
 | 
			
		||||
    pub async fn wait_for_element(&self, selector: &str) -> Result<WebElement, Box<dyn std::error::Error>> {
 | 
			
		||||
        let timeout_duration = Duration::from_secs(self.config.timeout_seconds);
 | 
			
		||||
        
 | 
			
		||||
        timeout(timeout_duration, async {
 | 
			
		||||
            loop {
 | 
			
		||||
                if let Ok(element) = self.driver.find(By::Css(selector)).await {
 | 
			
		||||
                    if element.is_displayed().await.unwrap_or(false) {
 | 
			
		||||
                        return Ok(element);
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                tokio::time::sleep(Duration::from_millis(100)).await;
 | 
			
		||||
            }
 | 
			
		||||
        }).await?
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get current page title
 | 
			
		||||
    pub async fn get_title(&self) -> Result<String, Box<dyn std::error::Error>> {
 | 
			
		||||
        Ok(self.driver.title().await?)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get current URL
 | 
			
		||||
    pub async fn get_current_url(&self) -> Result<String, Box<dyn std::error::Error>> {
 | 
			
		||||
        Ok(self.driver.current_url().await?)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Execute JavaScript
 | 
			
		||||
    pub async fn execute_script(&self, script: &str) -> Result<serde_json::Value, Box<dyn std::error::Error>> {
 | 
			
		||||
        Ok(self.driver.execute(script, vec![]).await?)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Quit the browser
 | 
			
		||||
    pub async fn quit(&mut self) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        self.driver.quit().await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get page source for debugging
 | 
			
		||||
    pub async fn get_page_source(&self) -> Result<String, Box<dyn std::error::Error>> {
 | 
			
		||||
        Ok(self.driver.source().await?)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Scroll to element
 | 
			
		||||
    pub async fn scroll_to_element(&self, selector: &str) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        let element = self.find_element(selector).await?;
 | 
			
		||||
        element.scroll_into_view().await?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Wait for text to appear in element
 | 
			
		||||
    pub async fn wait_for_text(&self, selector: &str, expected_text: &str) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        let timeout_duration = Duration::from_secs(self.config.timeout_seconds);
 | 
			
		||||
        
 | 
			
		||||
        timeout(timeout_duration, async {
 | 
			
		||||
            loop {
 | 
			
		||||
                if let Ok(element) = self.driver.find(By::Css(selector)).await {
 | 
			
		||||
                    if let Ok(text) = element.text().await {
 | 
			
		||||
                        if text.contains(expected_text) {
 | 
			
		||||
                            return Ok(());
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                tokio::time::sleep(Duration::from_millis(100)).await;
 | 
			
		||||
            }
 | 
			
		||||
        }).await?
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										115
									
								
								tests/tests_archive/ux_suite/environment/mod.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										115
									
								
								tests/tests_archive/ux_suite/environment/mod.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,115 @@
 | 
			
		||||
//! Test Environment Management
 | 
			
		||||
//! 
 | 
			
		||||
//! Handles isolated test environment setup including:
 | 
			
		||||
//! - Test server instance
 | 
			
		||||
//! - Browser automation
 | 
			
		||||
//! - Test data management
 | 
			
		||||
//! - API client for validation
 | 
			
		||||
 | 
			
		||||
pub mod browser_manager;
 | 
			
		||||
pub mod test_server;
 | 
			
		||||
pub mod test_data_manager;
 | 
			
		||||
pub mod api_client;
 | 
			
		||||
 | 
			
		||||
pub use browser_manager::*;
 | 
			
		||||
pub use test_server::*;
 | 
			
		||||
pub use test_data_manager::*;
 | 
			
		||||
pub use api_client::*;
 | 
			
		||||
 | 
			
		||||
use std::path::PathBuf;
 | 
			
		||||
use tokio::time::Duration;
 | 
			
		||||
 | 
			
		||||
/// Configuration for UX test environment
 | 
			
		||||
#[derive(Debug, Clone)]
 | 
			
		||||
pub struct UXTestConfig {
 | 
			
		||||
    pub test_port: u16,
 | 
			
		||||
    pub headless: bool,
 | 
			
		||||
    pub timeout_seconds: u64,
 | 
			
		||||
    pub screenshot_on_failure: bool,
 | 
			
		||||
    pub browser_type: BrowserType,
 | 
			
		||||
    pub test_data_dir: PathBuf,
 | 
			
		||||
    pub screenshot_dir: PathBuf,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for UXTestConfig {
 | 
			
		||||
    fn default() -> Self {
 | 
			
		||||
        let test_mode = std::env::var("UX_TEST_MODE").unwrap_or_else(|_| "dev".to_string());
 | 
			
		||||
        
 | 
			
		||||
        match test_mode.as_str() {
 | 
			
		||||
            "ci" => Self {
 | 
			
		||||
                test_port: 8081,
 | 
			
		||||
                headless: true,
 | 
			
		||||
                timeout_seconds: 30,
 | 
			
		||||
                screenshot_on_failure: true,
 | 
			
		||||
                browser_type: BrowserType::Chrome,
 | 
			
		||||
                test_data_dir: PathBuf::from("user_data_test"),
 | 
			
		||||
                screenshot_dir: PathBuf::from("tests/ux_suite/reports/screenshots"),
 | 
			
		||||
            },
 | 
			
		||||
            "dev" => Self {
 | 
			
		||||
                test_port: 8081,
 | 
			
		||||
                headless: false,
 | 
			
		||||
                timeout_seconds: 60,
 | 
			
		||||
                screenshot_on_failure: true,
 | 
			
		||||
                browser_type: BrowserType::Chrome,
 | 
			
		||||
                test_data_dir: PathBuf::from("user_data_test"),
 | 
			
		||||
                screenshot_dir: PathBuf::from("tests/ux_suite/reports/screenshots"),
 | 
			
		||||
            },
 | 
			
		||||
            _ => Self {
 | 
			
		||||
                test_port: 8081,
 | 
			
		||||
                headless: false,
 | 
			
		||||
                timeout_seconds: 60,
 | 
			
		||||
                screenshot_on_failure: true,
 | 
			
		||||
                browser_type: BrowserType::Chrome,
 | 
			
		||||
                test_data_dir: PathBuf::from("user_data_test"),
 | 
			
		||||
                screenshot_dir: PathBuf::from("tests/ux_suite/reports/screenshots"),
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Complete UX test environment
 | 
			
		||||
pub struct UXTestEnvironment {
 | 
			
		||||
    pub config: UXTestConfig,
 | 
			
		||||
    pub browser: BrowserManager,
 | 
			
		||||
    pub server: TestServer,
 | 
			
		||||
    pub data_manager: TestDataManager,
 | 
			
		||||
    pub api_client: APITestClient,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl UXTestEnvironment {
 | 
			
		||||
    /// Initialize a new test environment
 | 
			
		||||
    pub async fn new() -> Result<Self, Box<dyn std::error::Error>> {
 | 
			
		||||
        let config = UXTestConfig::default();
 | 
			
		||||
        
 | 
			
		||||
        // Create directories
 | 
			
		||||
        std::fs::create_dir_all(&config.test_data_dir)?;
 | 
			
		||||
        std::fs::create_dir_all(&config.screenshot_dir)?;
 | 
			
		||||
        
 | 
			
		||||
        // Initialize components
 | 
			
		||||
        let data_manager = TestDataManager::new(&config.test_data_dir)?;
 | 
			
		||||
        let server = TestServer::start(config.test_port).await?;
 | 
			
		||||
        let browser = BrowserManager::new(&config).await?;
 | 
			
		||||
        let api_client = APITestClient::new(config.test_port);
 | 
			
		||||
        
 | 
			
		||||
        Ok(Self {
 | 
			
		||||
            config,
 | 
			
		||||
            browser,
 | 
			
		||||
            server,
 | 
			
		||||
            data_manager,
 | 
			
		||||
            api_client,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Get a UX test helper for this environment
 | 
			
		||||
    pub fn ux_helper(&self) -> UXTestHelper {
 | 
			
		||||
        UXTestHelper::new(self)
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Clean up test environment
 | 
			
		||||
    pub async fn cleanup(&mut self) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        self.browser.quit().await?;
 | 
			
		||||
        self.server.stop().await?;
 | 
			
		||||
        self.data_manager.cleanup()?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										314
									
								
								tests/tests_archive/ux_suite/environment/test_data_manager.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										314
									
								
								tests/tests_archive/ux_suite/environment/test_data_manager.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,314 @@
 | 
			
		||||
//! Test Data Management
 | 
			
		||||
//! 
 | 
			
		||||
//! Manages test fixtures, user personas, and data isolation for UX testing
 | 
			
		||||
 | 
			
		||||
use serde::{Serialize, Deserialize};
 | 
			
		||||
use std::path::PathBuf;
 | 
			
		||||
use std::collections::HashMap;
 | 
			
		||||
 | 
			
		||||
/// Test user persona for different user types
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct TestPersona {
 | 
			
		||||
    pub email: String,
 | 
			
		||||
    pub password: String,
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    pub role: UserRole,
 | 
			
		||||
    pub profile: UserProfile,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub enum UserRole {
 | 
			
		||||
    Consumer,
 | 
			
		||||
    Farmer,
 | 
			
		||||
    AppProvider,
 | 
			
		||||
    ServiceProvider,
 | 
			
		||||
    Admin,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct UserProfile {
 | 
			
		||||
    pub country: String,
 | 
			
		||||
    pub timezone: String,
 | 
			
		||||
    pub currency_preference: String,
 | 
			
		||||
    pub wallet_balance: f64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Test marketplace data
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct TestMarketplaceData {
 | 
			
		||||
    pub products: Vec<TestProduct>,
 | 
			
		||||
    pub services: Vec<TestService>,
 | 
			
		||||
    pub nodes: Vec<TestNode>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct TestProduct {
 | 
			
		||||
    pub id: String,
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    pub category: String,
 | 
			
		||||
    pub price: f64,
 | 
			
		||||
    pub currency: String,
 | 
			
		||||
    pub description: String,
 | 
			
		||||
    pub provider_email: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct TestService {
 | 
			
		||||
    pub id: String,
 | 
			
		||||
    pub name: String,
 | 
			
		||||
    pub description: String,
 | 
			
		||||
    pub price: f64,
 | 
			
		||||
    pub provider_email: String,
 | 
			
		||||
    pub status: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct TestNode {
 | 
			
		||||
    pub id: String,
 | 
			
		||||
    pub farmer_email: String,
 | 
			
		||||
    pub location: String,
 | 
			
		||||
    pub specs: NodeSpecs,
 | 
			
		||||
    pub available: bool,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Serialize, Deserialize)]
 | 
			
		||||
pub struct NodeSpecs {
 | 
			
		||||
    pub cpu_cores: u32,
 | 
			
		||||
    pub ram_gb: u32,
 | 
			
		||||
    pub storage_gb: u32,
 | 
			
		||||
    pub price_per_hour: f64,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Test data manager for UX testing
 | 
			
		||||
#[derive(Clone)]
 | 
			
		||||
pub struct TestDataManager {
 | 
			
		||||
    test_data_dir: PathBuf,
 | 
			
		||||
    personas: HashMap<UserRole, TestPersona>,
 | 
			
		||||
    marketplace_data: TestMarketplaceData,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl TestDataManager {
 | 
			
		||||
    /// Create a new test data manager
 | 
			
		||||
    pub fn new(test_data_dir: &PathBuf) -> Result<Self, Box<dyn std::error::Error>> {
 | 
			
		||||
        let personas = Self::create_test_personas();
 | 
			
		||||
        let marketplace_data = Self::create_test_marketplace_data(&personas);
 | 
			
		||||
        
 | 
			
		||||
        let manager = Self {
 | 
			
		||||
            test_data_dir: test_data_dir.clone(),
 | 
			
		||||
            personas,
 | 
			
		||||
            marketplace_data,
 | 
			
		||||
        };
 | 
			
		||||
        
 | 
			
		||||
        manager.setup_test_data()?;
 | 
			
		||||
        Ok(manager)
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Create test user personas
 | 
			
		||||
    fn create_test_personas() -> HashMap<UserRole, TestPersona> {
 | 
			
		||||
        let mut personas = HashMap::new();
 | 
			
		||||
        
 | 
			
		||||
        personas.insert(UserRole::Consumer, TestPersona {
 | 
			
		||||
            email: "user1@example.com".to_string(),
 | 
			
		||||
            password: "testpass123".to_string(),
 | 
			
		||||
            name: "Test Consumer".to_string(),
 | 
			
		||||
            role: UserRole::Consumer,
 | 
			
		||||
            profile: UserProfile {
 | 
			
		||||
                country: "United States".to_string(),
 | 
			
		||||
                timezone: "America/New_York".to_string(),
 | 
			
		||||
                currency_preference: "USD".to_string(),
 | 
			
		||||
                wallet_balance: 100.0,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
        
 | 
			
		||||
        personas.insert(UserRole::Farmer, TestPersona {
 | 
			
		||||
            email: "farmer1@example.com".to_string(),
 | 
			
		||||
            password: "testpass123".to_string(),
 | 
			
		||||
            name: "Test Farmer".to_string(),
 | 
			
		||||
            role: UserRole::Farmer,
 | 
			
		||||
            profile: UserProfile {
 | 
			
		||||
                country: "Canada".to_string(),
 | 
			
		||||
                timezone: "America/Toronto".to_string(),
 | 
			
		||||
                currency_preference: "CAD".to_string(),
 | 
			
		||||
                wallet_balance: 500.0,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
        
 | 
			
		||||
        personas.insert(UserRole::AppProvider, TestPersona {
 | 
			
		||||
            email: "appdev1@example.com".to_string(),
 | 
			
		||||
            password: "testpass123".to_string(),
 | 
			
		||||
            name: "Test App Developer".to_string(),
 | 
			
		||||
            role: UserRole::AppProvider,
 | 
			
		||||
            profile: UserProfile {
 | 
			
		||||
                country: "Germany".to_string(),
 | 
			
		||||
                timezone: "Europe/Berlin".to_string(),
 | 
			
		||||
                currency_preference: "EUR".to_string(),
 | 
			
		||||
                wallet_balance: 200.0,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
        
 | 
			
		||||
        personas.insert(UserRole::ServiceProvider, TestPersona {
 | 
			
		||||
            email: "service1@example.com".to_string(),
 | 
			
		||||
            password: "testpass123".to_string(),
 | 
			
		||||
            name: "Test Service Provider".to_string(),
 | 
			
		||||
            role: UserRole::ServiceProvider,
 | 
			
		||||
            profile: UserProfile {
 | 
			
		||||
                country: "United Kingdom".to_string(),
 | 
			
		||||
                timezone: "Europe/London".to_string(),
 | 
			
		||||
                currency_preference: "TFC".to_string(),
 | 
			
		||||
                wallet_balance: 300.0,
 | 
			
		||||
            },
 | 
			
		||||
        });
 | 
			
		||||
        
 | 
			
		||||
        personas
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Create test marketplace data
 | 
			
		||||
    fn create_test_marketplace_data(personas: &HashMap<UserRole, TestPersona>) -> TestMarketplaceData {
 | 
			
		||||
        let farmer_email = personas.get(&UserRole::Farmer).unwrap().email.clone();
 | 
			
		||||
        let app_provider_email = personas.get(&UserRole::AppProvider).unwrap().email.clone();
 | 
			
		||||
        let service_provider_email = personas.get(&UserRole::ServiceProvider).unwrap().email.clone();
 | 
			
		||||
        
 | 
			
		||||
        TestMarketplaceData {
 | 
			
		||||
            products: vec![
 | 
			
		||||
                TestProduct {
 | 
			
		||||
                    id: "test-vm-1".to_string(),
 | 
			
		||||
                    name: "Test VM Small".to_string(),
 | 
			
		||||
                    category: "compute".to_string(),
 | 
			
		||||
                    price: 10.0,
 | 
			
		||||
                    currency: "TFC".to_string(),
 | 
			
		||||
                    description: "Small virtual machine for testing".to_string(),
 | 
			
		||||
                    provider_email: farmer_email.clone(),
 | 
			
		||||
                },
 | 
			
		||||
                TestProduct {
 | 
			
		||||
                    id: "test-app-1".to_string(),
 | 
			
		||||
                    name: "Test Application".to_string(),
 | 
			
		||||
                    category: "applications".to_string(),
 | 
			
		||||
                    price: 25.0,
 | 
			
		||||
                    currency: "TFC".to_string(),
 | 
			
		||||
                    description: "Test application for UX testing".to_string(),
 | 
			
		||||
                    provider_email: app_provider_email.clone(),
 | 
			
		||||
                },
 | 
			
		||||
            ],
 | 
			
		||||
            services: vec![
 | 
			
		||||
                TestService {
 | 
			
		||||
                    id: "test-service-1".to_string(),
 | 
			
		||||
                    name: "Test Consulting Service".to_string(),
 | 
			
		||||
                    description: "Professional consulting service for testing".to_string(),
 | 
			
		||||
                    price: 100.0,
 | 
			
		||||
                    provider_email: service_provider_email.clone(),
 | 
			
		||||
                    status: "available".to_string(),
 | 
			
		||||
                },
 | 
			
		||||
            ],
 | 
			
		||||
            nodes: vec![
 | 
			
		||||
                TestNode {
 | 
			
		||||
                    id: "test-node-1".to_string(),
 | 
			
		||||
                    farmer_email: farmer_email.clone(),
 | 
			
		||||
                    location: "New York, USA".to_string(),
 | 
			
		||||
                    specs: NodeSpecs {
 | 
			
		||||
                        cpu_cores: 8,
 | 
			
		||||
                        ram_gb: 16,
 | 
			
		||||
                        storage_gb: 500,
 | 
			
		||||
                        price_per_hour: 2.0,
 | 
			
		||||
                    },
 | 
			
		||||
                    available: true,
 | 
			
		||||
                },
 | 
			
		||||
            ],
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Setup test data files
 | 
			
		||||
    fn setup_test_data(&self) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        // Create test data directory
 | 
			
		||||
        std::fs::create_dir_all(&self.test_data_dir)?;
 | 
			
		||||
        
 | 
			
		||||
        // Create user data files for each persona
 | 
			
		||||
        for persona in self.personas.values() {
 | 
			
		||||
            self.create_user_data_file(persona)?;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Save marketplace data
 | 
			
		||||
        let marketplace_file = self.test_data_dir.join("marketplace_data.json");
 | 
			
		||||
        let marketplace_json = serde_json::to_string_pretty(&self.marketplace_data)?;
 | 
			
		||||
        std::fs::write(marketplace_file, marketplace_json)?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Create user data file for a persona
 | 
			
		||||
    fn create_user_data_file(&self, persona: &TestPersona) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        let encoded_email = persona.email.replace("@", "_at_").replace(".", "_dot_");
 | 
			
		||||
        let user_file = self.test_data_dir.join(format!("{}.json", encoded_email));
 | 
			
		||||
        
 | 
			
		||||
        let user_data = serde_json::json!({
 | 
			
		||||
            "email": persona.email,
 | 
			
		||||
            "name": persona.name,
 | 
			
		||||
            "profile": persona.profile,
 | 
			
		||||
            "role": persona.role,
 | 
			
		||||
            "wallet": {
 | 
			
		||||
                "balance": persona.profile.wallet_balance,
 | 
			
		||||
                "currency": persona.profile.currency_preference,
 | 
			
		||||
                "transactions": []
 | 
			
		||||
            },
 | 
			
		||||
            "cart": {
 | 
			
		||||
                "items": [],
 | 
			
		||||
                "total": 0.0
 | 
			
		||||
            },
 | 
			
		||||
            "orders": [],
 | 
			
		||||
            "settings": {
 | 
			
		||||
                "notifications": {
 | 
			
		||||
                    "security_alerts": true,
 | 
			
		||||
                    "billing_notifications": true,
 | 
			
		||||
                    "system_alerts": true,
 | 
			
		||||
                    "newsletter": false,
 | 
			
		||||
                    "dashboard_notifications": true
 | 
			
		||||
                },
 | 
			
		||||
                "ssh_keys": []
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        
 | 
			
		||||
        let user_json = serde_json::to_string_pretty(&user_data)?;
 | 
			
		||||
        std::fs::write(user_file, user_json)?;
 | 
			
		||||
        
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Get test persona by role
 | 
			
		||||
    pub fn get_persona(&self, role: &UserRole) -> Option<&TestPersona> {
 | 
			
		||||
        self.personas.get(role)
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Get all test personas
 | 
			
		||||
    pub fn get_all_personas(&self) -> &HashMap<UserRole, TestPersona> {
 | 
			
		||||
        &self.personas
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Get test marketplace data
 | 
			
		||||
    pub fn get_marketplace_data(&self) -> &TestMarketplaceData {
 | 
			
		||||
        &self.marketplace_data
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Reset test data to clean state
 | 
			
		||||
    pub fn reset_test_data(&self) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        // Remove all user data files
 | 
			
		||||
        if self.test_data_dir.exists() {
 | 
			
		||||
            std::fs::remove_dir_all(&self.test_data_dir)?;
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        // Recreate test data
 | 
			
		||||
        self.setup_test_data()?;
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Cleanup test data
 | 
			
		||||
    pub fn cleanup(&self) -> Result<(), Box<dyn std::error::Error>> {
 | 
			
		||||
        if self.test_data_dir.exists() {
 | 
			
		||||
            std::fs::remove_dir_all(&self.test_data_dir)?;
 | 
			
		||||
        }
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Get test user credentials
 | 
			
		||||
    pub fn get_test_credentials(&self, role: &UserRole) -> Option<(String, String)> {
 | 
			
		||||
        self.personas.get(role).map(|p| (p.email.clone(), p.password.clone()))
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										116
									
								
								tests/tests_archive/ux_suite/environment/test_server.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								tests/tests_archive/ux_suite/environment/test_server.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,116 @@
 | 
			
		||||
//! 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<actix_web::dev::ServerHandle>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl TestServer {
 | 
			
		||||
    /// Start a new test server instance
 | 
			
		||||
    pub async fn start(port: u16) -> Result<Self, Box<dyn std::error::Error>> {
 | 
			
		||||
        // 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<dyn std::error::Error>> {
 | 
			
		||||
        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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user