use std::time::Duration;
use onemoney_protocol::{
Error, Result,
client::{builder::ClientBuilder, config::Network, http::Client},
error::ErrorResponse,
};
#[test]
fn test_client_creation_methods() -> Result<()> {
let mainnet_client = Client::mainnet()?;
let mainnet_debug = format!("{:?}", mainnet_client);
assert!(mainnet_debug.contains("Client"));
assert!(mainnet_debug.contains("base_url"));
assert!(mainnet_debug.contains("hooks_count"));
let testnet_client = Client::testnet()?;
let testnet_debug = format!("{:?}", testnet_client);
assert!(testnet_debug.contains("Client"));
let local_client = Client::local()?;
let local_debug = format!("{:?}", local_client);
assert!(local_debug.contains("Client"));
Ok(())
}
#[test]
fn test_client_builder_creation() {
let mainnet_result = ClientBuilder::new().network(Network::Mainnet).build();
assert!(mainnet_result.is_ok());
let testnet_result = ClientBuilder::new().network(Network::Testnet).build();
assert!(testnet_result.is_ok());
let local_result = ClientBuilder::new().network(Network::Local).build();
assert!(local_result.is_ok());
}
#[test]
fn test_client_debug_implementation() -> Result<()> {
let client = Client::mainnet()?;
let debug_str = format!("{:?}", client);
assert!(debug_str.contains("Client"));
assert!(debug_str.contains("base_url"));
assert!(debug_str.contains("hooks_count"));
assert!(debug_str.contains("hooks_count: 0"));
Ok(())
}
#[test]
fn test_network_client_creation() -> Result<()> {
let mainnet = Client::mainnet()?;
let testnet = Client::testnet()?;
let local = Client::local()?;
let mainnet_debug = format!("{:?}", mainnet);
let testnet_debug = format!("{:?}", testnet);
let local_debug = format!("{:?}", local);
assert!(mainnet_debug.contains("Client"));
assert!(testnet_debug.contains("Client"));
assert!(local_debug.contains("Client"));
Ok(())
}
#[test]
fn test_client_builder_timeout() {
let timeouts = [
Duration::from_secs(10),
Duration::from_secs(30),
Duration::from_secs(60),
Duration::from_millis(5000),
];
for timeout in timeouts {
let result = ClientBuilder::new().network(Network::Testnet).timeout(timeout).build();
assert!(result.is_ok());
}
}
#[test]
fn test_client_builder_invalid_timeout() {
let result = ClientBuilder::new()
.network(Network::Local)
.timeout(Duration::from_secs(0))
.build();
assert!(result.is_ok());
}
#[test]
fn test_network_enum_coverage() {
let networks = [Network::Mainnet, Network::Testnet, Network::Local];
for network in networks {
let result = ClientBuilder::new().network(network).build();
assert!(result.is_ok());
let client = result.expect("Test data should be valid");
let debug_str = format!("{:?}", client);
assert!(debug_str.contains("Client"));
}
}
#[test]
fn test_error_response_serialization() {
let error_response = ErrorResponse {
error_code: "test_error".to_string(),
message: "Test error message".to_string(),
};
let json = serde_json::to_string(&error_response).expect("Test data should be valid");
assert!(json.contains("test_error"));
assert!(json.contains("Test error message"));
let deserialized: ErrorResponse = serde_json::from_str(&json).expect("Test data should be valid");
assert_eq!(error_response.error_code, deserialized.error_code);
assert_eq!(error_response.message, deserialized.message);
let cloned = error_response.clone();
assert_eq!(error_response.error_code, cloned.error_code);
assert_eq!(error_response.message, cloned.message);
let debug_str = format!("{:?}", error_response);
assert!(debug_str.contains("ErrorResponse"));
assert!(debug_str.contains("test_error"));
}
#[test]
fn test_error_response_various_codes() {
let error_codes = [
("validation_amount", "Amount must be positive"),
("invalid_credentials", "Invalid API key"),
("insufficient_permissions", "Access denied"),
("resource_transaction", "Transaction not found"),
("business_transfer", "Insufficient balance"),
("rate_limit_exceeded", "Too many requests"),
("system_database", "Database connection failed"),
("request_timeout", "Request took too long"),
("unknown_error", "Something went wrong"),
];
for (error_code, message) in error_codes {
let error_response = ErrorResponse {
error_code: error_code.to_string(),
message: message.to_string(),
};
let json = serde_json::to_string(&error_response).expect("Test data should be valid");
assert!(json.contains(error_code));
assert!(json.contains(message));
let deserialized: ErrorResponse = serde_json::from_str(&json).expect("Test data should be valid");
assert_eq!(error_response.error_code, deserialized.error_code);
assert_eq!(error_response.message, deserialized.message);
}
}
#[test]
fn test_error_response_edge_cases() {
let empty_error = ErrorResponse {
error_code: String::new(),
message: String::new(),
};
let json = serde_json::to_string(&empty_error).expect("Test data should be valid");
let deserialized: ErrorResponse = serde_json::from_str(&json).expect("Test data should be valid");
assert_eq!(empty_error.error_code, deserialized.error_code);
assert_eq!(empty_error.message, deserialized.message);
let long_error = ErrorResponse {
error_code: "a".repeat(1000),
message: "b".repeat(2000),
};
let json2 = serde_json::to_string(&long_error).expect("Test data should be valid");
let deserialized2: ErrorResponse = serde_json::from_str(&json2).expect("Test data should be valid");
assert_eq!(long_error.error_code, deserialized2.error_code);
assert_eq!(long_error.message, deserialized2.message);
let special_error = ErrorResponse {
error_code: "error_with_numbers_123".to_string(),
message: "Message with special characters and \"quotes\"".to_string(),
};
let json3 = serde_json::to_string(&special_error).expect("Test data should be valid");
let deserialized3: ErrorResponse = serde_json::from_str(&json3).expect("Test data should be valid");
assert_eq!(special_error.error_code, deserialized3.error_code);
assert_eq!(special_error.message, deserialized3.message);
}
#[test]
fn test_error_response_json_structure() {
let error_response = ErrorResponse {
error_code: "test_code".to_string(),
message: "test message".to_string(),
};
let json = serde_json::to_string(&error_response).expect("Test data should be valid");
let parsed: serde_json::Value = serde_json::from_str(&json).expect("Test data should be valid");
assert!(parsed.is_object());
assert!(parsed.get("error_code").is_some());
assert!(parsed.get("message").is_some());
if let Some(error_code) = parsed.get("error_code") {
assert_eq!(error_code.as_str(), Some("test_code"));
}
if let Some(message) = parsed.get("message") {
assert_eq!(message.as_str(), Some("test message"));
}
}
#[derive(Debug, Clone)]
struct ErrorClassificationTestCase {
name: &'static str,
status_code: u16,
error_code: &'static str,
message: &'static str,
expected_assertion: fn(&Error),
}
fn create_test_client() -> Client {
ClientBuilder::new()
.network(Network::Local)
.timeout(Duration::from_secs(5))
.build()
.expect("Test client creation should not fail")
}
fn test_error_classification(test_case: &ErrorClassificationTestCase) {
let client = create_test_client();
let error_response = ErrorResponse {
error_code: test_case.error_code.to_string(),
message: test_case.message.to_string(),
};
let response_body = serde_json::to_string(&error_response).expect("Test ErrorResponse should serialize");
let result = client.test_handle_error_response(test_case.status_code, &response_body);
(test_case.expected_assertion)(&result);
}
fn test_fallback_error_classification(status_code: u16, body: &str, expected_assertion: fn(&Error)) {
let client = create_test_client();
let result = client.test_handle_error_response(status_code, body);
expected_assertion(&result);
}
#[test]
fn test_validation_parameter_errors() {
let test_cases = vec![
ErrorClassificationTestCase {
name: "validation_username",
status_code: 400,
error_code: "validation_username",
message: "Username must be at least 3 characters",
expected_assertion: |error| match error {
Error::InvalidParameter { parameter, message } => {
assert_eq!(parameter, "username");
assert_eq!(message, "Username must be at least 3 characters");
}
_ => panic!("Expected InvalidParameter error, got: {:?}", error),
},
},
ErrorClassificationTestCase {
name: "validation_email",
status_code: 400,
error_code: "validation_email",
message: "Invalid email format",
expected_assertion: |error| match error {
Error::InvalidParameter { parameter, message } => {
assert_eq!(parameter, "email");
assert_eq!(message, "Invalid email format");
}
_ => panic!("Expected InvalidParameter error, got: {:?}", error),
},
},
ErrorClassificationTestCase {
name: "validation_amount",
status_code: 400,
error_code: "validation_amount",
message: "Amount must be positive",
expected_assertion: |error| match error {
Error::InvalidParameter { parameter, message } => {
assert_eq!(parameter, "amount");
assert_eq!(message, "Amount must be positive");
}
_ => panic!("Expected InvalidParameter error, got: {:?}", error),
},
},
ErrorClassificationTestCase {
name: "validation_address",
status_code: 400,
error_code: "validation_address",
message: "Invalid address format",
expected_assertion: |error| match error {
Error::InvalidParameter { parameter, message } => {
assert_eq!(parameter, "address");
assert_eq!(message, "Invalid address format");
}
_ => panic!("Expected InvalidParameter error, got: {:?}", error),
},
},
];
for test_case in test_cases {
println!("Running test case: {}", test_case.name);
test_error_classification(&test_case);
}
}
#[test]
fn test_authentication_errors() {
let test_cases = vec![
ErrorClassificationTestCase {
name: "unauthorized_invalid_token",
status_code: 401,
error_code: "invalid_token",
message: "The provided token is invalid",
expected_assertion: |error| match error {
Error::Authentication(message) => {
assert_eq!(message, "The provided token is invalid");
}
_ => panic!("Expected Authentication error, got: {:?}", error),
},
},
ErrorClassificationTestCase {
name: "unauthorized_expired_token",
status_code: 401,
error_code: "expired_token",
message: "Token has expired",
expected_assertion: |error| match error {
Error::Authentication(message) => {
assert_eq!(message, "Token has expired");
}
_ => panic!("Expected Authentication error, got: {:?}", error),
},
},
];
for test_case in test_cases {
println!("Running test case: {}", test_case.name);
test_error_classification(&test_case);
}
}
#[test]
fn test_authorization_errors() {
let test_cases = vec![
ErrorClassificationTestCase {
name: "forbidden_insufficient_permissions",
status_code: 403,
error_code: "insufficient_permissions",
message: "You do not have permission to access this resource",
expected_assertion: |error| match error {
Error::Authorization(message) => {
assert_eq!(message, "You do not have permission to access this resource");
}
_ => panic!("Expected Authorization error, got: {:?}", error),
},
},
ErrorClassificationTestCase {
name: "forbidden_access_denied",
status_code: 403,
error_code: "access_denied",
message: "Access denied for this operation",
expected_assertion: |error| match error {
Error::Authorization(message) => {
assert_eq!(message, "Access denied for this operation");
}
_ => panic!("Expected Authorization error, got: {:?}", error),
},
},
];
for test_case in test_cases {
println!("Running test case: {}", test_case.name);
test_error_classification(&test_case);
}
}
#[test]
fn test_resource_not_found_errors() {
let test_cases = vec![
ErrorClassificationTestCase {
name: "resource_transaction_not_found",
status_code: 404,
error_code: "resource_transaction",
message: "Transaction with hash 0x123 not found",
expected_assertion: |error| match error {
Error::ResourceNotFound {
resource_type,
identifier,
} => {
assert_eq!(resource_type, "transaction");
assert_eq!(identifier, "Transaction with hash 0x123 not found");
}
_ => panic!("Expected ResourceNotFound error, got: {:?}", error),
},
},
ErrorClassificationTestCase {
name: "resource_account_not_found",
status_code: 404,
error_code: "resource_account",
message: "Account not found",
expected_assertion: |error| match error {
Error::ResourceNotFound {
resource_type,
identifier,
} => {
assert_eq!(resource_type, "account");
assert_eq!(identifier, "Account not found");
}
_ => panic!("Expected ResourceNotFound error, got: {:?}", error),
},
},
];
for test_case in test_cases {
println!("Running test case: {}", test_case.name);
test_error_classification(&test_case);
}
}
#[test]
fn test_business_logic_errors() {
let test_cases = vec![
ErrorClassificationTestCase {
name: "business_transfer_failed",
status_code: 422,
error_code: "business_transfer",
message: "Insufficient balance for transfer",
expected_assertion: |error| match error {
Error::BusinessLogic { operation, reason } => {
assert_eq!(operation, "transfer");
assert_eq!(reason, "Insufficient balance for transfer");
}
_ => panic!("Expected BusinessLogic error, got: {:?}", error),
},
},
ErrorClassificationTestCase {
name: "business_mint_failed",
status_code: 422,
error_code: "business_mint",
message: "Cannot mint to blacklisted address",
expected_assertion: |error| match error {
Error::BusinessLogic { operation, reason } => {
assert_eq!(operation, "mint");
assert_eq!(reason, "Cannot mint to blacklisted address");
}
_ => panic!("Expected BusinessLogic error, got: {:?}", error),
},
},
];
for test_case in test_cases {
println!("Running test case: {}", test_case.name);
test_error_classification(&test_case);
}
}
#[test]
fn test_rate_limit_errors() {
let test_case = ErrorClassificationTestCase {
name: "rate_limit_exceeded_exact_match",
status_code: 429,
error_code: "rate_limit_exceeded",
message: "Too many requests",
expected_assertion: |error| match error {
Error::RateLimitExceeded { retry_after_seconds } => {
assert_eq!(*retry_after_seconds, None);
}
_ => panic!("Expected RateLimitExceeded error, got: {:?}", error),
},
};
test_error_classification(&test_case);
}
#[test]
fn test_server_errors() {
let test_cases = vec![
ErrorClassificationTestCase {
name: "system_database_error",
status_code: 500,
error_code: "system_database",
message: "Database connection failed",
expected_assertion: |error| match error {
Error::HttpTransport { message, status_code } => {
assert_eq!(message, "Database connection failed");
assert_eq!(*status_code, Some(500));
}
_ => panic!("Expected HttpTransport error, got: {:?}", error),
},
},
ErrorClassificationTestCase {
name: "system_service_unavailable",
status_code: 503,
error_code: "system_service",
message: "Service temporarily unavailable",
expected_assertion: |error| match error {
Error::HttpTransport { message, status_code } => {
assert_eq!(message, "Service temporarily unavailable");
assert_eq!(*status_code, Some(503));
}
_ => panic!("Expected HttpTransport error, got: {:?}", error),
},
},
];
for test_case in test_cases {
println!("Running test case: {}", test_case.name);
test_error_classification(&test_case);
}
}
#[test]
fn test_fallback_error_classification_cases() {
test_fallback_error_classification(400, "Invalid request", |error| match error {
Error::InvalidParameter { parameter, message } => {
assert_eq!(parameter, "request");
assert_eq!(message, "Invalid request");
}
_ => panic!("Expected InvalidParameter for 400 fallback, got: {:?}", error),
});
test_fallback_error_classification(401, "Unauthorized access", |error| match error {
Error::Authentication(message) => {
assert_eq!(message, "Unauthorized access");
}
_ => panic!("Expected Authentication for 401 fallback, got: {:?}", error),
});
test_fallback_error_classification(404, "Resource not found", |error| match error {
Error::ResourceNotFound {
resource_type,
identifier,
} => {
assert_eq!(resource_type, "unknown");
assert_eq!(identifier, "Resource not found");
}
_ => panic!("Expected ResourceNotFound for 404 fallback, got: {:?}", error),
});
test_fallback_error_classification(500, "Internal server error", |error| match error {
Error::HttpTransport { message, status_code } => {
assert_eq!(message, "Internal server error");
assert_eq!(*status_code, Some(500));
}
_ => panic!("Expected HttpTransport for 500 fallback, got: {:?}", error),
});
}
#[test]
fn test_error_classification_edge_cases() {
let edge_cases = vec![
ErrorClassificationTestCase {
name: "empty_error_code",
status_code: 400,
error_code: "",
message: "Empty error code",
expected_assertion: |error| match error {
Error::Api {
status_code,
error_code,
message,
} => {
assert_eq!(*status_code, 400);
assert_eq!(error_code, "");
assert_eq!(message, "Empty error code");
}
_ => panic!("Expected Api error for empty error code, got: {:?}", error),
},
},
ErrorClassificationTestCase {
name: "validation_empty_suffix",
status_code: 400,
error_code: "validation_",
message: "Parameter validation failed",
expected_assertion: |error| match error {
Error::InvalidParameter { parameter, message } => {
assert_eq!(parameter, "");
assert_eq!(message, "Parameter validation failed");
}
_ => panic!("Expected InvalidParameter error with empty param, got: {:?}", error),
},
},
ErrorClassificationTestCase {
name: "system_error_range_500",
status_code: 500,
error_code: "system_test",
message: "System error occurred",
expected_assertion: |error| match error {
Error::HttpTransport { message, status_code } => {
assert_eq!(message, "System error occurred");
assert_eq!(*status_code, Some(500));
}
_ => panic!("Expected HttpTransport error, got: {:?}", error),
},
},
];
for test_case in edge_cases {
println!("Running edge case: {}", test_case.name);
test_error_classification(&test_case);
}
}
#[test]
fn test_http_transport_error_status_codes() {
let system_status_codes = [500, 501, 502, 503, 504, 505, 550, 599];
for &status_code in &system_status_codes {
let test_case = ErrorClassificationTestCase {
name: "system_error_boundary",
status_code,
error_code: "system_test",
message: "System error occurred",
expected_assertion: |error| match error {
Error::HttpTransport {
message,
status_code: status,
} => {
assert_eq!(message, "System error occurred");
assert!(status.unwrap() >= 500 && status.unwrap() <= 599);
}
_ => panic!("Expected HttpTransport error for system_ error, got: {:?}", error),
},
};
test_error_classification(&test_case);
}
}
#[test]
fn test_http_client_cross_network_functionality() -> Result<()> {
let networks_and_expected_differences = [
(Network::Mainnet, "mainnet"),
(Network::Testnet, "testnet"),
(Network::Local, "local"),
];
for (network, expected_name) in networks_and_expected_differences {
let client = ClientBuilder::new()
.network(network)
.timeout(Duration::from_secs(30))
.build()?;
let debug_output = format!("{:?}", client);
assert!(debug_output.contains("Client"));
assert!(debug_output.contains("base_url"));
assert!(debug_output.contains("hooks_count: 0"));
println!("Created {} client successfully", expected_name);
}
Ok(())
}
#[test]
fn test_timeout_configuration_integration() {
let timeout_scenarios = [
("very_short", Duration::from_millis(100)),
("short", Duration::from_secs(1)),
("medium", Duration::from_secs(10)),
("long", Duration::from_secs(60)),
("very_long", Duration::from_secs(300)),
];
for (scenario_name, timeout) in timeout_scenarios {
let result = ClientBuilder::new().network(Network::Local).timeout(timeout).build();
assert!(result.is_ok(), "Failed to create client with {} timeout", scenario_name);
let client = result.unwrap();
let debug_str = format!("{:?}", client);
assert!(debug_str.contains("Client"));
println!(
"Successfully created client with {} timeout: {:?}",
scenario_name, timeout
);
}
}