use cargo_fresh::package::sparse_index::{fetch_latest, SparseIndexError};
use wiremock::matchers::{method, path};
use wiremock::{Mock, MockServer, ResponseTemplate};
fn client() -> reqwest::Client {
reqwest::Client::builder()
.user_agent("cargo-fresh-test")
.timeout(std::time::Duration::from_secs(5))
.build()
.unwrap()
}
#[tokio::test]
async fn success_returns_parsed_versions() {
let server = MockServer::start().await;
let body = r#"{"name":"ripgrep","vers":"13.0.0","yanked":false}
{"name":"ripgrep","vers":"14.1.1","yanked":false}
"#;
Mock::given(method("GET"))
.and(path("/ri/pg/ripgrep"))
.respond_with(ResponseTemplate::new(200).set_body_string(body))
.expect(1)
.mount(&server)
.await;
let v = fetch_latest(&client(), &server.uri(), "ripgrep").await.unwrap();
assert_eq!(v.stable.as_deref(), Some("14.1.1"));
}
#[tokio::test]
async fn not_found_is_not_retried() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/ca/rg/cargo-nonexistent"))
.respond_with(ResponseTemplate::new(404))
.expect(1) .mount(&server)
.await;
let err = fetch_latest(&client(), &server.uri(), "cargo-nonexistent")
.await
.unwrap_err();
assert!(matches!(err, SparseIndexError::NotFound), "err = {err:?}");
}
#[tokio::test]
async fn server_error_is_retried_once_then_fails() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/ri/pg/ripgrep"))
.respond_with(ResponseTemplate::new(503))
.expect(2) .mount(&server)
.await;
let err = fetch_latest(&client(), &server.uri(), "ripgrep")
.await
.unwrap_err();
match err {
SparseIndexError::Unavailable(e) => {
assert!(e.to_string().contains("503"), "inner = {e}");
}
other => panic!("expected Unavailable, got {other:?}"),
}
}
#[tokio::test]
async fn server_error_then_success_recovers() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/ri/pg/ripgrep"))
.respond_with(ResponseTemplate::new(503))
.up_to_n_times(1)
.mount(&server)
.await;
Mock::given(method("GET"))
.and(path("/ri/pg/ripgrep"))
.respond_with(
ResponseTemplate::new(200)
.set_body_string(r#"{"name":"ripgrep","vers":"14.0.0","yanked":false}"#),
)
.mount(&server)
.await;
let v = fetch_latest(&client(), &server.uri(), "ripgrep").await.unwrap();
assert_eq!(v.stable.as_deref(), Some("14.0.0"));
}
#[tokio::test]
async fn empty_body_returns_empty_versions() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/ca/rg/cargo-fresh"))
.respond_with(ResponseTemplate::new(200).set_body_string(""))
.expect(1)
.mount(&server)
.await;
let v = fetch_latest(&client(), &server.uri(), "cargo-fresh").await.unwrap();
assert!(v.stable.is_none() && v.prerelease.is_none());
}
#[tokio::test]
async fn check_package_updates_records_unavailable_error() {
use cargo_fresh::models::{CheckErrorKind, PackageInfo, PackageSource};
use cargo_fresh::package::check_package_updates;
let server = MockServer::start().await;
Mock::given(method("GET"))
.respond_with(ResponseTemplate::new(503))
.mount(&server)
.await;
let mut packages = vec![PackageInfo::with_source(
"ripgrep".into(),
Some("14.0.0".into()),
PackageSource::Crates,
)];
check_package_updates(&mut packages, false, false, Some(server.uri()), true)
.await
.unwrap();
let err = packages[0].check_error.as_ref().expect("check_error set");
assert_eq!(err.kind, CheckErrorKind::Unavailable);
assert!(packages[0].latest_version.is_none());
}
#[tokio::test]
async fn check_package_updates_records_not_found_error() {
use cargo_fresh::models::{CheckErrorKind, PackageInfo, PackageSource};
use cargo_fresh::package::check_package_updates;
let server = MockServer::start().await;
Mock::given(method("GET"))
.respond_with(ResponseTemplate::new(404))
.mount(&server)
.await;
let mut packages = vec![PackageInfo::with_source(
"nonexistent-crate".into(),
Some("0.1.0".into()),
PackageSource::Crates,
)];
check_package_updates(&mut packages, false, false, Some(server.uri()), true)
.await
.unwrap();
let err = packages[0].check_error.as_ref().expect("check_error set");
assert_eq!(err.kind, CheckErrorKind::NotFound);
assert!(packages[0].latest_version.is_none());
}