use std::{array::TryFromSliceError, error::Error as StdError, str::FromStr, time::Duration};
use alloy_primitives::Address;
use om_rest_types::crypto::CryptoError;
use onemoney_protocol::{Network, client::builder::ClientBuilder, error::*};
#[test]
fn test_error_display() {
let errors = [
Error::address("test"),
Error::custom("test"),
Error::validation("field", "test"),
];
for error in &errors {
let display_str = format!("{}", error);
let debug_str = format!("{:?}", error);
assert!(!display_str.is_empty(), "Error display should not be empty");
assert!(!debug_str.is_empty(), "Error debug should not be empty");
println!("Error display: {}", display_str);
println!("Error debug: {}", debug_str);
}
}
#[test]
fn test_error_source() {
let base_error = std::io::Error::other("test error");
let errors = [
Error::custom(format!("HTTP error: {}", base_error)),
Error::custom(format!("Serialization error: {}", base_error)),
];
for error in &errors {
let _source = error.source();
let error_msg = format!("{}", error);
let boxed: Box<dyn StdError> = Box::new(Error::custom(error_msg));
let _display = format!("{}", boxed);
}
}
#[test]
fn test_result_type() {
let success: Result<i32> = Ok(42);
assert!(success.is_ok());
if let Ok(value) = success {
assert_eq!(value, 42);
}
let error: Result<i32> = Err(Error::validation("field", "test"));
assert!(error.is_err());
}
#[test]
fn test_error_propagation() -> Result<()> {
fn inner_function() -> Result<i32> {
Err(Error::validation("field", "inner error"))
}
fn outer_function() -> Result<String> {
let value = inner_function()?; Ok(format!("Value: {}", value))
}
let result = outer_function();
assert!(result.is_err());
match result {
Err(Error::Validation { field: _, message }) => {
assert_eq!(message, "inner error");
}
_ => panic!("Expected ValidationError"),
}
Ok(())
}
#[test]
fn test_error_api_creation() {
let error = Error::api(404, "NOT_FOUND".to_string(), "Resource not found".to_string());
assert!(error.is_api_error());
assert_eq!(error.status_code(), Some(404));
assert_eq!(error.error_code(), Some("NOT_FOUND"));
assert!(format!("{}", error).contains("404"));
assert!(format!("{}", error).contains("NOT_FOUND"));
assert!(format!("{}", error).contains("Resource not found"));
}
#[test]
fn test_error_address_creation() {
let error = Error::address("Invalid checksum");
assert!(format!("{}", error).contains("Invalid checksum"));
assert!(!error.is_api_error());
assert!(!error.is_config_error());
assert!(!error.is_crypto_error());
}
#[test]
fn test_error_array_conversion() {
let error = Error::array_conversion(32, 16);
assert!(format!("{}", error).contains("expected length 32"));
assert!(format!("{}", error).contains("got 16"));
}
#[test]
fn test_error_validation_creation() {
let error = Error::validation("amount", "must be positive");
assert!(format!("{}", error).contains("amount"));
assert!(format!("{}", error).contains("must be positive"));
}
#[test]
fn test_error_custom_creation() {
let error = Error::custom("Something went wrong");
assert!(format!("{}", error).contains("Something went wrong"));
}
#[test]
fn test_error_http_transport() {
let error = Error::http_transport("Connection refused", Some(500));
assert!(format!("{}", error).contains("Connection refused"));
}
#[test]
fn test_error_request_timeout() {
let error = Error::request_timeout("/api/test", 5000);
assert!(format!("{}", error).contains("/api/test"));
assert!(format!("{}", error).contains("5000ms"));
}
#[test]
fn test_error_connection() {
let error = Error::connection("Network unreachable");
assert!(format!("{}", error).contains("Network unreachable"));
}
#[test]
fn test_error_dns_resolution() {
let error = Error::dns_resolution("Host not found");
assert!(format!("{}", error).contains("Host not found"));
}
#[test]
fn test_error_response_deserialization() {
let error = Error::response_deserialization("JSON", "Invalid syntax", "{broken");
assert!(format!("{}", error).contains("JSON"));
assert!(format!("{}", error).contains("Invalid syntax"));
assert!(format!("{}", error).contains("{broken"));
}
#[test]
fn test_error_authentication() {
let error = Error::authentication("Invalid credentials");
assert!(format!("{}", error).contains("Invalid credentials"));
}
#[test]
fn test_error_authorization() {
let error = Error::authorization("Insufficient permissions");
assert!(format!("{}", error).contains("Insufficient permissions"));
}
#[test]
fn test_error_rate_limit_exceeded() {
let error = Error::rate_limit_exceeded(Some(60));
assert!(format!("{}", error).contains("Rate limit exceeded"));
let error2 = Error::rate_limit_exceeded(None);
assert!(format!("{}", error2).contains("Rate limit exceeded"));
}
#[test]
fn test_error_invalid_parameter() {
let error = Error::invalid_parameter("timeout", "must be positive");
assert!(format!("{}", error).contains("timeout"));
assert!(format!("{}", error).contains("must be positive"));
}
#[test]
fn test_error_resource_not_found() {
let error = Error::resource_not_found("transaction", "0x123abc");
assert!(format!("{}", error).contains("transaction"));
assert!(format!("{}", error).contains("0x123abc"));
}
#[test]
fn test_error_business_logic() {
let error = Error::business_logic("transfer", "insufficient balance");
assert!(format!("{}", error).contains("transfer"));
assert!(format!("{}", error).contains("insufficient balance"));
}
#[test]
fn test_crypto_error_creation() {
let error = CryptoError::invalid_private_key("Wrong length");
assert!(format!("{}", error).contains("Wrong length"));
let error = CryptoError::invalid_public_key("Invalid format");
assert!(format!("{}", error).contains("Invalid format"));
let error = CryptoError::signature_failed("Key not found");
assert!(format!("{}", error).contains("Key not found"));
let error = CryptoError::verification_failed("Signature mismatch");
assert!(format!("{}", error).contains("Signature mismatch"));
let error = CryptoError::key_derivation("Derivation failed");
assert!(format!("{}", error).contains("Derivation failed"));
}
#[test]
fn test_error_from_crypto_error() {
let crypto_error = CryptoError::invalid_private_key("test");
let error: Error = crypto_error.into();
assert!(error.is_crypto_error());
assert!(format!("{}", error).contains("test"));
}
#[test]
fn test_config_error_creation() {
let error = ConfigError::invalid_timeout("negative value");
assert!(format!("{}", error).contains("negative value"));
let error = ConfigError::invalid_network("unknown network");
assert!(format!("{}", error).contains("unknown network"));
let error = ConfigError::missing_config("API key required");
assert!(format!("{}", error).contains("API key required"));
let error = ConfigError::client_builder("TLS error");
assert!(format!("{}", error).contains("TLS error"));
}
#[test]
fn test_error_from_config_error() {
let config_error = ConfigError::invalid_timeout("test");
let error: Error = config_error.into();
assert!(error.is_config_error());
assert!(format!("{}", error).contains("test"));
}
#[test]
fn test_error_from_conversions() {
let json_err = serde_json::from_str::<serde_json::Value>("{invalid").unwrap_err();
let error: Error = json_err.into();
assert!(format!("{}", error).contains("JSON parsing failed"));
let url_err = url::Url::parse("not-a-url").unwrap_err();
let error: Error = url_err.into();
assert!(format!("{}", error).contains("Invalid URL"));
let hex_err = hex::decode("not-hex").unwrap_err();
let error: Error = hex_err.into();
assert!(format!("{}", error).contains("Hex decoding failed"));
}
#[test]
fn test_error_from_try_from_slice_error() {
let data: [u8; 5] = [1, 2, 3, 4, 5];
let result: std::result::Result<[u8; 32], TryFromSliceError> = data.as_slice().try_into();
let slice_error = result.unwrap_err();
let error: Error = slice_error.into();
assert!(format!("{}", error).contains("Array conversion failed"));
assert!(format!("{}", error).contains("expected length 32"));
}
#[test]
fn test_address_parsing_errors() {
let invalid_addresses = [
"", "0x123", "0x1234567890abcdef1234567890abcdef123456789", "0xGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG", "not_a_hex_string", ];
for invalid_addr in &invalid_addresses {
let result = Address::from_str(invalid_addr);
assert!(result.is_err(), "Address '{}' should be invalid", invalid_addr);
if let Err(e) = result {
println!("Address '{}' error: {}", invalid_addr, e);
let error_msg = format!("{}", e);
assert!(!error_msg.is_empty());
}
}
}
#[test]
fn test_error_categories() {
let validation_error = Error::validation("field", "validation failed");
let crypto_error = Error::custom("crypto failed");
let http_error = Error::custom("http failed");
match validation_error {
Error::Validation { .. } => {} _ => panic!("Should be Validation"),
}
match crypto_error {
Error::Custom(_) => {} _ => panic!("Should be Custom"),
}
match http_error {
Error::Custom(_) => {} _ => panic!("Should be Custom"),
}
}
#[test]
fn test_error_type_checks() {
let api_error = Error::api(404, "NOT_FOUND".to_string(), "Resource not found".to_string());
assert!(api_error.is_api_error());
assert!(!api_error.is_config_error());
assert!(!api_error.is_crypto_error());
let crypto_error: Error = CryptoError::invalid_private_key("test").into();
assert!(!crypto_error.is_api_error());
assert!(!crypto_error.is_config_error());
assert!(crypto_error.is_crypto_error());
let config_error: Error = ConfigError::invalid_timeout("test").into();
assert!(!config_error.is_api_error());
assert!(config_error.is_config_error());
assert!(!config_error.is_crypto_error());
}
#[test]
fn test_error_status_code_methods() {
let api_error = Error::api(500, "SERVER_ERROR".to_string(), "Internal error".to_string());
assert_eq!(api_error.status_code(), Some(500));
assert_eq!(api_error.error_code(), Some("SERVER_ERROR"));
let custom_error = Error::custom("Not an API error");
assert_eq!(custom_error.status_code(), None);
assert_eq!(custom_error.error_code(), None);
}
#[test]
fn test_error_messages_are_helpful() {
let errors = [
Error::address("0x123"),
Error::custom("invalid private key: abc"),
Error::validation("field", "is required"),
Error::custom("signature verification failed"),
];
for error in &errors {
let message = format!("{}", error);
assert!(message.len() > 10, "Error message should be descriptive");
assert!(!message.eq_ignore_ascii_case("error"));
assert!(!message.eq_ignore_ascii_case("failed"));
println!("Error message: {}", message);
}
}
#[test]
fn test_error_debug_contains_variant() {
let errors = [
Error::address("test"),
Error::custom("invalid private key: test"),
Error::validation("field", "test"),
Error::custom("crypto: test"),
Error::custom("http: test"),
];
for error in &errors {
let debug_str = format!("{:?}", error);
assert!(
debug_str.contains("Address")
|| debug_str.contains("Custom")
|| debug_str.contains("Validation")
|| debug_str.contains("Crypto")
|| debug_str.contains("Http"),
"Debug string should contain variant name: {}",
debug_str
);
}
}
#[test]
fn test_error_response_serialization() {
let error_response = ErrorResponse {
error_code: "INVALID_INPUT".to_string(),
message: "The provided input is invalid".to_string(),
};
let json = serde_json::to_string(&error_response).expect("Test data should be valid");
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_constructors_heterogeneous_types() {
let format_str = "JSON";
let error_string = String::from("Parse error occurred");
let response_data = "{\"invalid\": syntax}";
let error = Error::response_deserialization(format_str, error_string, response_data);
let error_display = format!("{}", error);
assert!(error_display.contains("JSON"));
assert!(error_display.contains("Parse error occurred"));
assert!(error_display.contains("{\"invalid\": syntax}"));
let param_name = "timeout";
let message = format!("Value must be between {} and {}", 1, 3600);
let error = Error::invalid_parameter(param_name, message);
let error_display = format!("{}", error);
assert!(error_display.contains("timeout"));
assert!(error_display.contains("Value must be between 1 and 3600"));
let resource_type = String::from("Transaction");
let identifier = "0x1234abcd";
let error = Error::resource_not_found(resource_type, identifier);
let error_display = format!("{}", error);
assert!(error_display.contains("Transaction"));
assert!(error_display.contains("0x1234abcd"));
let operation = "token_transfer";
let reason = format!("Insufficient balance: {} < {}", 100, 500);
let error = Error::business_logic(operation, reason);
let error_display = format!("{}", error);
assert!(error_display.contains("token_transfer"));
assert!(error_display.contains("Insufficient balance: 100 < 500"));
}
#[test]
fn test_error_send_sync() {
fn assert_send_sync<T: Send + Sync>() {}
assert_send_sync::<Error>();
assert_send_sync::<Result<()>>();
let error = Error::validation("field", "test");
let error_msg = format!("{}", error);
std::thread::spawn(move || {
println!("Error in thread: {}", error_msg);
})
.join()
.expect("Test data should be valid");
}
#[test]
fn test_error_types() {
let validation_error = Error::validation("field", "test");
let custom_error = Error::custom("test");
let address_error = Error::address("invalid");
let _ = format!("{}", validation_error);
let _ = format!("{}", custom_error);
let _ = format!("{}", address_error);
let _ = format!("{:?}", validation_error);
let _ = format!("{:?}", custom_error);
let _ = format!("{:?}", address_error);
}
#[tokio::test]
async fn test_api_error_responses() {
let client = ClientBuilder::new()
.network(Network::Custom("http://httpbin.org/status/404".into())) .timeout(Duration::from_secs(5))
.build()
.expect("Client should build");
let result = client.fetch_chain_id_from_network().await;
assert!(result.is_err(), "Should fail");
}
#[cfg(test)]
mod reqwest_error_tests {
use super::*;
#[test]
fn test_reqwest_error_conversion_with_mock() {
let client = reqwest::Client::new();
let response = tokio_test::block_on(async {
client
.get("http://httpbin.org/delay/10")
.timeout(std::time::Duration::from_millis(1))
.send()
.await
});
if let Err(reqwest_error) = response {
let error: Error = reqwest_error.into();
let error_str = format!("{}", error);
assert!(error_str.contains("timeout") || error_str.contains("Request"));
}
}
}
#[test]
fn test_error_chain_integration() {
fn level_3() -> Result<String> {
Err(Error::validation("input", "invalid format"))
}
fn level_2() -> Result<String> {
level_3().map_err(|e| Error::custom(format!("Level 2 processing failed: {}", e)))
}
fn level_1() -> Result<String> {
level_2().map_err(|e| Error::custom(format!("Level 1 operation failed: {}", e)))
}
let result = level_1();
assert!(result.is_err());
match result {
Err(e) => {
let error_message = format!("{}", e);
assert!(error_message.contains("Level 1 operation failed"));
assert!(error_message.contains("Level 2 processing failed"));
assert!(error_message.contains("invalid format"));
println!("Chained error message: {}", error_message);
}
Ok(_) => panic!("Expected chained error"),
}
}
#[test]
fn test_error_context_preservation() {
let original_message = "original context information";
let validation_error = Error::validation("field", original_message);
let custom_error = Error::custom(format!("Wrapper: {}", validation_error));
let final_error = Error::custom(format!("Final: {}", custom_error));
let final_message = format!("{}", final_error);
assert!(final_message.contains("original context information"));
assert!(final_message.contains("Wrapper"));
assert!(final_message.contains("Final"));
println!("Context preserved: {}", final_message);
}
#[test]
fn test_cross_error_type_compatibility() {
let errors: Vec<Error> = vec![
Error::api(404, "NOT_FOUND".to_string(), "Resource not found".to_string()),
CryptoError::invalid_private_key("Invalid key").into(),
ConfigError::invalid_timeout("Negative timeout").into(),
Error::validation("amount", "must be positive"),
Error::custom("Generic error"),
];
for (i, error) in errors.iter().enumerate() {
let display_str = format!("{}", error);
let debug_str = format!("{:?}", error);
assert!(!display_str.is_empty(), "Error {} display should not be empty", i);
assert!(!debug_str.is_empty(), "Error {} debug should not be empty", i);
let _source = error.source();
println!("Error {}: {}", i, display_str);
}
let error_messages: Vec<String> = errors.iter().map(|e| format!("{}", e)).collect();
assert_eq!(error_messages.len(), 5);
for message in error_messages {
assert!(!message.is_empty());
assert!(message.len() > 5); }
}