#![allow(clippy::unwrap_used)] #![allow(clippy::cast_precision_loss)] #![allow(clippy::cast_sign_loss)] #![allow(clippy::cast_possible_truncation)] #![allow(clippy::cast_possible_wrap)] #![allow(clippy::cast_lossless)] #![allow(clippy::missing_panics_doc)] #![allow(clippy::missing_errors_doc)] #![allow(missing_docs)] #![allow(clippy::items_after_statements)] #![allow(clippy::used_underscore_binding)] #![allow(clippy::needless_pass_by_value)]
use fraiseql_server::error::{ErrorCode, ErrorResponse, GraphQLError};
fn sql_error_with_details() -> GraphQLError {
GraphQLError::database(
"SQL syntax error near line 42: SELECT * FROM users WHERE id = $1 LIMIT 10".to_string(),
)
}
fn generic_database_error() -> GraphQLError {
GraphQLError::database("Database error occurred")
}
fn auth_error_with_token_details() -> GraphQLError {
GraphQLError::new(
"Bearer token validation failed: jwt.ErrSignatureInvalid at offset 256",
ErrorCode::Unauthenticated,
)
}
fn generic_auth_error() -> GraphQLError {
GraphQLError::unauthenticated()
}
#[test]
fn test_error_code_mapping_to_http_status() {
assert_eq!(
ErrorCode::DatabaseError.status_code(),
axum::http::StatusCode::INTERNAL_SERVER_ERROR
);
assert_eq!(ErrorCode::Unauthenticated.status_code(), axum::http::StatusCode::UNAUTHORIZED);
assert_eq!(ErrorCode::ValidationError.status_code(), axum::http::StatusCode::OK);
assert_eq!(ErrorCode::Forbidden.status_code(), axum::http::StatusCode::FORBIDDEN);
}
#[test]
fn test_graphql_error_message_contains_details() {
let error = sql_error_with_details();
assert_eq!(error.code, ErrorCode::DatabaseError);
assert!(error.message.contains("SQL syntax error"));
assert!(error.message.contains("SELECT"));
assert!(error.message.contains("LIMIT"));
}
#[test]
fn test_generic_error_message_no_details() {
let error = generic_database_error();
assert_eq!(error.code, ErrorCode::DatabaseError);
assert!(error.message.contains("Database error"));
assert!(!error.message.contains("SELECT"));
assert!(!error.message.contains("SQL"));
}
#[test]
fn test_auth_error_with_token_information() {
let error = auth_error_with_token_details();
assert_eq!(error.code, ErrorCode::Unauthenticated);
assert!(error.message.contains("Bearer token"));
assert!(error.message.contains("jwt"));
assert!(error.message.contains("offset"));
}
#[test]
fn test_auth_error_generic_message() {
let error = generic_auth_error();
assert_eq!(error.code, ErrorCode::Unauthenticated);
assert_eq!(error.message, "Authentication required");
assert!(!error.message.contains("Bearer"));
assert!(!error.message.contains("jwt"));
}
#[test]
fn test_error_response_serialization() {
let error = sql_error_with_details();
let response = ErrorResponse::from_error(error);
assert_eq!(response.errors.len(), 1);
assert_eq!(response.errors[0].code, ErrorCode::DatabaseError);
}
#[test]
fn test_multiple_errors_response() {
let error1 = generic_database_error();
let error2 = generic_auth_error();
let response = ErrorResponse::new(vec![error1, error2]);
assert_eq!(response.errors.len(), 2);
assert_eq!(response.errors[0].code, ErrorCode::DatabaseError);
assert_eq!(response.errors[1].code, ErrorCode::Unauthenticated);
}
#[test]
fn test_error_with_extensions() {
let error = generic_database_error()
.with_request_id("req-123")
.with_location(42, 10)
.with_path(vec!["user".to_string(), "profile".to_string()]);
assert!(error.extensions.is_some());
let ext = error.extensions.unwrap();
assert_eq!(ext.request_id, Some("req-123".to_string()));
assert!(error.locations.is_some());
assert!(error.path.is_some());
}
#[test]
fn test_validation_error_message_pattern() {
let error = GraphQLError::validation("Query exceeds maximum depth: 12 > 10");
assert_eq!(error.code, ErrorCode::ValidationError);
assert!(error.message.contains("Query exceeds"));
assert!(error.message.contains("depth"));
assert!(!error.message.contains("SELECT"));
assert!(!error.message.contains("token"));
}
#[test]
fn test_timeout_error_redaction() {
let error = GraphQLError::timeout("database_query");
assert_eq!(error.code, ErrorCode::Timeout);
assert!(error.message.contains("exceeded timeout"));
assert!(error.message.contains("database_query"));
}
#[test]
fn test_rate_limit_error_message() {
let error = GraphQLError::rate_limited("Too many requests from 192.168.1.1");
assert_eq!(error.code, ErrorCode::RateLimitExceeded);
assert!(error.message.contains("Too many requests"));
}
#[test]
fn test_forbidden_error_no_internal_details() {
let error = GraphQLError::forbidden();
assert_eq!(error.code, ErrorCode::Forbidden);
assert_eq!(error.message, "Access denied");
assert!(!error.message.contains("admin"));
assert!(!error.message.contains("field"));
}
#[test]
fn test_not_found_error_specificity() {
let error = GraphQLError::not_found("User with ID 123 not found");
assert_eq!(error.code, ErrorCode::NotFound);
assert!(error.message.contains("not found"));
}
#[test]
fn test_internal_server_error_redaction() {
let error = GraphQLError::internal("Unexpected: index out of bounds");
assert_eq!(error.code, ErrorCode::InternalServerError);
assert!(error.message.contains("Unexpected"));
}
#[test]
fn test_parse_error_can_include_hint() {
let error = GraphQLError::parse("Unexpected token } at line 5");
assert_eq!(error.code, ErrorCode::ParseError);
assert!(error.message.contains("Unexpected token"));
assert!(error.message.contains("line"));
assert!(!error.message.contains("SELECT"));
}
#[test]
fn test_error_builder_pattern() {
let error = GraphQLError::new("Something went wrong", ErrorCode::InternalServerError)
.with_request_id("trace-456")
.with_location(10, 15);
assert_eq!(error.code, ErrorCode::InternalServerError);
assert!(error.extensions.is_some());
assert!(error.locations.is_some());
}
#[test]
fn test_sensitive_field_names_not_in_errors() {
let error = GraphQLError::forbidden();
assert!(!error.message.contains("password"));
assert!(!error.message.contains("ssn"));
assert!(!error.message.contains("credit_card"));
assert!(!error.message.contains("api_key"));
}
#[test]
fn test_error_response_with_request_id_tracking() {
let error1 = generic_database_error().with_request_id("request-001");
let error2 = generic_auth_error().with_request_id("request-001");
let response = ErrorResponse::new(vec![error1, error2]);
assert_eq!(response.errors.len(), 2);
for error in &response.errors {
if let Some(extensions) = &error.extensions {
assert_eq!(
extensions.request_id,
Some("request-001".to_string()),
"All errors should share request ID"
);
}
}
}
#[test]
fn test_database_error_variations() {
let connection_error = GraphQLError::database("Failed to connect to database");
let query_error = GraphQLError::database("Query execution failed");
let timeout_error = GraphQLError::timeout("database_query");
assert_eq!(connection_error.code, ErrorCode::DatabaseError);
assert_eq!(query_error.code, ErrorCode::DatabaseError);
assert_eq!(timeout_error.code, ErrorCode::Timeout);
assert!(!connection_error.message.contains("SELECT"));
assert!(!query_error.message.contains("SELECT"));
}
#[test]
fn test_validation_error_includes_limits() {
let depth_error = GraphQLError::validation("Query exceeds maximum depth: 15 > 10");
let complexity_error =
GraphQLError::validation("Query exceeds maximum complexity: 1500 > 1000");
assert!(depth_error.message.contains("15"));
assert!(depth_error.message.contains("10"));
assert!(complexity_error.message.contains("1500"));
assert!(complexity_error.message.contains("1000"));
}
#[test]
fn test_error_path_for_field_errors() {
let error = GraphQLError::execution("Field 'email' failed validation")
.with_path(vec!["user".to_string(), "email".to_string()]);
assert!(error.path.is_some());
let path = error.path.unwrap();
assert_eq!(path.len(), 2);
assert_eq!(path[0], "user");
assert_eq!(path[1], "email");
}
#[test]
fn test_error_equality_by_code() {
let error1 = GraphQLError::forbidden();
let error2 = GraphQLError::forbidden();
assert_eq!(error1.code, error2.code);
assert_eq!(error1.code, ErrorCode::Forbidden);
}
#[test]
fn test_error_message_length_limits() {
let error = GraphQLError::database("Short error");
assert!(error.message.len() < 10000, "Error message should be bounded");
}
#[test]
fn test_request_error_for_malformed_input() {
let error = GraphQLError::request("Invalid JSON in request body");
assert_eq!(error.code, ErrorCode::RequestError);
assert!(error.message.contains("Invalid JSON"));
assert!(!error.message.contains('{'));
assert!(!error.message.contains('}'));
}