use apollo_errors::{CodeCase, FieldCase, FormatConfig};
mod common;
use apollo_errors::Error as ErrorTrait;
use common::{
ConfigStructError, InnerError, SimpleStructError, TransparentToStructError,
TransparentWrapperError,
};
use http::StatusCode;
use insta::assert_json_snapshot;
#[test]
fn test_transparent_display_forwards_to_inner() {
let error = TransparentWrapperError::Inner(InnerError::DatabaseError);
assert_eq!(error.to_string(), "Database connection failed");
}
#[test]
fn test_transparent_display_with_fields() {
let error = TransparentWrapperError::Inner(InnerError::NetworkTimeout { timeout_ms: 5000 });
assert_eq!(error.to_string(), "Network timeout after 5000ms");
}
#[test]
fn test_transparent_source_returns_inner() {
let error = TransparentWrapperError::Inner(InnerError::DatabaseError);
let source = std::error::Error::source(&error);
assert!(source.is_some());
assert_eq!(source.unwrap().to_string(), "Database connection failed");
}
#[test]
fn test_transparent_json_forwards_to_inner() {
let error = TransparentWrapperError::Inner(InnerError::DatabaseError);
let json = error.to_json(FormatConfig::default()).unwrap();
assert_json_snapshot!(json, @r#"
{
"error": "db::connection_failed",
"message": "Database connection failed"
}
"#);
}
#[test]
fn test_transparent_json_with_extension_fields() {
let error = TransparentWrapperError::Inner(InnerError::NetworkTimeout { timeout_ms: 5000 });
let json = error.to_json(FormatConfig::default()).unwrap();
assert_json_snapshot!(json, @r#"
{
"error": "network::timeout",
"message": "Network timeout after 5000ms",
"timeout_ms": 5000
}
"#);
}
#[test]
fn test_transparent_graphql_forwards_to_inner() {
let error = TransparentWrapperError::Inner(InnerError::DatabaseError);
let graphql = error.to_graphql(FormatConfig::default()).unwrap();
assert_json_snapshot!(graphql, @r#"
{
"extensions": {
"code": "db::connection_failed"
},
"message": "Database connection failed"
}
"#);
}
#[test]
fn test_transparent_graphql_with_extension_fields() {
let error = TransparentWrapperError::Inner(InnerError::NetworkTimeout { timeout_ms: 3000 });
let graphql = error.to_graphql(FormatConfig::default()).unwrap();
assert_json_snapshot!(graphql, @r#"
{
"extensions": {
"code": "network::timeout",
"timeout_ms": 3000
},
"message": "Network timeout after 3000ms"
}
"#);
}
#[test]
fn test_transparent_jsonrpc_forwards_to_inner() {
let error = TransparentWrapperError::Inner(InnerError::DatabaseError);
let jsonrpc = error.to_jsonrpc(FormatConfig::default()).unwrap();
assert_json_snapshot!(jsonrpc, @r#"
{
"code": -32000,
"data": {
"diagnostic_code": "db::connection_failed"
},
"message": "Database connection failed"
}
"#);
}
#[test]
fn test_transparent_jsonrpc_with_extension_fields() {
let error = TransparentWrapperError::Inner(InnerError::NetworkTimeout { timeout_ms: 5000 });
let jsonrpc = error.to_jsonrpc(FormatConfig::default()).unwrap();
assert_json_snapshot!(jsonrpc, @r#"
{
"code": -32000,
"data": {
"diagnostic_code": "network::timeout",
"timeout_ms": 5000
},
"message": "Network timeout after 5000ms"
}
"#);
}
#[test]
fn test_transparent_http_status_forwards_to_inner() {
let error = TransparentWrapperError::Inner(InnerError::DatabaseError);
assert_eq!(error.http_status(), StatusCode::SERVICE_UNAVAILABLE);
}
#[test]
fn test_transparent_http_status_with_different_status() {
let error = TransparentWrapperError::Inner(InnerError::NetworkTimeout { timeout_ms: 1000 });
assert_eq!(error.http_status(), StatusCode::GATEWAY_TIMEOUT);
}
#[test]
fn test_transparent_text_forwards_to_inner() {
let error = TransparentWrapperError::Inner(InnerError::DatabaseError);
assert_eq!(
error.to_text(FormatConfig::default()),
"[db::connection_failed] Database connection failed"
);
}
#[test]
fn test_transparent_diagnostic_code_forwards_to_inner() {
use miette::Diagnostic;
let error = TransparentWrapperError::Inner(InnerError::DatabaseError);
let code = error.code().map(|c| c.to_string());
assert_eq!(code, Some("db::connection_failed".to_string()));
}
#[test]
fn test_transparent_diagnostic_help_forwards_to_inner() {
use miette::Diagnostic;
let error = TransparentWrapperError::Inner(InnerError::DatabaseError);
let help = error.help().map(|h| h.to_string());
assert_eq!(help, Some("Check your database credentials".to_string()));
}
#[test]
fn test_regular_variant_in_mixed_enum_still_works() {
let error = TransparentWrapperError::ApplicationError;
let json = error.to_json(FormatConfig::default()).unwrap();
assert_json_snapshot!(json, @r#"
{
"error": "app::error",
"message": "Application error"
}
"#);
}
#[test]
fn test_transparent_to_struct_display() {
let error = TransparentToStructError::Simple(SimpleStructError);
assert_eq!(error.to_string(), "Simple struct error occurred");
}
#[test]
fn test_transparent_to_struct_with_fields_display() {
let error = TransparentToStructError::Config(ConfigStructError {
port: 9999,
config_path: "/app/config.yaml".to_string(),
});
assert_eq!(error.to_string(), "Configuration error: invalid port 9999");
}
#[test]
fn test_transparent_to_struct_json() {
let error = TransparentToStructError::Simple(SimpleStructError);
let json = error.to_json(FormatConfig::default()).unwrap();
assert_json_snapshot!(json, @r#"
{
"error": "structs::simple",
"message": "Simple struct error occurred"
}
"#);
}
#[test]
fn test_transparent_to_struct_with_fields_json() {
let error = TransparentToStructError::Config(ConfigStructError {
port: 8080,
config_path: "/etc/app.toml".to_string(),
});
let json = error.to_json(FormatConfig::default()).unwrap();
assert_json_snapshot!(json, @r#"
{
"config_path": "/etc/app.toml",
"error": "structs::config_error",
"message": "Configuration error: invalid port 8080",
"port": 8080
}
"#);
}
#[test]
fn test_transparent_to_struct_graphql() {
let error = TransparentToStructError::Config(ConfigStructError {
port: 443,
config_path: "/config.json".to_string(),
});
let graphql = error.to_graphql(FormatConfig::default()).unwrap();
assert_json_snapshot!(graphql, @r#"
{
"extensions": {
"code": "structs::config_error",
"config_path": "/config.json",
"port": 443
},
"message": "Configuration error: invalid port 443"
}
"#);
}
#[test]
fn test_transparent_to_struct_jsonrpc() {
let error = TransparentToStructError::Config(ConfigStructError {
port: 443,
config_path: "/config.json".to_string(),
});
let jsonrpc = error.to_jsonrpc(FormatConfig::default()).unwrap();
assert_json_snapshot!(jsonrpc, @r#"
{
"code": -32000,
"data": {
"config_path": "/config.json",
"diagnostic_code": "structs::config_error",
"port": 443
},
"message": "Configuration error: invalid port 443"
}
"#);
}
#[test]
fn test_transparent_to_struct_http_status() {
let error = TransparentToStructError::Simple(SimpleStructError);
assert_eq!(error.http_status(), StatusCode::INTERNAL_SERVER_ERROR);
}
#[test]
fn test_transparent_to_struct_custom_http_status() {
let error = TransparentToStructError::Config(ConfigStructError {
port: 80,
config_path: "config".to_string(),
});
assert_eq!(error.http_status(), StatusCode::BAD_REQUEST);
}
#[test]
fn test_transparent_to_struct_text() {
let error = TransparentToStructError::Simple(SimpleStructError);
assert_eq!(
error.to_text(FormatConfig::default()),
"[structs::simple] Simple struct error occurred"
);
}
#[test]
fn test_transparent_to_struct_diagnostic_code() {
use miette::Diagnostic;
let error = TransparentToStructError::Config(ConfigStructError {
port: 80,
config_path: "config".to_string(),
});
let code = error.code().map(|c| c.to_string());
assert_eq!(code, Some("structs::config_error".to_string()));
}
#[test]
fn test_transparent_to_struct_diagnostic_help() {
use miette::Diagnostic;
let error = TransparentToStructError::Config(ConfigStructError {
port: 80,
config_path: "config".to_string(),
});
let help = error.help().map(|h| h.to_string());
assert_eq!(help, Some("Check your configuration file".to_string()));
}
#[test]
fn test_transparent_to_struct_source_returns_inner() {
let error = TransparentToStructError::Config(ConfigStructError {
port: 80,
config_path: "config".to_string(),
});
let source = std::error::Error::source(&error);
assert!(source.is_some());
assert_eq!(
source.unwrap().to_string(),
"Configuration error: invalid port 80"
);
}
#[test]
fn test_direct_variant_in_transparent_to_struct_enum() {
let error = TransparentToStructError::Direct;
let json = error.to_json(FormatConfig::default()).unwrap();
assert_json_snapshot!(json, @r#"
{
"error": "wrapper::direct",
"message": "Direct enum error"
}
"#);
}
#[test]
fn test_transparent_propagates_field_case() {
let error = TransparentWrapperError::Inner(InnerError::NetworkTimeout { timeout_ms: 5000 });
let config = FormatConfig {
field_case: FieldCase::CamelCase,
..FormatConfig::default()
};
let json = error.to_json(config).unwrap();
let obj = json.as_object().unwrap();
assert!(
obj.contains_key("timeoutMs"),
"transparent delegation should apply field_case, got keys: {:?}",
obj.keys().collect::<Vec<_>>()
);
assert!(!obj.contains_key("timeout_ms"));
}
#[test]
fn test_transparent_propagates_code_case() {
let error = TransparentWrapperError::Inner(InnerError::NetworkTimeout { timeout_ms: 1000 });
let config = FormatConfig {
code_case: CodeCase::ScreamingSnakeCase,
..FormatConfig::default()
};
let graphql = error.to_graphql(config).unwrap();
assert_eq!(
graphql["extensions"]["code"].as_str().unwrap(),
"NETWORK_TIMEOUT"
);
}