use paper_gap::provider;
use reqwest::StatusCode;
use std::time::Duration;
#[test]
fn retries_only_transient_statuses() {
for status in [
StatusCode::TOO_MANY_REQUESTS,
StatusCode::BAD_GATEWAY,
StatusCode::SERVICE_UNAVAILABLE,
StatusCode::GATEWAY_TIMEOUT,
] {
assert!(provider::retry_delay(status, None).is_some());
}
for status in [
StatusCode::BAD_REQUEST,
StatusCode::UNAUTHORIZED,
StatusCode::NOT_FOUND,
StatusCode::INTERNAL_SERVER_ERROR,
] {
assert_eq!(provider::retry_delay(status, None), None);
}
}
#[test]
fn retry_after_is_parsed_and_capped() {
assert_eq!(
provider::retry_delay(StatusCode::TOO_MANY_REQUESTS, Some("2")),
Some(Duration::from_secs(2))
);
assert_eq!(
provider::retry_delay(StatusCode::TOO_MANY_REQUESTS, Some("60")),
Some(Duration::from_secs(5))
);
assert_eq!(
provider::retry_delay(StatusCode::TOO_MANY_REQUESTS, Some("invalid")),
Some(Duration::from_millis(250))
);
}
fn local_server(statuses: Vec<u16>) -> (String, std::thread::JoinHandle<()>) {
use std::io::{Read, Write};
let listener = std::net::TcpListener::bind("127.0.0.1:0").unwrap();
let url = format!("http://{}", listener.local_addr().unwrap());
let handle = std::thread::spawn(move || {
for status in statuses {
let (mut stream, _) = listener.accept().unwrap();
let mut request = [0; 1024];
let _ = stream.read(&mut request).unwrap();
write!(
stream,
"HTTP/1.1 {status} test\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"
)
.unwrap();
}
});
(url, handle)
}
#[tokio::test]
async fn retries_transient_response_once_but_not_permanent_error() {
let (url, server) = local_server(vec![503, 200]);
let response = provider::send_with_retry(reqwest::Client::new().get(url))
.await
.unwrap();
assert_eq!(response.status(), StatusCode::OK);
server.join().unwrap();
let (url, server) = local_server(vec![404]);
let response = provider::send_with_retry(reqwest::Client::new().get(url))
.await
.unwrap();
assert_eq!(response.status(), StatusCode::NOT_FOUND);
server.join().unwrap();
}