#![cfg(test)]
use axum::http::StatusCode;
use fraiseql_server::error::{ErrorCode, ErrorExtensions, GraphQLError};
#[test]
fn test_example_basic_error_handling() {
let error = GraphQLError::validation("Field 'email' is required for mutation 'createUser'");
assert_eq!(error.code, ErrorCode::ValidationError);
assert_eq!(error.message, "Field 'email' is required for mutation 'createUser'");
assert_eq!(error.code.status_code(), StatusCode::BAD_REQUEST);
}
#[test]
fn test_example_parse_error_with_location() {
let error =
GraphQLError::parse("Unexpected token '}' at line 1, column 15").with_location(1, 15);
assert_eq!(error.code, ErrorCode::ParseError);
assert!(error.locations.is_some());
let locations = error.locations.unwrap();
assert_eq!(locations[0].line, 1);
assert_eq!(locations[0].column, 15);
}
#[test]
fn test_example_field_error_with_path() {
let error = GraphQLError::validation("Cannot query field 'unknownField' on type 'User'")
.with_path(vec!["user".to_string(), "profile".to_string()])
.with_location(5, 10);
assert_eq!(error.code, ErrorCode::ValidationError);
assert!(error.path.is_some());
let path = error.path.unwrap();
assert_eq!(path.len(), 2);
assert_eq!(path[0], "user");
assert_eq!(path[1], "profile");
}
#[test]
fn test_example_authentication_error() {
let error = GraphQLError::unauthenticated();
assert_eq!(error.code, ErrorCode::Unauthenticated);
assert_eq!(error.code.status_code(), StatusCode::UNAUTHORIZED);
assert_eq!(error.message, "Authentication required");
}
#[test]
fn test_example_authorization_error() {
let error = GraphQLError::forbidden();
assert_eq!(error.code, ErrorCode::Forbidden);
assert_eq!(error.code.status_code(), StatusCode::FORBIDDEN);
assert_eq!(error.message, "Access denied");
}
#[test]
fn test_example_database_error() {
let error = GraphQLError::database("Connection timeout: unable to reach database server");
assert_eq!(error.code, ErrorCode::DatabaseError);
assert_eq!(error.code.status_code(), StatusCode::INTERNAL_SERVER_ERROR);
}
#[test]
fn test_example_timeout_error() {
let error = GraphQLError::timeout("Query execution");
assert_eq!(error.code, ErrorCode::Timeout);
assert_eq!(error.code.status_code(), StatusCode::REQUEST_TIMEOUT);
assert!(error.message.contains("exceeded timeout"));
}
#[test]
fn test_example_rate_limit_error() {
let error = GraphQLError::rate_limited("Exceeded 1000 requests per minute");
assert_eq!(error.code, ErrorCode::RateLimitExceeded);
assert_eq!(error.code.status_code(), StatusCode::TOO_MANY_REQUESTS);
}
#[test]
fn test_example_request_id_tracing() {
let error = GraphQLError::database("Connection error").with_request_id("req-abc-123-xyz");
assert!(error.extensions.is_some());
let ext = error.extensions.unwrap();
assert_eq!(ext.request_id, Some("req-abc-123-xyz".to_string()));
}
#[test]
fn test_example_error_with_extensions() {
let extensions = ErrorExtensions {
category: Some("DATABASE".to_string()),
status: Some(500),
request_id: Some("req-db-001".to_string()),
};
let error = GraphQLError::database("Connection pool exhausted").with_extensions(extensions);
assert!(error.extensions.is_some());
let ext = error.extensions.unwrap();
assert_eq!(ext.category, Some("DATABASE".to_string()));
assert_eq!(ext.status, Some(500));
assert_eq!(ext.request_id, Some("req-db-001".to_string()));
}
#[test]
fn test_example_error_chaining() {
let error = GraphQLError::validation("Argument type mismatch")
.with_location(2, 5)
.with_path(vec!["user".to_string(), "age".to_string()])
.with_request_id("req-val-456");
assert_eq!(error.code, ErrorCode::ValidationError);
assert!(error.locations.is_some());
assert!(error.path.is_some());
assert!(error.extensions.is_some());
}
#[test]
fn test_example_error_code_status_mapping() {
struct ErrorMapping {
code: ErrorCode,
status: StatusCode,
}
let mappings = [
ErrorMapping {
code: ErrorCode::ValidationError,
status: StatusCode::BAD_REQUEST,
},
ErrorMapping {
code: ErrorCode::ParseError,
status: StatusCode::BAD_REQUEST,
},
ErrorMapping {
code: ErrorCode::RequestError,
status: StatusCode::BAD_REQUEST,
},
ErrorMapping {
code: ErrorCode::Unauthenticated,
status: StatusCode::UNAUTHORIZED,
},
ErrorMapping {
code: ErrorCode::Forbidden,
status: StatusCode::FORBIDDEN,
},
ErrorMapping {
code: ErrorCode::NotFound,
status: StatusCode::NOT_FOUND,
},
ErrorMapping {
code: ErrorCode::Timeout,
status: StatusCode::REQUEST_TIMEOUT,
},
ErrorMapping {
code: ErrorCode::RateLimitExceeded,
status: StatusCode::TOO_MANY_REQUESTS,
},
ErrorMapping {
code: ErrorCode::DatabaseError,
status: StatusCode::INTERNAL_SERVER_ERROR,
},
ErrorMapping {
code: ErrorCode::InternalServerError,
status: StatusCode::INTERNAL_SERVER_ERROR,
},
];
for mapping in &mappings {
let error = GraphQLError::new("test", mapping.code);
assert_eq!(
error.code.status_code(),
mapping.status,
"Status code mismatch for {:?}",
mapping.code
);
}
}
#[test]
fn test_example_multiple_errors_in_response() {
let errors = [
GraphQLError::validation("Field 'email' is required")
.with_path(vec!["user".to_string(), "email".to_string()]),
GraphQLError::validation("Field 'age' must be >= 18")
.with_path(vec!["user".to_string(), "age".to_string()]),
];
assert_eq!(errors.len(), 2);
assert!(errors.iter().all(|e| e.code == ErrorCode::ValidationError));
assert!(errors[0].path.is_some());
assert!(errors[1].path.is_some());
}
#[test]
fn test_example_handler_error_patterns() {
{
let error =
GraphQLError::request("Invalid variable type").with_request_id("req-handler-001");
assert_eq!(error.code, ErrorCode::RequestError);
assert!(error.extensions.is_some());
}
{
let error = GraphQLError::forbidden();
assert_eq!(error.code, ErrorCode::Forbidden);
}
{
let error = GraphQLError::not_found("User with ID 123 not found");
assert_eq!(error.code, ErrorCode::NotFound);
}
{
let error = GraphQLError::internal("Unexpected error in field resolver");
assert_eq!(error.code, ErrorCode::InternalServerError);
}
}
#[test]
fn test_example_security_error_patterns() {
{
let error = GraphQLError::request("Invalid input: SQL keywords detected");
assert_eq!(error.code, ErrorCode::RequestError);
assert_eq!(error.code.status_code(), StatusCode::BAD_REQUEST);
}
{
let error = GraphQLError::request("Invalid input: HTML/script tags not allowed");
assert_eq!(error.code, ErrorCode::RequestError);
}
{
let error = GraphQLError::rate_limited("Too many failed login attempts");
assert_eq!(error.code, ErrorCode::RateLimitExceeded);
assert_eq!(error.code.status_code(), StatusCode::TOO_MANY_REQUESTS);
}
}
#[test]
fn test_example_error_recovery_patterns() {
{
let error = GraphQLError::timeout("Query execution");
let status = error.code.status_code();
assert_eq!(status, StatusCode::REQUEST_TIMEOUT);
}
{
let error = GraphQLError::internal("Service temporarily unavailable");
let status = error.code.status_code();
assert_eq!(status, StatusCode::INTERNAL_SERVER_ERROR);
}
{
let error = GraphQLError::validation("Invalid field type");
let status = error.code.status_code();
assert_eq!(status, StatusCode::BAD_REQUEST);
}
{
let error = GraphQLError::forbidden();
let status = error.code.status_code();
assert_eq!(status, StatusCode::FORBIDDEN);
}
}
#[test]
fn test_example_resolver_error_handling() {
let db_error = GraphQLError::database("Failed to query user table")
.with_path(vec!["user".to_string()])
.with_request_id("req-resolver-001");
assert_eq!(db_error.code, ErrorCode::DatabaseError);
assert_eq!(db_error.code.status_code(), StatusCode::INTERNAL_SERVER_ERROR);
let auth_error = GraphQLError::forbidden()
.with_path(vec!["user".to_string(), "email".to_string()])
.with_request_id("req-resolver-001");
assert_eq!(auth_error.code, ErrorCode::Forbidden);
assert_eq!(auth_error.code.status_code(), StatusCode::FORBIDDEN);
let validation_error = GraphQLError::validation("Argument 'id' must be positive")
.with_location(3, 20)
.with_request_id("req-resolver-001");
assert_eq!(validation_error.code, ErrorCode::ValidationError);
assert_eq!(validation_error.code.status_code(), StatusCode::BAD_REQUEST);
}
#[test]
fn test_example_federation_error_handling() {
let orders_timeout = GraphQLError::timeout("Orders subgraph")
.with_path(vec!["user".to_string(), "orders".to_string()])
.with_request_id("req-fed-001");
assert_eq!(orders_timeout.code, ErrorCode::Timeout);
assert_eq!(orders_timeout.code.status_code(), StatusCode::REQUEST_TIMEOUT);
assert!(orders_timeout.extensions.is_some());
let ext = orders_timeout.extensions.unwrap();
assert_eq!(ext.request_id, Some("req-fed-001".to_string()));
}
#[test]
fn test_example_graphql_response_with_partial_errors() {
let partial_error =
GraphQLError::forbidden().with_path(vec!["user".to_string(), "posts".to_string()]);
assert_eq!(partial_error.code, ErrorCode::Forbidden);
assert!(partial_error.path.is_some());
let path = partial_error.path.unwrap();
assert_eq!(path[0], "user");
assert_eq!(path[1], "posts");
}
#[test]
fn test_example_batch_query_error_handling() {
let query2_error = GraphQLError::parse("Unexpected token '}' in query 2")
.with_location(5, 15)
.with_request_id("req-batch-001");
let query3_error = GraphQLError::forbidden().with_request_id("req-batch-001");
assert_eq!(query2_error.code, ErrorCode::ParseError);
assert_eq!(query3_error.code, ErrorCode::Forbidden);
let ext2 = query2_error.extensions.unwrap_or(ErrorExtensions {
category: None,
status: None,
request_id: Some("req-batch-001".to_string()),
});
let ext3 = query3_error.extensions.unwrap_or(ErrorExtensions {
category: None,
status: None,
request_id: Some("req-batch-001".to_string()),
});
assert_eq!(ext2.request_id, Some("req-batch-001".to_string()));
assert_eq!(ext3.request_id, Some("req-batch-001".to_string()));
}
#[test]
fn test_example_subscription_error_handling() {
let auth_error = GraphQLError::unauthenticated().with_request_id("req-sub-001");
assert_eq!(auth_error.code, ErrorCode::Unauthenticated);
assert_eq!(auth_error.code.status_code(), StatusCode::UNAUTHORIZED);
let resource_error =
GraphQLError::rate_limited("Client already has 100 active subscriptions, maximum is 100")
.with_request_id("req-sub-002");
assert_eq!(resource_error.code, ErrorCode::RateLimitExceeded);
assert_eq!(resource_error.code.status_code(), StatusCode::TOO_MANY_REQUESTS);
}
#[test]
fn test_example_complete_error_context() {
let error = GraphQLError::database("Connection to primary replica failed after 3 retries")
.with_location(10, 5) .with_path(vec!["orders".to_string(), "items".to_string()]) .with_request_id("req-abc-xyz-123") .with_extensions(ErrorExtensions {
category: Some("DATABASE_REPLICA".to_string()),
status: Some(503),
request_id: Some("req-abc-xyz-123".to_string()),
});
assert_eq!(error.code, ErrorCode::DatabaseError);
assert!(error.locations.is_some());
assert!(error.path.is_some());
assert!(error.extensions.is_some());
assert_eq!(error.locations.unwrap()[0].line, 10);
assert_eq!(error.path.unwrap()[0], "orders");
assert_eq!(error.extensions.unwrap().category, Some("DATABASE_REPLICA".to_string()));
}