use server::config::{Config, ConfigError, Environment, Protocol}; use std::fs; use tempfile::tempdir; #[tokio::test] async fn test_config_loading_from_toml_file() { let dir = tempdir().unwrap(); let config_path = dir.path().join("config.toml"); let config_content = r#" [server] protocol = "http" host = "127.0.0.1" port = 3030 environment = "development" log_level = "info" [database] url = "postgresql://localhost:5432/test" max_connections = 10 min_connections = 1 connect_timeout = 30 idle_timeout = 600 max_lifetime = 1800 [session] secret = "test-secret" cookie_name = "session_id" cookie_secure = false cookie_http_only = true cookie_same_site = "lax" max_age = 3600 [cors] allowed_origins = ["http://localhost:3030"] allowed_methods = ["GET", "POST"] allowed_headers = ["Content-Type"] allow_credentials = true max_age = 3600 [static] assets_dir = "public" site_root = "target/site" site_pkg_dir = "pkg" [server_dirs] public_dir = "public" uploads_dir = "uploads" logs_dir = "logs" temp_dir = "tmp" cache_dir = "cache" config_dir = "config" data_dir = "data" backup_dir = "backups" [security] enable_csrf = true csrf_token_name = "csrf_token" rate_limit_requests = 100 rate_limit_window = 60 bcrypt_cost = 12 [oauth] enabled = false [email] enabled = false provider = "console" smtp_host = "localhost" smtp_port = 587 smtp_username = "" smtp_password = "" smtp_use_tls = true smtp_use_starttls = false sendgrid_api_key = "" sendgrid_endpoint = "https://api.sendgrid.com/v3/mail/send" from_email = "test@example.com" from_name = "Test" template_dir = "templates/email" [redis] enabled = false url = "redis://localhost:6379" pool_size = 10 connection_timeout = 5 command_timeout = 5 [app] name = "Test App" version = "0.1.0" debug = true enable_metrics = false enable_health_check = true enable_compression = true max_request_size = 10485760 [logging] format = "text" level = "info" file_path = "logs/app.log" max_file_size = 10485760 max_files = 5 enable_console = true enable_file = false [content] enabled = false content_dir = "content" cache_enabled = true cache_ttl = 3600 max_file_size = 5242880 [features] [features.auth] enabled = true jwt = true oauth = false two_factor = false sessions = true password_reset = true email_verification = true account_lockout = true [features.rbac] enabled = false database_access = false file_access = false content_access = false api_access = false categories = false tags = false caching = false audit_logging = false toml_config = false hierarchical_permissions = false dynamic_rules = false [features.content] enabled = true markdown = true syntax_highlighting = false file_uploads = false versioning = false scheduling = false seo = false [features.security] csrf = true security_headers = true rate_limiting = true input_sanitization = true sql_injection_protection = true xss_protection = true content_security_policy = true [features.performance] response_caching = false query_caching = false compression = true connection_pooling = true lazy_loading = false background_tasks = false [features.custom] "#; fs::write(&config_path, config_content).unwrap(); let config = Config::load_from_file(&config_path).unwrap(); // Verify server configuration assert_eq!(config.server.host, "127.0.0.1"); assert_eq!(config.server.port, 3030); assert_eq!(config.server.log_level, "info"); assert!(matches!(config.server.protocol, Protocol::Http)); assert!(matches!( config.server.environment, Environment::Development )); // Verify database configuration assert_eq!(config.database.url, "postgresql://localhost:5432/test"); assert_eq!(config.database.max_connections, 10); // Verify application configuration assert_eq!(config.app.name, "Test App"); assert_eq!(config.app.version, "0.1.0"); assert_eq!(config.app.debug, true); // Verify server directories configuration assert_eq!(config.server_dirs.public_dir, "public"); assert_eq!(config.server_dirs.uploads_dir, "uploads"); assert_eq!(config.server_dirs.logs_dir, "logs"); assert_eq!(config.server_dirs.cache_dir, "cache"); // Verify helper methods assert_eq!(config.server_address(), "127.0.0.1:3030"); assert_eq!(config.server_url(), "http://127.0.0.1:3030"); assert_eq!(config.is_development(), true); assert_eq!(config.is_production(), false); assert_eq!(config.requires_tls(), false); } #[tokio::test] async fn test_https_configuration_validation() { let dir = tempdir().unwrap(); let config_path = dir.path().join("config.toml"); // Create a configuration that should fail validation (HTTPS without TLS config) let config_content = r#" [server] protocol = "https" host = "127.0.0.1" port = 443 environment = "production" log_level = "info" [database] url = "postgresql://localhost:5432/test" max_connections = 10 min_connections = 1 connect_timeout = 30 idle_timeout = 600 max_lifetime = 1800 [session] secret = "test-secret" cookie_name = "session_id" cookie_secure = false cookie_http_only = true cookie_same_site = "lax" max_age = 3600 [cors] allowed_origins = ["http://localhost:3030"] allowed_methods = ["GET", "POST"] allowed_headers = ["Content-Type"] allow_credentials = true max_age = 3600 [static] assets_dir = "public" site_root = "target/site" site_pkg_dir = "pkg" [server_dirs] public_dir = "public" uploads_dir = "uploads" logs_dir = "logs" temp_dir = "tmp" cache_dir = "cache" config_dir = "config" data_dir = "data" backup_dir = "backups" [security] enable_csrf = true csrf_token_name = "csrf_token" rate_limit_requests = 100 rate_limit_window = 60 bcrypt_cost = 12 [oauth] enabled = false [email] enabled = false provider = "console" smtp_host = "localhost" smtp_port = 587 smtp_username = "" smtp_password = "" smtp_use_tls = true smtp_use_starttls = false sendgrid_api_key = "" sendgrid_endpoint = "https://api.sendgrid.com/v3/mail/send" from_email = "test@example.com" from_name = "Test" template_dir = "templates/email" [redis] enabled = false url = "redis://localhost:6379" pool_size = 10 connection_timeout = 5 command_timeout = 5 [app] name = "Test App" version = "0.1.0" debug = true enable_metrics = false enable_health_check = true enable_compression = true max_request_size = 10485760 [logging] format = "text" level = "info" file_path = "logs/app.log" max_file_size = 10485760 max_files = 5 enable_console = true enable_file = false [content] enabled = false content_dir = "content" cache_enabled = true cache_ttl = 3600 max_file_size = 5242880 [features] [features.auth] enabled = true jwt = true oauth = false two_factor = false sessions = true password_reset = true email_verification = true [features.rbac] enabled = false database_access = false file_access = false content_access = false api_access = false categories = false tags = false caching = false audit_logging = false toml_config = false hierarchical_permissions = false dynamic_rules = false [features.content] enabled = true markdown = true syntax_highlighting = false file_uploads = false versioning = false scheduling = false seo = false [features.security] csrf = true security_headers = true rate_limiting = true input_sanitization = true sql_injection_protection = true xss_protection = true content_security_policy = true [features.performance] response_caching = false query_caching = false compression = true connection_pooling = true lazy_loading = false background_tasks = false [features.custom] "#; fs::write(&config_path, config_content).unwrap(); let config = Config::load_from_file(&config_path).unwrap(); // This should fail because HTTPS is enabled but no TLS config is provided let result = config.validate(); assert!(result.is_err()); match result.unwrap_err() { ConfigError::ValidationError(msg) => { assert!(msg.contains("HTTPS protocol requires TLS configuration")); } other => { panic!("Expected ValidationError about HTTPS/TLS, got: {:?}", other); } } } #[tokio::test] async fn test_invalid_toml_format() { let dir = tempdir().unwrap(); let config_path = dir.path().join("config.toml"); // Create an invalid TOML file let invalid_toml = r#" [server protocol = "http" host = "127.0.0.1" port = 3030 "#; fs::write(&config_path, invalid_toml).unwrap(); let result = Config::load_from_file(&config_path); assert!(result.is_err()); match result.unwrap_err() { ConfigError::ParseError(_) => { // Expected error } other => { panic!("Expected ParseError, got: {:?}", other); } } } #[tokio::test] async fn test_database_pool_config() { let dir = tempdir().unwrap(); let config_path = dir.path().join("config.toml"); let config_content = r#" [server] protocol = "http" host = "127.0.0.1" port = 3030 environment = "development" log_level = "info" [database] url = "postgresql://localhost:5432/test" max_connections = 15 min_connections = 2 connect_timeout = 45 idle_timeout = 900 max_lifetime = 3600 [session] secret = "test-secret" cookie_name = "session_id" cookie_secure = false cookie_http_only = true cookie_same_site = "lax" max_age = 3600 [cors] allowed_origins = ["http://localhost:3030"] allowed_methods = ["GET", "POST"] allowed_headers = ["Content-Type"] allow_credentials = true max_age = 3600 [static] assets_dir = "public" site_root = "target/site" site_pkg_dir = "pkg" [server_dirs] public_dir = "public" uploads_dir = "uploads" logs_dir = "logs" temp_dir = "tmp" cache_dir = "cache" config_dir = "config" data_dir = "data" backup_dir = "backups" [security] enable_csrf = true csrf_token_name = "csrf_token" rate_limit_requests = 100 rate_limit_window = 60 bcrypt_cost = 12 [oauth] enabled = false [email] enabled = false provider = "console" smtp_host = "localhost" smtp_port = 587 smtp_username = "" smtp_password = "" smtp_use_tls = true smtp_use_starttls = false sendgrid_api_key = "" sendgrid_endpoint = "https://api.sendgrid.com/v3/mail/send" from_email = "test@example.com" from_name = "Test" template_dir = "templates/email" [redis] enabled = false url = "redis://localhost:6379" pool_size = 10 connection_timeout = 5 command_timeout = 5 [app] name = "Test App" version = "0.1.0" debug = true enable_metrics = false enable_health_check = true enable_compression = true max_request_size = 10485760 [logging] format = "text" level = "info" file_path = "logs/app.log" max_file_size = 10485760 max_files = 5 enable_console = true enable_file = false [content] enabled = false content_dir = "content" cache_enabled = true cache_ttl = 3600 max_file_size = 5242880 [features] [features.auth] enabled = true jwt = true oauth = false two_factor = false sessions = true password_reset = true email_verification = true [features.rbac] enabled = false database_access = false file_access = false content_access = false api_access = false categories = false tags = false caching = false audit_logging = false toml_config = false hierarchical_permissions = false dynamic_rules = false [features.content] enabled = true markdown = true syntax_highlighting = false file_uploads = false versioning = false scheduling = false seo = false [features.security] csrf = true security_headers = true rate_limiting = true input_sanitization = true sql_injection_protection = true xss_protection = true content_security_policy = true [features.performance] response_caching = false query_caching = false compression = true connection_pooling = true lazy_loading = false background_tasks = false [features.custom] "#; fs::write(&config_path, config_content).unwrap(); let config = Config::load_from_file(&config_path).unwrap(); let pool_config = config.database_pool_config(); assert_eq!(pool_config.url, "postgresql://localhost:5432/test"); assert_eq!(pool_config.max_connections, 15); assert_eq!(pool_config.min_connections, 2); assert_eq!( pool_config.connect_timeout, std::time::Duration::from_secs(45) ); assert_eq!( pool_config.idle_timeout, std::time::Duration::from_secs(900) ); assert_eq!( pool_config.max_lifetime, std::time::Duration::from_secs(3600) ); } #[test] fn test_environment_string_substitution_mock() { // Test the string substitution function with a mock approach // This test assumes DB_PASSWORD is already set in environment let input_without_substitution = "postgresql://user:password@localhost:5432/db"; // Test that strings without substitution pass through unchanged let result = Config::substitute_env_in_string(input_without_substitution); assert_eq!(result, "postgresql://user:password@localhost:5432/db"); } #[test] fn test_config_defaults() { let config = Config::default(); // Test default values assert_eq!(config.server.host, "127.0.0.1"); assert_eq!(config.server.port, 3030); assert!(matches!(config.server.protocol, Protocol::Http)); assert!(matches!( config.server.environment, Environment::Development )); assert_eq!(config.app.name, "My Rust App"); assert_eq!(config.app.debug, true); assert_eq!(config.features.auth.enabled, true); assert_eq!(config.features.security.csrf, true); // Test server directories defaults assert_eq!(config.server_dirs.public_dir, "public"); assert_eq!(config.server_dirs.uploads_dir, "uploads"); assert_eq!(config.server_dirs.logs_dir, "logs"); assert_eq!(config.server_dirs.temp_dir, "tmp"); assert_eq!(config.server_dirs.cache_dir, "cache"); assert_eq!(config.server_dirs.config_dir, "config"); assert_eq!(config.server_dirs.data_dir, "data"); assert_eq!(config.server_dirs.backup_dir, "backups"); } #[test] fn test_config_helper_methods() { let mut config = Config::default(); // Test HTTP configuration assert_eq!(config.server_address(), "127.0.0.1:3030"); assert_eq!(config.server_url(), "http://127.0.0.1:3030"); assert_eq!(config.is_development(), true); assert_eq!(config.is_production(), false); assert_eq!(config.requires_tls(), false); // Test HTTPS configuration config.server.protocol = Protocol::Https; config.server.port = 443; config.server.environment = Environment::Production; assert_eq!(config.server_address(), "127.0.0.1:443"); assert_eq!(config.server_url(), "https://127.0.0.1:443"); assert_eq!(config.is_development(), false); assert_eq!(config.is_production(), true); assert_eq!(config.requires_tls(), true); }