lxy 0.1.1

A convenient async http and RPC framework in Rust
Documentation
use super::*;
use crate::define_error;
use serde_json::json;

#[test]
fn test_error_creation() {
  let err = Error::new(ErrorCode::InvalidInput, "invalid_email", "Email is invalid");
  assert_eq!(err.code(), ErrorCode::InvalidInput);
  assert_eq!(err.error_type(), "invalid_email");
  assert_eq!(err.message(), "Email is invalid");
  assert!(err.data().is_none());
}

#[test]
fn test_with_data() {
  let err = Error::new(ErrorCode::RateLimited, "rate_limit", "Too many requests")
    .with_data(json!({"retry_after": 60}));

  assert!(err.data().is_some());
  assert_eq!(err.data().unwrap()["retry_after"], 60);
}

#[test]
fn test_error_internal() {
  use std::io;

  let io_error = io::Error::new(io::ErrorKind::NotFound, "file not found");
  let err = Error::internal(io_error);

  assert_eq!(err.code(), ErrorCode::Internal);
  assert_eq!(err.error_type(), "internal_error");
  assert_eq!(err.message(), "file not found");
  assert!(err.source().is_some());
}

#[test]
fn test_typed_error() {
  define_error!(ErrorCode::InvalidInput, TestError);

  let err = TestError::error("Test message");
  assert_eq!(err.code(), ErrorCode::InvalidInput);
  assert_eq!(err.error_type(), "TestError");
  assert_eq!(err.message(), "Test message");
}

#[test]
fn test_typed_with_data() {
  define_error!(ErrorCode::ResourceNotFound, TestErrorWithData);

  let err = TestErrorWithData::error_with_data("Not found", json!({"id": 123}));
  assert_eq!(err.code(), ErrorCode::ResourceNotFound);
  assert_eq!(err.error_type(), "TestErrorWithData");
  assert_eq!(err.message(), "Not found");
  assert!(err.data().is_some());
  assert_eq!(err.data().unwrap()["id"], 123);
}

#[test]
fn test_error_code_display() {
  assert_eq!(ErrorCode::InvalidInput.to_string(), "InvalidInput");
  assert_eq!(ErrorCode::Unauthorized.to_string(), "Unauthorized");
  assert_eq!(ErrorCode::ResourceNotFound.to_string(), "ResourceNotFound");
}

#[test]
fn test_error_display() {
  let err = Error::new(ErrorCode::InvalidInput, "invalid_email", "Email is invalid");
  let display = format!("{}", err);
  assert!(display.contains("invalid_email"));
  assert!(display.contains("InvalidInput"));
  assert!(display.contains("Email is invalid"));
}

#[test]
fn test_error_serialization() {
  let err = Error::new(ErrorCode::InvalidInput, "invalid_email", "Email is invalid")
    .with_data(json!({"field": "email"}));

  let serialized = serde_json::to_string(&err).unwrap();
  let value: serde_json::Value = serde_json::from_str(&serialized).unwrap();

  assert_eq!(value["type"], "invalid_email");
  assert_eq!(value["message"], "Email is invalid");
  assert_eq!(value["data"]["field"], "email");
}

#[test]
fn test_error_internal_conversion() {
  use std::io;

  fn returns_io_error() -> std::io::Result<String> {
    Err(io::Error::new(io::ErrorKind::NotFound, "file not found"))
  }

  fn uses_lxy_result() -> Result<String> {
    let content = returns_io_error().map_err(Error::internal)?;
    Ok(content)
  }

  let result = uses_lxy_result();
  assert!(result.is_err());

  let err = result.unwrap_err();
  assert_eq!(err.code(), ErrorCode::Internal);
  assert_eq!(err.error_type(), "internal_error");
}

#[cfg(debug_assertions)]
#[test]
fn test_backtrace_captured() {
  let err = Error::new(ErrorCode::Internal, "test_error", "Error with backtrace");
  assert!(err.backtrace().is_some());
}

