#[cfg(test)]
use anodizer_core::retry::{HttpError, Retriable};
#[cfg(test)]
fn classify_octocrab_error(
err: octocrab::Error,
) -> (Box<dyn std::error::Error + Send + Sync + 'static>, u16) {
match &err {
octocrab::Error::GitHub { source, .. } => {
let status = source.status_code.as_u16();
(Box::new(HttpError::new(err, status)), status)
}
octocrab::Error::Hyper { .. }
| octocrab::Error::Http { .. }
| octocrab::Error::Service { .. }
| octocrab::Error::Other { .. }
| octocrab::Error::Serde { .. }
| octocrab::Error::Json { .. } => (Box::new(Retriable::new(err)), 0),
_ => (Box::new(HttpError::new(err, 0)), 0),
}
}
#[cfg(test)]
mod tests {
use super::*;
use anodizer_core::retry::is_retriable;
async fn make_transport_error() -> octocrab::Error {
let builder = octocrab::OctocrabBuilder::new()
.base_uri("http://nonexistent.invalid/")
.ok()
.unwrap_or_else(|| panic!("OctocrabBuilder::base_uri rejected RFC 2606 .invalid URL"));
let octo = builder
.build()
.ok()
.unwrap_or_else(|| panic!("OctocrabBuilder::build failed"));
match octo.get::<serde_json::Value, _, ()>("/", None::<&()>).await {
Ok(_) => panic!("unexpected success against RFC 2606 .invalid host"),
Err(e) => e,
}
}
#[tokio::test]
async fn transport_error_classifies_as_retriable_regardless_of_message() {
let err = make_transport_error().await;
let is_transport = matches!(
&err,
octocrab::Error::Hyper { .. }
| octocrab::Error::Http { .. }
| octocrab::Error::Service { .. }
| octocrab::Error::Other { .. }
);
assert!(
is_transport,
"expected a transport-class octocrab error from RFC 2606 .invalid host, got: {err:?}"
);
let (wrapped, status) = classify_octocrab_error(err);
assert_eq!(status, 0, "transport errors carry no HTTP status");
assert!(
is_retriable(&*wrapped),
"transport-class octocrab errors must be classified as retriable \
via the Retriable wrapper"
);
}
#[test]
fn http_error_inner_5xx_429_is_retriable_4xx_is_not() {
let http500 = HttpError::new(std::io::Error::other("internal server error"), 500);
assert!(is_retriable(&http500), "500 must be retriable");
let http429 = HttpError::new(std::io::Error::other("rate limited"), 429);
assert!(is_retriable(&http429), "429 must be retriable");
let http422 = HttpError::new(std::io::Error::other("validation failed"), 422);
assert!(
!is_retriable(&http422),
"4xx (other than 429) must fast-fail, not retry"
);
}
#[test]
fn retriable_wrapper_overrides_message_classification() {
let inner = std::io::Error::other("service error");
let wrapped = Retriable::new(inner);
assert!(
is_retriable(&wrapped),
"Retriable wrapper must force is_retriable -> true"
);
}
}