use cloudllm::tools::HttpClient;
fn assert_domain_rejected(
result: Result<cloudllm::tools::http_client::HttpResponse, impl std::fmt::Display>,
) {
assert!(result.is_err());
let message = result.unwrap_err().to_string();
assert!(
message.contains("blocked")
|| message.contains("not allowed")
|| message.contains("reserved/private range")
|| message.contains("Could not resolve host"),
"unexpected domain rejection message: {}",
message
);
}
#[tokio::test]
async fn test_http_client_creation() {
let _client = HttpClient::new();
let _default_client = HttpClient::default();
}
#[tokio::test]
async fn test_query_parameter_building() {
let mut client = HttpClient::new();
client.allow_domain("example.com");
client.with_query_param("key1", "value1");
client.with_query_param("key2", "value2");
client.with_query_param("key3", "value3");
}
#[tokio::test]
async fn test_header_building() {
let mut client = HttpClient::new();
client.with_header("X-Custom", "header-value");
client.with_header("Authorization", "Bearer token123");
client.with_header("User-Agent", "MyAgent/1.0");
client.with_header("Accept", "application/json");
}
#[tokio::test]
async fn test_basic_auth_encoding() {
let mut client = HttpClient::new();
client.with_basic_auth("user", "pass");
}
#[tokio::test]
async fn test_timeout_configuration() {
let mut client = HttpClient::new();
let timeout = std::time::Duration::from_secs(10);
client.with_timeout(timeout);
client.with_timeout(std::time::Duration::from_secs(5));
}
#[tokio::test]
async fn test_max_response_size_configuration() {
let mut client = HttpClient::new();
client.with_max_response_size(50 * 1024 * 1024); client.with_max_response_size(100 * 1024 * 1024); }
#[tokio::test]
async fn test_domain_allowlist() {
let mut client = HttpClient::new();
client.allow_domain("api.example.com");
client.allow_domain("api.partner.com");
let _ = client.get("https://api.example.com/test").await;
}
#[tokio::test]
async fn test_domain_blocklist() {
let mut client = HttpClient::new();
client.deny_domain("malicious.com");
client.deny_domain("phishing.net");
assert_domain_rejected(client.get("https://malicious.com/data").await);
assert_domain_rejected(client.get("https://phishing.net/data").await);
}
#[tokio::test]
async fn test_domain_extraction_https() {
let mut client = HttpClient::new();
client.allow_domain("api.example.com");
let _ = client.get("https://api.example.com/path/to/resource").await;
}
#[tokio::test]
async fn test_domain_extraction_http() {
let mut client = HttpClient::new();
client.allow_domain("example.com");
let _ = client.get("http://example.com/data").await;
}
#[tokio::test]
async fn test_domain_extraction_with_port() {
let mut client = HttpClient::new();
client.allow_domain("api.example.com");
let _ = client.get("https://api.example.com:8080/data").await;
}
#[tokio::test]
async fn test_domain_extraction_with_path() {
let mut client = HttpClient::new();
client.allow_domain("example.com");
let _ = client.get("https://example.com/api/v1/users").await;
}
#[tokio::test]
async fn test_blocklist_takes_precedence() {
let mut client = HttpClient::new();
client.allow_domain("evil.com"); client.deny_domain("evil.com");
assert_domain_rejected(client.get("https://evil.com/data").await);
}
#[tokio::test]
async fn test_empty_allowlist_allows_all() {
let client = HttpClient::new();
let _ = client.get("https://any-domain.com/data").await;
}
#[tokio::test]
async fn test_http_response_success_check() {
use cloudllm::tools::http_client::HttpResponse;
let response_2xx = HttpResponse {
status: 200,
headers: std::collections::HashMap::new(),
body: "OK".to_string(),
};
assert!(response_2xx.is_success());
assert!(!response_2xx.is_client_error());
assert!(!response_2xx.is_server_error());
let response_201 = HttpResponse {
status: 201,
headers: std::collections::HashMap::new(),
body: "Created".to_string(),
};
assert!(response_201.is_success());
}
#[tokio::test]
async fn test_http_response_client_error_check() {
use cloudllm::tools::http_client::HttpResponse;
let response_404 = HttpResponse {
status: 404,
headers: std::collections::HashMap::new(),
body: "Not Found".to_string(),
};
assert!(!response_404.is_success());
assert!(response_404.is_client_error());
assert!(!response_404.is_server_error());
let response_400 = HttpResponse {
status: 400,
headers: std::collections::HashMap::new(),
body: "Bad Request".to_string(),
};
assert!(response_400.is_client_error());
}
#[tokio::test]
async fn test_http_response_server_error_check() {
use cloudllm::tools::http_client::HttpResponse;
let response_500 = HttpResponse {
status: 500,
headers: std::collections::HashMap::new(),
body: "Internal Server Error".to_string(),
};
assert!(!response_500.is_success());
assert!(!response_500.is_client_error());
assert!(response_500.is_server_error());
let response_502 = HttpResponse {
status: 502,
headers: std::collections::HashMap::new(),
body: "Bad Gateway".to_string(),
};
assert!(response_502.is_server_error());
}
#[tokio::test]
async fn test_http_response_json_parsing() {
use cloudllm::tools::http_client::HttpResponse;
let json_body = r#"{"name": "Alice", "age": 30, "active": true}"#;
let response = HttpResponse {
status: 200,
headers: std::collections::HashMap::new(),
body: json_body.to_string(),
};
let parsed = response.json().unwrap();
assert_eq!(parsed["name"], "Alice");
assert_eq!(parsed["age"], 30);
assert_eq!(parsed["active"], true);
}
#[tokio::test]
async fn test_http_response_json_parsing_array() {
use cloudllm::tools::http_client::HttpResponse;
let json_body = r#"[{"id": 1}, {"id": 2}, {"id": 3}]"#;
let response = HttpResponse {
status: 200,
headers: std::collections::HashMap::new(),
body: json_body.to_string(),
};
let parsed = response.json().unwrap();
assert!(parsed.is_array());
assert_eq!(parsed.as_array().unwrap().len(), 3);
}
#[tokio::test]
async fn test_http_response_json_parsing_failure() {
use cloudllm::tools::http_client::HttpResponse;
let invalid_json = "this is not json";
let response = HttpResponse {
status: 200,
headers: std::collections::HashMap::new(),
body: invalid_json.to_string(),
};
let parsed = response.json();
assert!(parsed.is_err());
}
#[tokio::test]
async fn test_query_params_special_characters() {
let mut client = HttpClient::new();
client.allow_domain("example.com");
client.with_query_param("search", "hello world");
client.with_query_param("filter", "status=active&type=premium");
}
#[tokio::test]
async fn test_builder_pattern_chainable() {
let mut client = HttpClient::new();
let _ = client
.allow_domain("api.example.com")
.with_header("Authorization", "Bearer token")
.with_query_param("key", "value")
.with_timeout(std::time::Duration::from_secs(10));
}
#[tokio::test]
async fn test_multiple_domains_allowlist() {
let mut client = HttpClient::new();
client.allow_domain("api1.com");
client.allow_domain("api2.com");
client.allow_domain("api3.com");
let _ = client.get("https://api1.com/data").await;
let _ = client.get("https://api2.com/data").await;
let _ = client.get("https://api3.com/data").await;
}
#[tokio::test]
async fn test_multiple_domains_blocklist() {
let mut client = HttpClient::new();
client.deny_domain("bad1.com");
client.deny_domain("bad2.com");
client.deny_domain("bad3.com");
let result1 = client.get("https://bad1.com/data").await;
assert!(result1.is_err());
let result2 = client.get("https://bad2.com/data").await;
assert!(result2.is_err());
let result3 = client.get("https://bad3.com/data").await;
assert!(result3.is_err());
}
#[tokio::test]
async fn test_invalid_url_domain_extraction() {
let mut client = HttpClient::new();
client.allow_domain("example.com");
let result = client.get("example.com/data").await;
assert!(result.is_err());
let result = client.get("https://").await;
assert!(result.is_err());
}
#[tokio::test]
async fn test_http_client_clone() {
let mut client = HttpClient::new();
client.allow_domain("example.com");
client.with_header("X-Test", "value");
let _cloned = client.clone();
}
#[tokio::test]
async fn test_http_response_status_code_boundaries() {
use cloudllm::tools::http_client::HttpResponse;
let status_199 = HttpResponse {
status: 199,
headers: std::collections::HashMap::new(),
body: String::new(),
};
assert!(!status_199.is_success());
let status_200 = HttpResponse {
status: 200,
headers: std::collections::HashMap::new(),
body: String::new(),
};
assert!(status_200.is_success());
let status_299 = HttpResponse {
status: 299,
headers: std::collections::HashMap::new(),
body: String::new(),
};
assert!(status_299.is_success());
let status_300 = HttpResponse {
status: 300,
headers: std::collections::HashMap::new(),
body: String::new(),
};
assert!(!status_300.is_success());
}
#[tokio::test]
async fn test_default_timeout_is_30_seconds() {
let _client = HttpClient::new();
let mut client_custom = HttpClient::new();
client_custom.with_timeout(std::time::Duration::from_secs(5));
}
#[tokio::test]
async fn test_default_max_response_size_is_10mb() {
let _client = HttpClient::new();
let mut client_small = HttpClient::new();
client_small.with_max_response_size(1024); }