#![expect(clippy::unwrap_used)]
use super::*;
#[test]
fn exit_code_config() {
let err = BzrError::Config("bad config".into());
assert_eq!(err.exit_code(), 3);
}
#[test]
fn exit_code_api() {
let err = BzrError::Api {
code: 101,
message: "Invalid Bug ID".into(),
};
assert_eq!(err.exit_code(), 4);
}
#[test]
fn exit_code_io() {
let err = BzrError::Io(std::io::Error::new(
std::io::ErrorKind::NotFound,
"file not found",
));
assert_eq!(err.exit_code(), 6);
}
#[test]
fn exit_code_other() {
let err = BzrError::Other("something went wrong".into());
assert_eq!(err.exit_code(), 1);
}
#[test]
fn exit_code_toml_parse() {
let toml_err: std::result::Result<toml::Value, _> = toml::from_str("{{bad");
let err = BzrError::TomlParse(toml_err.unwrap_err());
assert_eq!(err.exit_code(), 3);
}
#[test]
fn error_type_config() {
let err = BzrError::Config("x".into());
assert_eq!(err.error_type(), "config");
}
#[test]
fn error_type_api() {
let err = BzrError::Api {
code: 1,
message: "x".into(),
};
assert_eq!(err.error_type(), "api");
}
#[test]
fn error_type_io() {
let err = BzrError::Io(std::io::Error::other("x"));
assert_eq!(err.error_type(), "io");
}
#[test]
fn error_type_other() {
let err = BzrError::Other("x".into());
assert_eq!(err.error_type(), "other");
}
#[test]
fn exit_code_not_found() {
let err = BzrError::NotFound {
resource: "bug",
id: "42".into(),
};
assert_eq!(err.exit_code(), 2);
assert_eq!(err.error_type(), "not_found");
assert_eq!(err.to_string(), "bug not found: 42");
}
#[test]
fn error_type_toml_parse() {
let toml_err: std::result::Result<toml::Value, _> = toml::from_str("{{bad");
let err = BzrError::TomlParse(toml_err.unwrap_err());
assert_eq!(err.error_type(), "config");
}
#[test]
fn exit_code_http_status() {
let err = BzrError::HttpStatus {
status: 500,
body: "internal error".into(),
};
assert_eq!(err.exit_code(), 5);
assert_eq!(err.error_type(), "http");
assert_eq!(err.to_string(), "HTTP 500: internal error");
}
#[test]
fn exit_code_input_validation() {
let err = BzrError::InputValidation("bad flag".into());
assert_eq!(err.exit_code(), 7);
assert_eq!(err.error_type(), "input");
assert_eq!(err.to_string(), "bad flag");
}
#[test]
fn exit_code_deserialize() {
let err = BzrError::Deserialize("invalid JSON".into());
assert_eq!(err.exit_code(), 8);
assert_eq!(err.error_type(), "deserialize");
assert_eq!(err.to_string(), "Failed to parse response: invalid JSON");
}
#[test]
fn exit_code_auth() {
let err = BzrError::Auth("invalid API key".into());
assert_eq!(err.exit_code(), 9);
assert_eq!(err.error_type(), "auth");
assert_eq!(err.to_string(), "Authentication error: invalid API key");
}
#[test]
fn exit_code_data_integrity() {
let err = BzrError::DataIntegrity("attachment has no data".into());
assert_eq!(err.exit_code(), 10);
assert_eq!(err.error_type(), "data_integrity");
}
#[test]
fn exit_code_keyring() {
let err = BzrError::Keyring("keychain locked".into());
assert_eq!(err.exit_code(), 12);
assert_eq!(err.error_type(), "keyring");
assert_eq!(err.to_string(), "keyring error: keychain locked");
}
#[test]
fn exit_code_pin_mismatch() {
let err = BzrError::PinMismatch {
server: "test".into(),
expected: "sha256//old".into(),
actual: "sha256//new".into(),
};
assert_eq!(err.exit_code(), 13);
assert_eq!(err.error_type(), "tls");
assert!(err.to_string().contains("pin mismatch"));
}
#[test]
fn exit_code_issuer_changed() {
let err = BzrError::IssuerChanged {
server: "test".into(),
expected_issuer: "CN=Good CA".into(),
actual_issuer: "CN=Evil CA".into(),
};
assert_eq!(err.exit_code(), 13);
assert_eq!(err.error_type(), "tls");
assert!(err.to_string().contains("MITM"));
}
#[tokio::test]
async fn format_http_error_includes_source_chain() {
let client = reqwest::Client::builder().build().unwrap();
let err = client
.get("http://127.0.0.1:1/unreachable")
.send()
.await
.unwrap_err();
let display_only = err.to_string();
assert!(
display_only.contains("error sending request"),
"expected reqwest error kind: {display_only}"
);
let formatted = format_http_error(&err);
assert!(
formatted.len() > display_only.len(),
"format_http_error should include source chain, got: {formatted}"
);
assert!(
formatted.contains("connect") || formatted.contains("refused") || formatted.contains("tcp"),
"expected connection-level detail in: {formatted}"
);
}