From 69c1d4a301b3ee0717aff4d66ddec546aacf1ed0 Mon Sep 17 00:00:00 2001 From: Denis Cornehl Date: Fri, 16 Jun 2023 17:53:54 +0200 Subject: [PATCH] upgrade mockito --- Cargo.lock | 35 +++++++++++++++++++++++--- Cargo.toml | 2 +- src/config.rs | 2 ++ src/repositories/github.rs | 50 ++++++++++++++++++++++++-------------- src/repositories/gitlab.rs | 50 +++++++++++++++++++++++++++----------- src/web/releases.rs | 49 ++++++++++++++++++++++++++++--------- 6 files changed, 140 insertions(+), 48 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7c552bde3..c27bcd46d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1718,6 +1718,21 @@ dependencies = [ "new_debug_unreachable", ] +[[package]] +name = "futures" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + [[package]] name = "futures-channel" version = "0.3.28" @@ -1734,6 +1749,17 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c" +[[package]] +name = "futures-executor" +version = "0.3.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + [[package]] name = "futures-io" version = "0.3.28" @@ -1769,6 +1795,7 @@ version = "0.3.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26b01e40b772d54cf6c6d721c1d1abd0647a0106a12ecaa1c186273392a69533" dependencies = [ + "futures-channel", "futures-core", "futures-io", "futures-macro", @@ -3261,13 +3288,14 @@ dependencies = [ [[package]] name = "mockito" -version = "0.31.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80f9fece9bd97ab74339fe19f4bcaf52b76dcc18e5364c7977c1838f76b38de9" +checksum = "ea57936ab3bf56156f135f20ee24b840e5a8ad97a8e1710eace33ac078f8f537" dependencies = [ "assert-json-diff 2.0.2", "colored", - "httparse", + "futures", + "hyper", "lazy_static", "log", "rand 0.8.5", @@ -3275,6 +3303,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "similar", + "tokio", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 91ecc7bb9..91eff1267 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -126,7 +126,7 @@ procfs = "0.15.1" criterion = "0.5.1" kuchiki = "0.8" rand = "0.8" -mockito = "0.31.0" +mockito = "1.0.2" test-case = "3.0.0" aws-smithy-client = { version = "0.55.1", features = ["test-util"]} aws-smithy-http = "0.55.1" diff --git a/src/config.rs b/src/config.rs index 0cf907626..fd4106b21 100644 --- a/src/config.rs +++ b/src/config.rs @@ -8,6 +8,7 @@ pub struct Config { pub prefix: PathBuf, pub registry_index_path: PathBuf, pub registry_url: Option, + pub registry_api_host: String, // Database connection params pub(crate) database_url: String, @@ -135,6 +136,7 @@ impl Config { registry_index_path: env("REGISTRY_INDEX_PATH", prefix.join("crates.io-index"))?, registry_url: maybe_env("REGISTRY_URL")?, + registry_api_host: env("DOCSRS_REGISTRY_API_HOST", "https://crates.io".into())?, prefix: prefix.clone(), database_url: require_env("DOCSRS_DATABASE_URL")?, diff --git a/src/repositories/github.rs b/src/repositories/github.rs index 62d6a7a16..c38b3cd55 100644 --- a/src/repositories/github.rs +++ b/src/repositories/github.rs @@ -43,6 +43,7 @@ const GRAPHQL_SINGLE: &str = "query($owner: String!, $repo: String!) { }"; pub struct GitHub { + endpoint: String, client: HttpClient, github_updater_min_rate_limit: u32, } @@ -51,6 +52,13 @@ impl GitHub { /// Returns `Err` if the access token has invalid syntax (but *not* if it isn't authorized). /// Returns `Ok(None)` if there is no access token. pub fn new(config: &Config) -> Result> { + Self::with_custom_endpoint(config, "https://github.com/gitapi/graphql") + } + + pub fn with_custom_endpoint>( + config: &Config, + endpoint: E, + ) -> Result> { let mut headers = HeaderMap::new(); headers.insert(USER_AGENT, HeaderValue::from_static(APP_USER_AGENT)); headers.insert(ACCEPT, HeaderValue::from_static("application/json")); @@ -69,6 +77,7 @@ impl GitHub { Ok(Some(GitHub { client, + endpoint: endpoint.as_ref().to_owned(), github_updater_min_rate_limit: config.github_updater_min_rate_limit, })) } @@ -176,16 +185,9 @@ impl GitHub { query: &str, variables: impl serde::Serialize, ) -> Result> { - #[cfg(not(test))] - let host = "https://github.com/gitapi/graphql"; - #[cfg(test)] - let host = format!("{}/graphql", mockito::server_url()); - #[cfg(test)] - let host = &host; - Ok(self .client - .post(host) + .post(&self.endpoint) .json(&serde_json::json!({ "query": query, "variables": variables, @@ -256,19 +258,28 @@ struct GraphIssues { #[cfg(test)] mod tests { - use super::GitHub; + use super::{Config, GitHub}; use crate::repositories::updater::{repository_name, RepositoryForge}; use crate::repositories::RateLimitReached; - use mockito::mock; + + fn mock_server_and_github(config: &Config) -> (mockito::ServerGuard, GitHub) { + let server = mockito::Server::new(); + let updater = GitHub::with_custom_endpoint(config, format!("{}/graphql", server.url())) + .expect("GitHub::new failed") + .unwrap(); + + (server, updater) + } #[test] fn test_rate_limit_fail() { crate::test::wrapper(|env| { let mut config = env.base_config(); config.github_accesstoken = Some("qsjdnfqdq".to_owned()); - let updater = GitHub::new(&config).expect("GitHub::new failed").unwrap(); + let (mut server, updater) = mock_server_and_github(&config); - let _m1 = mock("POST", "/graphql") + let _m1 = server + .mock("POST", "/graphql") .with_header("content-type", "application/json") .with_body( r#"{"errors":[{"type":"RATE_LIMITED","message":"API rate limit exceeded"}]}"#, @@ -288,9 +299,10 @@ mod tests { crate::test::wrapper(|env| { let mut config = env.base_config(); config.github_accesstoken = Some("qsjdnfqdq".to_owned()); - let updater = GitHub::new(&config).expect("GitHub::new failed").unwrap(); + let (mut server, updater) = mock_server_and_github(&config); - let _m1 = mock("POST", "/graphql") + let _m1 = server + .mock("POST", "/graphql") .with_header("content-type", "application/json") .with_body(r#"{"data": {"nodes": [], "rateLimit": {"remaining": 0}}}"#) .create(); @@ -308,9 +320,10 @@ mod tests { crate::test::wrapper(|env| { let mut config = env.base_config(); config.github_accesstoken = Some("qsjdnfqdq".to_owned()); - let updater = GitHub::new(&config).expect("GitHub::new failed").unwrap(); + let (mut server, updater) = mock_server_and_github(&config); - let _m1 = mock("POST", "/graphql") + let _m1 = server + .mock("POST", "/graphql") .with_header("content-type", "application/json") .with_body( r#"{"data": {"nodes": [], "rateLimit": {"remaining": 100000}}, "errors": @@ -334,9 +347,10 @@ mod tests { crate::test::wrapper(|env| { let mut config = env.base_config(); config.github_accesstoken = Some("qsjdnfqdq".to_owned()); - let updater = GitHub::new(&config).expect("GitHub::new failed").unwrap(); + let (mut server, updater) = mock_server_and_github(&config); - let _m1 = mock("POST", "/graphql") + let _m1 = server + .mock("POST", "/graphql") .with_header("content-type", "application/json") .with_body( r#"{"data": {"repository": {"id": "hello", "nameWithOwner": "foo/bar", diff --git a/src/repositories/gitlab.rs b/src/repositories/gitlab.rs index 4eb8f394a..e1c688fcf 100644 --- a/src/repositories/gitlab.rs +++ b/src/repositories/gitlab.rs @@ -43,10 +43,19 @@ const GRAPHQL_SINGLE: &str = "query($fullPath: ID!) { pub struct GitLab { client: HttpClient, host: &'static str, + endpoint: String, } impl GitLab { pub fn new(host: &'static str, access_token: &Option) -> Result { + Self::with_custom_endpoint(host, access_token, format!("https://{}/api/graphql", host)) + } + + pub fn with_custom_endpoint>( + host: &'static str, + access_token: &Option, + endpoint: E, + ) -> Result { let mut headers = HeaderMap::new(); headers.insert(USER_AGENT, HeaderValue::from_static(APP_USER_AGENT)); headers.insert(ACCEPT, HeaderValue::from_static("application/json")); @@ -64,7 +73,11 @@ impl GitLab { } let client = HttpClient::builder().default_headers(headers).build()?; - Ok(GitLab { client, host }) + Ok(GitLab { + client, + host, + endpoint: endpoint.as_ref().to_string(), + }) } } @@ -165,14 +178,9 @@ impl GitLab { query: &str, variables: impl serde::Serialize, ) -> Result<(GraphResponse, Option)> { - #[cfg(not(test))] - let host = format!("https://{}/api/graphql", self.host); - #[cfg(test)] - let host = format!("{}/api/graphql", mockito::server_url()); - let res = self .client - .post(host) + .post(&self.endpoint) .json(&serde_json::json!({ "query": query, "variables": variables, @@ -252,13 +260,25 @@ mod tests { use super::GitLab; use crate::repositories::updater::{repository_name, RepositoryForge}; use crate::repositories::RateLimitReached; - use mockito::mock; + + fn mock_server_and_gitlab() -> (mockito::ServerGuard, GitLab) { + let server = mockito::Server::new(); + let updater = GitLab::with_custom_endpoint( + "gitlab.com", + &None, + format!("{}/api/graphql", server.url()), + ) + .expect("GitLab::new failed"); + + (server, updater) + } #[test] fn test_rate_limit() { - let updater = GitLab::new("gitlab.com", &None).expect("GitLab::new failed"); + let (mut server, updater) = mock_server_and_gitlab(); - let _m1 = mock("POST", "/api/graphql") + let _m1 = server + .mock("POST", "/api/graphql") .with_header("content-type", "application/json") .with_header("RateLimit-Remaining", "0") .with_body("{}") @@ -278,9 +298,10 @@ mod tests { #[test] fn not_found() { - let updater = GitLab::new("gitlab.com", &None).expect("GitLab::new failed"); + let (mut server, updater) = mock_server_and_gitlab(); - let _m1 = mock("POST", "/api/graphql") + let _m1 = server + .mock("POST", "/api/graphql") .with_header("content-type", "application/json") .with_body(r#"{"data": {"projects": {"nodes": []}}}"#) .create(); @@ -296,9 +317,10 @@ mod tests { #[test] fn get_repository_info() { - let updater = GitLab::new("gitlab.com", &None).expect("GitLab::new failed"); + let (mut server, updater) = mock_server_and_gitlab(); - let _m1 = mock("POST", "/api/graphql") + let _m1 = server + .mock("POST", "/api/graphql") .with_header("content-type", "application/json") .with_body( r#"{"data": {"project": {"id": "hello", "fullPath": "foo/bar", diff --git a/src/web/releases.rs b/src/web/releases.rs index f6a2b76f7..846916ffb 100644 --- a/src/web/releases.rs +++ b/src/web/releases.rs @@ -163,12 +163,10 @@ async fn get_search_results( .unwrap() }); - #[cfg(not(test))] - let host = "https://crates.io"; - #[cfg(test)] - let host = mockito::server_url(); - - let url = url::Url::parse(&format!("{host}/api/v1/crates{query_params}"))?; + let url = url::Url::parse(&format!( + "{}/api/v1/crates{query_params}", + config.registry_api_host + ))?; debug!("fetching search results from {}", url); // extract the query from the query args. @@ -756,7 +754,7 @@ mod tests { use anyhow::Error; use chrono::{Duration, TimeZone}; use kuchiki::traits::TendrilSink; - use mockito::{mock, Matcher}; + use mockito::Matcher; use reqwest::StatusCode; use serde_json::json; use std::collections::HashSet; @@ -899,10 +897,16 @@ mod tests { #[test] fn search_result_passes_cratesio_pagination_links() { wrapper(|env| { + let mut crates_io = mockito::Server::new(); + env.override_config(|config| { + config.registry_api_host = crates_io.url(); + }); + let web = env.frontend(); env.fake_release().name("some_random_crate").create()?; - let _m = mock("GET", "/api/v1/crates") + let _m = crates_io + .mock("GET", "/api/v1/crates") .match_query(Matcher::AllOf(vec![ Matcher::UrlEncoded("q".into(), "some_random_crate".into()), Matcher::UrlEncoded("per_page".into(), "30".into()), @@ -978,11 +982,14 @@ mod tests { #[test_case(StatusCode::BAD_GATEWAY)] fn crates_io_errors_are_correctly_returned_and_we_dont_try_parsing(status: StatusCode) { wrapper(|env| { + let mut crates_io = mockito::Server::new(); env.override_config(|config| { config.crates_io_api_call_retries = 0; + config.registry_api_host = crates_io.url(); }); - let _m = mock("GET", "/api/v1/crates") + let _m = crates_io + .mock("GET", "/api/v1/crates") .match_query(Matcher::AllOf(vec![ Matcher::UrlEncoded("q".into(), "doesnt_matter_here".into()), Matcher::UrlEncoded("per_page".into(), "30".into()), @@ -1004,10 +1011,16 @@ mod tests { #[test] fn search_encoded_pagination_passed_to_cratesio() { wrapper(|env| { + let mut crates_io = mockito::Server::new(); + env.override_config(|config| { + config.registry_api_host = crates_io.url(); + }); + let web = env.frontend(); env.fake_release().name("some_random_crate").create()?; - let _m = mock("GET", "/api/v1/crates") + let _m = crates_io + .mock("GET", "/api/v1/crates") .match_query(Matcher::AllOf(vec![ Matcher::UrlEncoded("some".into(), "dummy".into()), Matcher::UrlEncoded("pagination".into(), "parameters".into()), @@ -1045,10 +1058,16 @@ mod tests { #[test] fn search_lucky_with_unknown_crate() { wrapper(|env| { + let mut crates_io = mockito::Server::new(); + env.override_config(|config| { + config.registry_api_host = crates_io.url(); + }); + let web = env.frontend(); env.fake_release().name("some_random_crate").create()?; - let _m = mock("GET", "/api/v1/crates") + let _m = crates_io + .mock("GET", "/api/v1/crates") .match_query(Matcher::AllOf(vec![ Matcher::UrlEncoded("q".into(), "some_random_".into()), Matcher::UrlEncoded("per_page".into(), "30".into()), @@ -1086,6 +1105,11 @@ mod tests { #[test] fn search() { wrapper(|env| { + let mut crates_io = mockito::Server::new(); + env.override_config(|config| { + config.registry_api_host = crates_io.url(); + }); + let web = env.frontend(); env.fake_release() .name("some_random_crate") @@ -1101,7 +1125,8 @@ mod tests { .version("0.0.1") .create()?; - let _m = mock("GET", "/api/v1/crates") + let _m = crates_io + .mock("GET", "/api/v1/crates") .match_query(Matcher::AllOf(vec![ Matcher::UrlEncoded("q".into(), "some_random_crate".into()), Matcher::UrlEncoded("per_page".into(), "30".into()),