#[cfg(test)]
mod tests {
use crate::{EnterpriseClient, RestError, Result};
use wiremock::matchers::{basic_auth, method, path};
use wiremock::{Mock, MockServer, ResponseTemplate};
#[tokio::test]
async fn test_enterprise_client_builder_default() {
let builder = EnterpriseClient::builder();
let client = builder.username("test").password("test").build();
assert!(client.is_ok());
}
#[tokio::test]
async fn test_enterprise_client_creation() {
let result = EnterpriseClient::builder()
.base_url("https://example.com")
.username("test_user")
.password("test_pass")
.timeout(std::time::Duration::from_secs(10))
.insecure(false)
.build();
assert!(result.is_ok());
}
#[tokio::test]
async fn test_enterprise_client_get_request() {
let mock_server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/test"))
.and(basic_auth("test_user", "test_pass"))
.respond_with(
ResponseTemplate::new(200).set_body_json(serde_json::json!({"status": "ok"})),
)
.mount(&mock_server)
.await;
let client = EnterpriseClient::builder()
.base_url(mock_server.uri())
.username("test_user")
.password("test_pass")
.timeout(std::time::Duration::from_secs(10))
.insecure(false)
.build()
.unwrap();
let result: Result<serde_json::Value> = client.get("/test").await;
assert!(result.is_ok());
let value = result.unwrap();
assert_eq!(value["status"], "ok");
}
#[tokio::test]
async fn test_enterprise_client_post_request() {
let mock_server = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/test"))
.and(basic_auth("test_user", "test_pass"))
.respond_with(
ResponseTemplate::new(201).set_body_json(serde_json::json!({"created": true})),
)
.mount(&mock_server)
.await;
let client = EnterpriseClient::builder()
.base_url(mock_server.uri())
.username("test_user")
.password("test_pass")
.timeout(std::time::Duration::from_secs(10))
.insecure(false)
.build()
.unwrap();
let test_data = serde_json::json!({"name": "test"});
let result: Result<serde_json::Value> = client.post("/test", &test_data).await;
assert!(result.is_ok());
let value = result.unwrap();
assert_eq!(value["created"], true);
}
#[tokio::test]
async fn test_enterprise_client_error_handling() {
let mock_server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/error"))
.and(basic_auth("test_user", "test_pass"))
.respond_with(
ResponseTemplate::new(404).set_body_json(serde_json::json!({"error": "Not found"})),
)
.mount(&mock_server)
.await;
let client = EnterpriseClient::builder()
.base_url(mock_server.uri())
.username("test_user")
.password("test_pass")
.timeout(std::time::Duration::from_secs(10))
.insecure(false)
.build()
.unwrap();
let result: Result<serde_json::Value> = client.get("/error").await;
assert!(result.is_err());
let err = result.unwrap_err();
assert!(
err.is_not_found(),
"Expected not found error, got: {:?}",
err
);
}
#[tokio::test]
async fn test_enterprise_client_authentication_error() {
let mock_server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/auth-test"))
.respond_with(
ResponseTemplate::new(401)
.set_body_json(serde_json::json!({"error": "Unauthorized"})),
)
.mount(&mock_server)
.await;
let client = EnterpriseClient::builder()
.base_url(mock_server.uri())
.username("wrong_user")
.password("wrong_pass")
.timeout(std::time::Duration::from_secs(10))
.insecure(false)
.build()
.unwrap();
let result: Result<serde_json::Value> = client.get("/auth-test").await;
assert!(result.is_err());
let err = result.unwrap_err();
assert!(
err.is_unauthorized(),
"Expected unauthorized error, got: {:?}",
err
);
}
#[test]
fn test_rest_error_display() {
let err = RestError::AuthenticationFailed;
assert_eq!(err.to_string(), "Authentication failed");
let err = RestError::ApiError {
code: 400,
message: "Bad request".to_string(),
};
assert_eq!(err.to_string(), "API error: Bad request (code: 400)");
let err = RestError::ConnectionError("Connection refused".to_string());
assert_eq!(err.to_string(), "Connection error: Connection refused");
let err = RestError::TlsError("cert validation failed".to_string());
assert_eq!(
err.to_string(),
"TLS certificate error: cert validation failed"
);
}
#[tokio::test]
async fn test_enterprise_client_delete_request() {
let mock_server = MockServer::start().await;
Mock::given(method("DELETE"))
.and(path("/test/123"))
.and(basic_auth("test_user", "test_pass"))
.respond_with(ResponseTemplate::new(204))
.mount(&mock_server)
.await;
let client = EnterpriseClient::builder()
.base_url(mock_server.uri())
.username("test_user")
.password("test_pass")
.timeout(std::time::Duration::from_secs(10))
.insecure(false)
.build()
.unwrap();
let result = client.delete("/test/123").await;
assert!(result.is_ok());
}
#[tokio::test]
async fn test_database_action_export() {
let mock_server = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/v1/bdbs/1/actions/export"))
.and(basic_auth("admin", "password"))
.respond_with(
ResponseTemplate::new(200)
.set_body_json(serde_json::json!({"action_uid": "export-123"})),
)
.mount(&mock_server)
.await;
let client = EnterpriseClient::builder()
.base_url(mock_server.uri())
.username("admin")
.password("password")
.build()
.unwrap();
let handler = crate::bdb::DatabaseHandler::new(client);
let result = handler.export(1, "ftp://backup/db1.rdb").await;
assert!(result.is_ok());
assert!(result.unwrap().action_uid.is_some());
}
#[tokio::test]
async fn test_database_action_import() {
let mock_server = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/v1/bdbs/1/actions/import"))
.and(basic_auth("admin", "password"))
.respond_with(
ResponseTemplate::new(200)
.set_body_json(serde_json::json!({"action_uid": "import-456"})),
)
.mount(&mock_server)
.await;
let client = EnterpriseClient::builder()
.base_url(mock_server.uri())
.username("admin")
.password("password")
.build()
.unwrap();
let handler = crate::bdb::DatabaseHandler::new(client);
let result = handler.import(1, "ftp://backup/db1.rdb", true).await;
assert!(result.is_ok());
assert!(result.unwrap().action_uid.is_some());
}
#[tokio::test]
async fn test_database_action_backup() {
let mock_server = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/v1/bdbs/1/actions/backup"))
.and(basic_auth("admin", "password"))
.respond_with(
ResponseTemplate::new(200)
.set_body_json(serde_json::json!({"backup_uid": "backup-789"})),
)
.mount(&mock_server)
.await;
let client = EnterpriseClient::builder()
.base_url(mock_server.uri())
.username("admin")
.password("password")
.build()
.unwrap();
let handler = crate::bdb::DatabaseHandler::new(client);
let result = handler.backup(1).await;
assert!(result.is_ok());
assert!(result.unwrap().backup_uid.is_some());
}
#[tokio::test]
async fn test_database_get_shards() {
let mock_server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/v1/bdbs/1/shards"))
.and(basic_auth("admin", "password"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!([
{"shard_id": 1, "role": "master"},
{"shard_id": 2, "role": "slave"}
])))
.mount(&mock_server)
.await;
let client = EnterpriseClient::builder()
.base_url(mock_server.uri())
.username("admin")
.password("password")
.build()
.unwrap();
let handler = crate::bdb::DatabaseHandler::new(client);
let result = handler.shards(1).await;
assert!(result.is_ok());
let shards = result.unwrap();
assert!(shards.is_array());
}
#[tokio::test]
async fn test_cluster_join_node() {
let mock_server = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/v1/bootstrap/join"))
.and(basic_auth("admin", "password"))
.respond_with(
ResponseTemplate::new(200).set_body_json(serde_json::json!({"status": "joined"})),
)
.mount(&mock_server)
.await;
let client = EnterpriseClient::builder()
.base_url(mock_server.uri())
.username("admin")
.password("password")
.build()
.unwrap();
let handler = crate::cluster::ClusterHandler::new(client);
let result = handler.join_node("192.168.1.10", "admin", "password").await;
assert!(result.is_ok());
assert_eq!(result.unwrap()["status"], "joined");
}
#[tokio::test]
async fn test_database_get_endpoints() {
let mock_server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/v1/bdbs/1/endpoints"))
.and(basic_auth("admin", "password"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!([
{
"uid": "endpoint:1:1",
"addr": ["192.168.1.10", "192.168.1.11"],
"port": 12000,
"dns_name": "redis-12000.cluster.local",
"proxy_policy": "single",
"addr_type": "internal",
"oss_cluster_api_preferred_ip_type": "internal"
},
{
"uid": "endpoint:1:2",
"addr": ["10.0.0.5"],
"port": 12001,
"dns_name": "redis-12001.cluster.local",
"proxy_policy": "all-master-shards",
"addr_type": "external",
"exclude_proxies": [1, 2],
"include_proxies": [3, 4, 5]
}
])))
.mount(&mock_server)
.await;
let client = EnterpriseClient::builder()
.base_url(mock_server.uri())
.username("admin")
.password("password")
.build()
.unwrap();
let handler = crate::bdb::DatabaseHandler::new(client);
let result = handler.endpoints(1).await;
assert!(result.is_ok());
let endpoints = result.unwrap();
assert_eq!(endpoints.len(), 2);
assert_eq!(endpoints[0].uid, Some("endpoint:1:1".to_string()));
assert_eq!(endpoints[0].port, Some(12000));
assert_eq!(
endpoints[0].dns_name,
Some("redis-12000.cluster.local".to_string())
);
assert_eq!(endpoints[0].proxy_policy, Some("single".to_string()));
assert_eq!(endpoints[0].addr_type, Some("internal".to_string()));
assert_eq!(endpoints[1].uid, Some("endpoint:1:2".to_string()));
assert_eq!(endpoints[1].port, Some(12001));
assert_eq!(
endpoints[1].proxy_policy,
Some("all-master-shards".to_string())
);
assert_eq!(endpoints[1].exclude_proxies, Some(vec![1, 2]));
assert_eq!(endpoints[1].include_proxies, Some(vec![3, 4, 5]));
}
#[tokio::test]
async fn test_ca_cert_builder_path_nonexistent() {
let result = EnterpriseClient::builder()
.base_url("https://example.com")
.username("test")
.password("test")
.ca_cert("/nonexistent/path/ca.pem")
.build();
match result {
Err(e) => assert!(
e.to_string().contains("Failed to read CA certificate"),
"Expected CA cert read error, got: {}",
e
),
Ok(_) => panic!("Expected error for nonexistent CA cert path"),
}
}
#[test]
fn test_ca_cert_from_env() {
unsafe {
std::env::remove_var("REDIS_ENTERPRISE_PASSWORD");
std::env::remove_var("REDIS_ENTERPRISE_CA_CERT");
}
let result = EnterpriseClient::from_env();
assert!(result.is_err()); }
#[tokio::test]
async fn test_url_normalization() {
let test_cases = vec![
(
"https://localhost:9443",
"/v1/cluster",
"https://localhost:9443/v1/cluster",
),
(
"https://localhost:9443/",
"/v1/cluster",
"https://localhost:9443/v1/cluster",
),
(
"https://localhost:9443",
"v1/cluster",
"https://localhost:9443/v1/cluster",
),
(
"https://localhost:9443/",
"v1/cluster",
"https://localhost:9443/v1/cluster",
),
(
"https://localhost:9443",
"/v1/bdbs/1",
"https://localhost:9443/v1/bdbs/1",
),
(
"https://localhost:9443/",
"/v1/bdbs/1",
"https://localhost:9443/v1/bdbs/1",
),
];
for (base_url, test_path, _expected) in test_cases {
let mock_server = MockServer::start().await;
Mock::given(method("GET"))
.and(path(test_path.trim_start_matches('/')))
.and(basic_auth("test", "test"))
.respond_with(
ResponseTemplate::new(200).set_body_json(serde_json::json!({"ok": true})),
)
.mount(&mock_server)
.await;
let client = EnterpriseClient::builder()
.base_url(base_url.replace("https://localhost:9443", &mock_server.uri()))
.username("test")
.password("test")
.build()
.unwrap();
let result: Result<serde_json::Value> = client.get(test_path).await;
assert!(
result.is_ok(),
"Failed for base_url: {}, path: {}",
base_url,
test_path
);
}
}
}