use std::path::PathBuf;
use serde::Deserialize;
use serde_json::json;
use wiremock::matchers::{method, path};
use wiremock::{Mock, MockServer, ResponseTemplate};
use rmcl::net::{HttpClient, download_file};
#[derive(Debug, Deserialize)]
struct ApiResponse {
ok: bool,
}
#[tokio::test]
async fn get_json_retries_5xx_then_succeeds() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api"))
.respond_with(ResponseTemplate::new(503))
.up_to_n_times(1)
.expect(1)
.mount(&server)
.await;
Mock::given(method("GET"))
.and(path("/api"))
.respond_with(ResponseTemplate::new(200).set_body_json(json!({"ok": true})))
.expect(1)
.mount(&server)
.await;
let url = format!("{}/api", server.uri());
let result: ApiResponse = HttpClient::new().get_json(&url).await.unwrap();
assert!(result.ok);
}
#[tokio::test]
async fn get_json_fails_fast_on_4xx() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api"))
.respond_with(ResponseTemplate::new(404))
.expect(1)
.mount(&server)
.await;
let url = format!("{}/api", server.uri());
let err = HttpClient::new()
.get_json::<ApiResponse>(&url)
.await
.unwrap_err();
assert!(
format!("{err:?}").contains("404"),
"expected 404 in error, got: {err:?}"
);
}
#[tokio::test]
async fn get_json_gives_up_after_max_retries() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/api"))
.respond_with(ResponseTemplate::new(503))
.expect(4)
.mount(&server)
.await;
let url = format!("{}/api", server.uri());
let err = HttpClient::new()
.get_json::<ApiResponse>(&url)
.await
.unwrap_err();
assert!(
format!("{err:?}").contains("503"),
"expected 503 in final error, got: {err:?}"
);
}
#[tokio::test]
async fn download_file_retries_5xx_then_succeeds() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/file.bin"))
.respond_with(ResponseTemplate::new(502))
.up_to_n_times(1)
.expect(1)
.mount(&server)
.await;
Mock::given(method("GET"))
.and(path("/file.bin"))
.respond_with(ResponseTemplate::new(200).set_body_bytes(b"hello, retried".to_vec()))
.expect(1)
.mount(&server)
.await;
let tmp = tempfile::tempdir().unwrap();
let dest = tmp.path().join("downloaded.bin");
let url = format!("{}/file.bin", server.uri());
download_file(&HttpClient::new(), &url, &dest, |_, _| {})
.await
.unwrap();
let content = std::fs::read(&dest).unwrap();
assert_eq!(content, b"hello, retried");
}
#[tokio::test]
async fn download_file_fails_fast_on_4xx() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/file.bin"))
.respond_with(ResponseTemplate::new(404))
.expect(1)
.mount(&server)
.await;
let tmp = tempfile::tempdir().unwrap();
let dest: PathBuf = tmp.path().join("never-written.bin");
let url = format!("{}/file.bin", server.uri());
let err = download_file(&HttpClient::new(), &url, &dest, |_, _| {})
.await
.unwrap_err();
assert!(
format!("{err:?}").contains("404"),
"expected 404 in error, got: {err:?}"
);
}