use redis::RedisResult; use sal_redisclient::*; use std::env; #[cfg(test)] mod redis_client_tests { use super::*; #[test] fn test_env_vars() { // Save original REDISDB value to restore later let original_redisdb = env::var("REDISDB").ok(); // Set test environment variables env::set_var("REDISDB", "5"); // Test with invalid value env::set_var("REDISDB", "invalid"); // Test with unset value env::remove_var("REDISDB"); // Restore original REDISDB value if let Some(redisdb) = original_redisdb { env::set_var("REDISDB", redisdb); } else { env::remove_var("REDISDB"); } } #[test] fn test_redis_config_environment_variables() { // Test that environment variables are properly handled let original_home = env::var("HOME").ok(); let original_redis_host = env::var("REDIS_HOST").ok(); let original_redis_port = env::var("REDIS_PORT").ok(); // Set test environment variables env::set_var("HOME", "/tmp/test"); env::set_var("REDIS_HOST", "test.redis.com"); env::set_var("REDIS_PORT", "6380"); // Test that the configuration builder respects environment variables let config = RedisConfigBuilder::new() .host(&env::var("REDIS_HOST").unwrap_or_else(|_| "127.0.0.1".to_string())) .port( env::var("REDIS_PORT") .ok() .and_then(|p| p.parse().ok()) .unwrap_or(6379), ); assert_eq!(config.host, "test.redis.com"); assert_eq!(config.port, 6380); // Restore original environment variables if let Some(home) = original_home { env::set_var("HOME", home); } else { env::remove_var("HOME"); } if let Some(host) = original_redis_host { env::set_var("REDIS_HOST", host); } else { env::remove_var("REDIS_HOST"); } if let Some(port) = original_redis_port { env::set_var("REDIS_PORT", port); } else { env::remove_var("REDIS_PORT"); } } #[test] fn test_redis_config_validation() { // Test configuration validation and edge cases // Test invalid port handling let config = RedisConfigBuilder::new().port(0); assert_eq!(config.port, 0); // Should accept any port value // Test empty strings let config = RedisConfigBuilder::new().host("").username("").password(""); assert_eq!(config.host, ""); assert_eq!(config.username, Some("".to_string())); assert_eq!(config.password, Some("".to_string())); // Test chaining methods let config = RedisConfigBuilder::new() .host("localhost") .port(6379) .db(1) .use_tls(true) .connection_timeout(30); assert_eq!(config.host, "localhost"); assert_eq!(config.port, 6379); assert_eq!(config.db, 1); assert_eq!(config.use_tls, true); assert_eq!(config.connection_timeout, Some(30)); } #[test] fn test_redis_config_builder() { // Test the Redis configuration builder // Test default values let config = RedisConfigBuilder::new(); assert_eq!(config.host, "127.0.0.1"); assert_eq!(config.port, 6379); assert_eq!(config.db, 0); assert_eq!(config.username, None); assert_eq!(config.password, None); assert_eq!(config.use_tls, false); assert_eq!(config.use_unix_socket, false); assert_eq!(config.socket_path, None); assert_eq!(config.connection_timeout, None); // Test setting values let config = RedisConfigBuilder::new() .host("redis.example.com") .port(6380) .db(1) .username("user") .password("pass") .use_tls(true) .connection_timeout(30); assert_eq!(config.host, "redis.example.com"); assert_eq!(config.port, 6380); assert_eq!(config.db, 1); assert_eq!(config.username, Some("user".to_string())); assert_eq!(config.password, Some("pass".to_string())); assert_eq!(config.use_tls, true); assert_eq!(config.connection_timeout, Some(30)); // Test socket path setting let config = RedisConfigBuilder::new().socket_path("/tmp/redis.sock"); assert_eq!(config.use_unix_socket, true); assert_eq!(config.socket_path, Some("/tmp/redis.sock".to_string())); } #[test] fn test_connection_url_building() { // Test building connection URLs // Test default URL let config = RedisConfigBuilder::new(); let url = config.build_connection_url(); assert_eq!(url, "redis://127.0.0.1:6379/0"); // Test with authentication let config = RedisConfigBuilder::new().username("user").password("pass"); let url = config.build_connection_url(); assert_eq!(url, "redis://user:pass@127.0.0.1:6379/0"); // Test with password only let config = RedisConfigBuilder::new().password("pass"); let url = config.build_connection_url(); assert_eq!(url, "redis://:pass@127.0.0.1:6379/0"); // Test with TLS let config = RedisConfigBuilder::new().use_tls(true); let url = config.build_connection_url(); assert_eq!(url, "rediss://127.0.0.1:6379/0"); // Test with Unix socket let config = RedisConfigBuilder::new().socket_path("/tmp/redis.sock"); let url = config.build_connection_url(); assert_eq!(url, "unix:///tmp/redis.sock"); } } // Integration tests that require a real Redis server // These tests will be skipped if Redis is not available #[cfg(test)] mod redis_integration_tests { use super::*; // Helper function to check if Redis is available fn is_redis_available() -> bool { match get_redis_client() { Ok(_) => true, Err(_) => false, } } #[test] fn test_redis_client_integration() { if !is_redis_available() { println!("Skipping Redis integration tests - Redis server not available"); return; } println!("Running Redis integration tests..."); // Test basic operations test_basic_redis_operations(); // Test more complex operations test_hash_operations(); test_list_operations(); // Test error handling test_error_handling(); } fn test_basic_redis_operations() { if !is_redis_available() { return; } // Test setting and getting values let client_result = get_redis_client(); if client_result.is_err() { // Skip the test if we can't connect to Redis return; } // Create SET command let mut set_cmd = redis::cmd("SET"); set_cmd.arg("test_key").arg("test_value"); // Execute SET command let set_result: RedisResult<()> = execute(&mut set_cmd); assert!(set_result.is_ok()); // Create GET command let mut get_cmd = redis::cmd("GET"); get_cmd.arg("test_key"); // Execute GET command and check the result if let Ok(value) = execute::(&mut get_cmd) { assert_eq!(value, "test_value"); } // Test expiration let mut expire_cmd = redis::cmd("EXPIRE"); expire_cmd.arg("test_key").arg(1); // Expire in 1 second let expire_result: RedisResult = execute(&mut expire_cmd); assert!(expire_result.is_ok()); assert_eq!(expire_result.unwrap(), 1); // Sleep for 2 seconds to let the key expire std::thread::sleep(std::time::Duration::from_secs(2)); // Check that the key has expired let mut exists_cmd = redis::cmd("EXISTS"); exists_cmd.arg("test_key"); let exists_result: RedisResult = execute(&mut exists_cmd); assert!(exists_result.is_ok()); assert_eq!(exists_result.unwrap(), 0); // Clean up let _: RedisResult<()> = execute(&mut redis::cmd("DEL").arg("test_key")); } fn test_hash_operations() { if !is_redis_available() { return; } // Test hash operations let hash_key = "test_hash"; // Set hash fields let mut hset_cmd = redis::cmd("HSET"); hset_cmd .arg(hash_key) .arg("field1") .arg("value1") .arg("field2") .arg("value2"); let hset_result: RedisResult = execute(&mut hset_cmd); assert!(hset_result.is_ok()); assert_eq!(hset_result.unwrap(), 2); // Get hash field let mut hget_cmd = redis::cmd("HGET"); hget_cmd.arg(hash_key).arg("field1"); let hget_result: RedisResult = execute(&mut hget_cmd); assert!(hget_result.is_ok()); assert_eq!(hget_result.unwrap(), "value1"); // Get all hash fields let mut hgetall_cmd = redis::cmd("HGETALL"); hgetall_cmd.arg(hash_key); let hgetall_result: RedisResult> = execute(&mut hgetall_cmd); assert!(hgetall_result.is_ok()); let hgetall_values = hgetall_result.unwrap(); assert_eq!(hgetall_values.len(), 4); // field1, value1, field2, value2 // Delete hash field let mut hdel_cmd = redis::cmd("HDEL"); hdel_cmd.arg(hash_key).arg("field1"); let hdel_result: RedisResult = execute(&mut hdel_cmd); assert!(hdel_result.is_ok()); assert_eq!(hdel_result.unwrap(), 1); // Clean up let _: RedisResult<()> = execute(&mut redis::cmd("DEL").arg(hash_key)); } fn test_list_operations() { if !is_redis_available() { return; } // Test list operations let list_key = "test_list"; // Push items to list let mut rpush_cmd = redis::cmd("RPUSH"); rpush_cmd .arg(list_key) .arg("item1") .arg("item2") .arg("item3"); let rpush_result: RedisResult = execute(&mut rpush_cmd); assert!(rpush_result.is_ok()); assert_eq!(rpush_result.unwrap(), 3); // Get list length let mut llen_cmd = redis::cmd("LLEN"); llen_cmd.arg(list_key); let llen_result: RedisResult = execute(&mut llen_cmd); assert!(llen_result.is_ok()); assert_eq!(llen_result.unwrap(), 3); // Get list range let mut lrange_cmd = redis::cmd("LRANGE"); lrange_cmd.arg(list_key).arg(0).arg(-1); let lrange_result: RedisResult> = execute(&mut lrange_cmd); assert!(lrange_result.is_ok()); let lrange_values = lrange_result.unwrap(); assert_eq!(lrange_values.len(), 3); assert_eq!(lrange_values[0], "item1"); assert_eq!(lrange_values[1], "item2"); assert_eq!(lrange_values[2], "item3"); // Pop item from list let mut lpop_cmd = redis::cmd("LPOP"); lpop_cmd.arg(list_key); let lpop_result: RedisResult = execute(&mut lpop_cmd); assert!(lpop_result.is_ok()); assert_eq!(lpop_result.unwrap(), "item1"); // Clean up let _: RedisResult<()> = execute(&mut redis::cmd("DEL").arg(list_key)); } fn test_error_handling() { if !is_redis_available() { return; } // Test error handling // Test invalid command let mut invalid_cmd = redis::cmd("INVALID_COMMAND"); let invalid_result: RedisResult<()> = execute(&mut invalid_cmd); assert!(invalid_result.is_err()); // Test wrong data type let key = "test_wrong_type"; // Set a string value let mut set_cmd = redis::cmd("SET"); set_cmd.arg(key).arg("string_value"); let set_result: RedisResult<()> = execute(&mut set_cmd); assert!(set_result.is_ok()); // Try to use a hash command on a string let mut hget_cmd = redis::cmd("HGET"); hget_cmd.arg(key).arg("field"); let hget_result: RedisResult = execute(&mut hget_cmd); assert!(hget_result.is_err()); // Clean up let _: RedisResult<()> = execute(&mut redis::cmd("DEL").arg(key)); } }