// Integration tests for LLM Proxy Gateway use llm_proxy::config::Config; use llm_proxy::database::Database; use llm_proxy::state::AppState; use llm_proxy::rate_limiting::RateLimitManager; use tempfile::TempDir; use std::fs; #[tokio::test] async fn test_config_loading() { // Create a temporary config file let temp_dir = TempDir::new().unwrap(); let config_path = temp_dir.path().join("config.toml"); let config_content = r#" [server] port = 8080 host = "0.0.0.0" [database] path = "./data/test.db" max_connections = 5 [providers.openai] enabled = true base_url = "https://api.openai.com/v1" [providers.gemini] enabled = true base_url = "https://generativelanguage.googleapis.com/v1" [providers.deepseek] enabled = true base_url = "https://api.deepseek.com" [providers.grok] enabled = false base_url = "https://api.x.ai/v1" [model_mapping] "gpt-*" = "openai" "gemini-*" = "gemini" "deepseek-*" = "deepseek" "grok-*" = "grok" [pricing] openai = { input = 0.01, output = 0.03 } gemini = { input = 0.0005, output = 0.0015 } deepseek = { input = 0.00014, output = 0.00028 } grok = { input = 0.001, output = 0.003 } "#; fs::write(&config_path, config_content).unwrap(); // Test loading config let config = Config::load_from_path(&config_path); assert!(config.is_ok()); let config = config.unwrap(); assert_eq!(config.server.port, 8080); assert!(config.providers.openai.is_some()); assert!(config.providers.grok.is_none()); } #[tokio::test] async fn test_database_initialization() { // Create a temporary database file let temp_dir = TempDir::new().unwrap(); let db_path = temp_dir.path().join("test.db"); // Test database initialization let database = Database::new(&db_path).await; assert!(database.is_ok()); let database = database.unwrap(); // Test connection let test_result = database.test_connection().await; assert!(test_result.is_ok()); } #[tokio::test] async fn test_provider_manager() { // Create a provider manager use llm_proxy::providers::{ProviderManager, Provider}; use llm_proxy::config::OpenAIConfig; let mut manager = ProviderManager::new(); assert_eq!(manager.providers.len(), 0); // Test adding providers (we can't actually add real providers without API keys) // This test just verifies the manager structure works assert!(manager.get_provider_for_model("gpt-4").is_none()); assert!(manager.get_provider("openai").is_none()); } #[tokio::test] async fn test_rate_limit_manager() { let manager = RateLimitManager::new(60, 10); // Test client rate limiting let allowed = manager.check_request("test-client").await; assert!(allowed); // First request should be allowed // Test provider circuit breaker let allowed = manager.check_provider("openai").await; assert!(allowed); // Circuit should be closed initially // Record some failures manager.record_provider_failure("openai").await; manager.record_provider_failure("openai").await; manager.record_provider_failure("openai").await; manager.record_provider_failure("openai").await; manager.record_provider_failure("openai").await; // After 5 failures, circuit should be open let allowed = manager.check_provider("openai").await; assert!(!allowed); // Circuit should be open // Record success to close circuit manager.record_provider_success("openai").await; manager.record_provider_success("openai").await; manager.record_provider_success("openai").await; // After 3 successes in half-open state, circuit should be closed let allowed = manager.check_provider("openai").await; assert!(allowed); // Circuit should be closed again } #[tokio::test] async fn test_app_state_creation() { // Create a temporary database let temp_dir = TempDir::new().unwrap(); let db_path = temp_dir.path().join("test.db"); let database = Database::new(&db_path).await.unwrap(); // Test AppState creation using test utilities use llm_proxy::test_utils::create_test_state; let state = create_test_state().await; // Verify state components are initialized assert!(state.database.test_connection().await.is_ok()); } #[tokio::test] async fn test_multimodal_image_converter() { use llm_proxy::multimodal::{ImageConverter, ImageInput}; // Test model detection assert!(ImageConverter::model_supports_multimodal("gpt-4-vision-preview")); assert!(ImageConverter::model_supports_multimodal("gemini-pro-vision")); assert!(!ImageConverter::model_supports_multimodal("gpt-3.5-turbo")); assert!(!ImageConverter::model_supports_multimodal("gemini-pro")); // Test data URL parsing (utility function) let test_url = "data:image/jpeg;base64,SGVsbG8gV29ybGQ="; let parts: Vec<&str> = test_url[5..].split(";base64,").collect(); assert_eq!(parts.len(), 2); assert_eq!(parts[0], "image/jpeg"); assert_eq!(parts[1], "SGVsbG8gV29ybGQ="); } #[tokio::test] async fn test_error_conversions() { use llm_proxy::errors::AppError; use anyhow::anyhow; // Test anyhow error conversion let anyhow_error = anyhow!("Test error"); let app_error: AppError = anyhow_error.into(); match app_error { AppError::InternalError(msg) => assert_eq!(msg, "Test error"), _ => panic!("Expected InternalError"), } // Test sqlx error conversion use sqlx::Error as SqlxError; let sqlx_error = SqlxError::PoolClosed; let app_error: AppError = sqlx_error.into(); match app_error { AppError::DatabaseError(msg) => assert!(msg.contains("pool closed")), _ => panic!("Expected DatabaseError"), } }