#[test]
fn test_define_errors_macro() {
  use crate::define_errors;

  define_errors! {
    ErrorCode::ResourceNotFound => [
      TestUserNotFound,
      TestProjectNotFound,
    ],
    ErrorCode::InvalidInput => [
      TestInvalidEmail,
    ],
  }

  let err: Error = TestUserNotFound.into();
  assert_eq!(err.code(), ErrorCode::ResourceNotFound);
  assert_eq!(err.error_type(), "TestUserNotFound");

  let err: Error = TestProjectNotFound.into();
  assert_eq!(err.code(), ErrorCode::ResourceNotFound);
  assert_eq!(err.error_type(), "TestProjectNotFound");

  let err: Error = TestInvalidEmail.into();
  assert_eq!(err.code(), ErrorCode::InvalidInput);
  assert_eq!(err.error_type(), "TestInvalidEmail");
}

#[test]
fn test_define_errors_with_message() {
  use crate::define_errors;

  define_errors! {
    ErrorCode::ResourceNotFound => [
      TestUser2NotFound,
    ],
  }

  let err = TestUser2NotFound::error("User 123 not found");
  assert_eq!(err.code(), ErrorCode::ResourceNotFound);
  assert_eq!(err.error_type(), "TestUser2NotFound");
  assert_eq!(err.message(), "User 123 not found");
}

#[test]
fn test_define_errors_with_data() {
  use crate::define_errors;

  define_errors! {
    ErrorCode::RateLimited => [
      TestRateLimit,
    ],
  }

  let err = TestRateLimit::error_with_data("Too many requests", json!({"retry_after": 60}));
  assert_eq!(err.code(), ErrorCode::RateLimited);
  assert_eq!(err.error_type(), "TestRateLimit");
  assert!(err.data().is_some());
  assert_eq!(err.data().unwrap()["retry_after"], 60);
}

#[test]
fn test_define_error_with_from() {
  use crate::define_error;

  define_error!(ErrorCode::InvalidInput, TestCustomError);

  let err: Error = TestCustomError.into();
  assert_eq!(err.code(), ErrorCode::InvalidInput);
  assert_eq!(err.error_type(), "TestCustomError");
}

#[test]
fn test_with_source() {
  use std::io;

  let io_error = io::Error::new(io::ErrorKind::NotFound, "file not found");
  let err = Error::new(
    ErrorCode::Internal,
    "config_load_failed",
    "Failed to load config",
  )
  .with_source(io_error);

  assert_eq!(err.code(), ErrorCode::Internal);
  assert_eq!(err.error_type(), "config_load_failed");
  assert_eq!(err.message(), "Failed to load config");
  assert!(err.source().is_some());

  let source = err.source().unwrap();
  assert_eq!(source.to_string(), "file not found");
}

#[test]
fn test_typed_with_source() {
  use crate::define_error;
  use std::io;

  define_error!(ErrorCode::Internal, ConfigLoadFailed);

  let io_error = io::Error::new(io::ErrorKind::PermissionDenied, "permission denied");
  let err = ConfigLoadFailed::error_with_source("Failed to load configuration", io_error);

  assert_eq!(err.code(), ErrorCode::Internal);
  assert_eq!(err.error_type(), "ConfigLoadFailed");
  assert_eq!(err.message(), "Failed to load configuration");
  assert!(err.source().is_some());

  let source = err.source().unwrap();
  assert_eq!(source.to_string(), "permission denied");
}

#[test]
fn test_error_chain() {
  use std::io;

  let io_error = io::Error::new(io::ErrorKind::NotFound, "file.txt not found");
  let mid_error = Error::new(
    ErrorCode::Internal,
    "file_read_failed",
    "Failed to read file",
  )
  .with_source(io_error);

  assert!(mid_error.source().is_some());
  let source = mid_error.source().unwrap();
  assert_eq!(source.to_string(), "file.txt not found");
}

#[test]
fn test_std_error_source() {
  use std::error::Error as StdError;
  use std::io;

  let io_error = io::Error::new(io::ErrorKind::NotFound, "config.toml");
  let err = Error::internal(io_error);

  let source = StdError::source(&err);
  assert!(source.is_some());
  assert_eq!(source.unwrap().to_string(), "config.toml");
}