mirror of
https://github.com/cargo-bins/cargo-binstall.git
synced 2025-04-19 20:18:43 +00:00
Speedup binstalk-git-repo-api unit testing (#1731)
* Share `remote::Client` between tests when in cargo-test Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Run rate-limited binstalk-git-repo-api test in serial Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Update client-side rate limit to 1 request per 300ms Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Reduce test cases in `rate_limited_test_has_release_artifact_and_download_artifacts` Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Add `cargo-nextest` that I forgot Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> * Fix unit testing: Pass auth-token to restful API to avoid rate limit Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com> --------- Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
This commit is contained in:
parent
4d7a91aa4c
commit
6622bf1ae3
7 changed files with 75 additions and 35 deletions
6
.config/nextest.toml
Normal file
6
.config/nextest.toml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
[test-groups]
|
||||||
|
rate-limited = { max-threads = 1 }
|
||||||
|
|
||||||
|
[[profile.default.overrides]]
|
||||||
|
filter = 'test(rate_limited::)'
|
||||||
|
test-group = 'rate-limited'
|
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -354,6 +354,7 @@ version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"binstalk-downloader",
|
"binstalk-downloader",
|
||||||
"compact_str",
|
"compact_str",
|
||||||
|
"once_cell",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"serde",
|
"serde",
|
||||||
"serde-tuple-vec-map",
|
"serde-tuple-vec-map",
|
||||||
|
|
|
@ -26,3 +26,4 @@ url = "2.3.1"
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
binstalk-downloader = { version = "0.11.1", path = "../binstalk-downloader" }
|
binstalk-downloader = { version = "0.11.1", path = "../binstalk-downloader" }
|
||||||
tracing-subscriber = "0.3"
|
tracing-subscriber = "0.3"
|
||||||
|
once_cell = "1"
|
||||||
|
|
|
@ -131,6 +131,8 @@ struct Inner {
|
||||||
|
|
||||||
auth_token: Option<CompactString>,
|
auth_token: Option<CompactString>,
|
||||||
is_auth_token_valid: AtomicBool,
|
is_auth_token_valid: AtomicBool,
|
||||||
|
|
||||||
|
only_use_restful_api: AtomicBool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Github API client for querying whether a release artifact exitsts.
|
/// Github API client for querying whether a release artifact exitsts.
|
||||||
|
@ -147,9 +149,16 @@ impl GhApiClient {
|
||||||
|
|
||||||
auth_token,
|
auth_token,
|
||||||
is_auth_token_valid: AtomicBool::new(true),
|
is_auth_token_valid: AtomicBool::new(true),
|
||||||
|
|
||||||
|
only_use_restful_api: AtomicBool::new(false),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If you don't want to use GitHub GraphQL API for whatever reason, call this.
|
||||||
|
pub fn set_only_use_restful_api(&self) {
|
||||||
|
self.0.only_use_restful_api.store(true, Relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn remote_client(&self) -> &remote::Client {
|
pub fn remote_client(&self) -> &remote::Client {
|
||||||
&self.0.client
|
&self.0.client
|
||||||
}
|
}
|
||||||
|
@ -193,12 +202,13 @@ impl GhApiClient {
|
||||||
) -> Result<U, GhApiError>
|
) -> Result<U, GhApiError>
|
||||||
where
|
where
|
||||||
GraphQLFn: Fn(&remote::Client, &T, &str) -> GraphQLFut,
|
GraphQLFn: Fn(&remote::Client, &T, &str) -> GraphQLFut,
|
||||||
RestfulFn: Fn(&remote::Client, &T) -> RestfulFut,
|
RestfulFn: Fn(&remote::Client, &T, Option<&str>) -> RestfulFut,
|
||||||
GraphQLFut: Future<Output = Result<U, GhApiError>> + Send + Sync + 'static,
|
GraphQLFut: Future<Output = Result<U, GhApiError>> + Send + Sync + 'static,
|
||||||
RestfulFut: Future<Output = Result<U, GhApiError>> + Send + Sync + 'static,
|
RestfulFut: Future<Output = Result<U, GhApiError>> + Send + Sync + 'static,
|
||||||
{
|
{
|
||||||
self.check_retry_after()?;
|
self.check_retry_after()?;
|
||||||
|
|
||||||
|
if !self.0.only_use_restful_api.load(Relaxed) {
|
||||||
if let Some(auth_token) = self.get_auth_token() {
|
if let Some(auth_token) = self.get_auth_token() {
|
||||||
match graphql_func(&self.0.client, data, auth_token).await {
|
match graphql_func(&self.0.client, data, auth_token).await {
|
||||||
Err(GhApiError::Unauthorized) => {
|
Err(GhApiError::Unauthorized) => {
|
||||||
|
@ -207,8 +217,9 @@ impl GhApiClient {
|
||||||
res => return res.map_err(|err| err.context("GraphQL API")),
|
res => return res.map_err(|err| err.context("GraphQL API")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
restful_func(&self.0.client, data)
|
restful_func(&self.0.client, data, self.get_auth_token())
|
||||||
.await
|
.await
|
||||||
.map_err(|err| err.context("Restful API"))
|
.map_err(|err| err.context("Restful API"))
|
||||||
}
|
}
|
||||||
|
@ -313,6 +324,7 @@ impl GhApiClient {
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
use compact_str::{CompactString, ToCompactString};
|
use compact_str::{CompactString, ToCompactString};
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
use std::{env, num::NonZeroU16, time::Duration};
|
use std::{env, num::NonZeroU16, time::Duration};
|
||||||
use tokio::time::sleep;
|
use tokio::time::sleep;
|
||||||
use tracing::subscriber::set_global_default;
|
use tracing::subscriber::set_global_default;
|
||||||
|
@ -368,6 +380,7 @@ mod test {
|
||||||
tag: CompactString::new_inline("cargo-audit/v0.17.6"),
|
tag: CompactString::new_inline("cargo-audit/v0.17.6"),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[allow(unused)]
|
||||||
pub(super) const ARTIFACTS: &[&str] = &[
|
pub(super) const ARTIFACTS: &[&str] = &[
|
||||||
"cargo-audit-aarch64-unknown-linux-gnu-v0.17.6.tgz",
|
"cargo-audit-aarch64-unknown-linux-gnu-v0.17.6.tgz",
|
||||||
"cargo-audit-armv7-unknown-linux-gnueabihf-v0.17.6.tgz",
|
"cargo-audit-armv7-unknown-linux-gnueabihf-v0.17.6.tgz",
|
||||||
|
@ -490,14 +503,20 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create_remote_client() -> remote::Client {
|
fn create_remote_client() -> remote::Client {
|
||||||
|
static CLIENT: OnceCell<remote::Client> = OnceCell::new();
|
||||||
|
|
||||||
|
CLIENT
|
||||||
|
.get_or_init(|| {
|
||||||
remote::Client::new(
|
remote::Client::new(
|
||||||
concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")),
|
concat!(env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION")),
|
||||||
None,
|
None,
|
||||||
NonZeroU16::new(200).unwrap(),
|
NonZeroU16::new(300).unwrap(),
|
||||||
1.try_into().unwrap(),
|
1.try_into().unwrap(),
|
||||||
[],
|
[],
|
||||||
)
|
)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
})
|
||||||
|
.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Mark this as an async fn so that you won't accidentally use it in
|
/// Mark this as an async fn so that you won't accidentally use it in
|
||||||
|
@ -505,17 +524,24 @@ mod test {
|
||||||
fn create_client() -> Vec<GhApiClient> {
|
fn create_client() -> Vec<GhApiClient> {
|
||||||
let client = create_remote_client();
|
let client = create_remote_client();
|
||||||
|
|
||||||
let mut gh_clients = vec![GhApiClient::new(client.clone(), None)];
|
let auth_token = env::var("CI_UNIT_TEST_GITHUB_TOKEN")
|
||||||
|
.ok()
|
||||||
|
.map(CompactString::from);
|
||||||
|
|
||||||
if let Ok(token) = env::var("CI_UNIT_TEST_GITHUB_TOKEN") {
|
let gh_client = GhApiClient::new(client.clone(), auth_token.clone());
|
||||||
gh_clients.push(GhApiClient::new(client, Some(token.into())));
|
gh_client.set_only_use_restful_api();
|
||||||
|
|
||||||
|
let mut gh_clients = vec![gh_client];
|
||||||
|
|
||||||
|
if auth_token.is_some() {
|
||||||
|
gh_clients.push(GhApiClient::new(client, auth_token));
|
||||||
}
|
}
|
||||||
|
|
||||||
gh_clients
|
gh_clients
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_get_repo_info() {
|
async fn rate_limited_test_get_repo_info() {
|
||||||
const PUBLIC_REPOS: [GhRepo; 1] = [GhRepo {
|
const PUBLIC_REPOS: [GhRepo; 1] = [GhRepo {
|
||||||
owner: CompactString::new_inline("cargo-bins"),
|
owner: CompactString::new_inline("cargo-bins"),
|
||||||
repo: CompactString::new_inline("cargo-binstall"),
|
repo: CompactString::new_inline("cargo-binstall"),
|
||||||
|
@ -575,17 +601,11 @@ mod test {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test]
|
#[tokio::test]
|
||||||
async fn test_has_release_artifact_and_download_artifacts() {
|
async fn rate_limited_test_has_release_artifact_and_download_artifacts() {
|
||||||
const RELEASES: [(GhRelease, &[&str]); 2] = [
|
const RELEASES: [(GhRelease, &[&str]); 1] = [(
|
||||||
(
|
|
||||||
cargo_binstall_v0_20_1::RELEASE,
|
cargo_binstall_v0_20_1::RELEASE,
|
||||||
cargo_binstall_v0_20_1::ARTIFACTS,
|
cargo_binstall_v0_20_1::ARTIFACTS,
|
||||||
),
|
)];
|
||||||
(
|
|
||||||
cargo_audit_v_0_17_6::RELEASE,
|
|
||||||
cargo_audit_v_0_17_6::ARTIFACTS,
|
|
||||||
),
|
|
||||||
];
|
|
||||||
const NON_EXISTENT_RELEASES: [GhRelease; 1] = [GhRelease {
|
const NON_EXISTENT_RELEASES: [GhRelease; 1] = [GhRelease {
|
||||||
repo: GhRepo {
|
repo: GhRepo {
|
||||||
owner: CompactString::new_inline("cargo-bins"),
|
owner: CompactString::new_inline("cargo-bins"),
|
||||||
|
|
|
@ -38,6 +38,7 @@ fn get_api_endpoint() -> &'static Url {
|
||||||
pub(super) fn issue_restful_api<T>(
|
pub(super) fn issue_restful_api<T>(
|
||||||
client: &remote::Client,
|
client: &remote::Client,
|
||||||
path: &[&str],
|
path: &[&str],
|
||||||
|
auth_token: Option<&str>,
|
||||||
) -> impl Future<Output = Result<T, GhApiError>> + Send + Sync + 'static
|
) -> impl Future<Output = Result<T, GhApiError>> + Send + Sync + 'static
|
||||||
where
|
where
|
||||||
T: DeserializeOwned,
|
T: DeserializeOwned,
|
||||||
|
@ -50,11 +51,16 @@ where
|
||||||
|
|
||||||
debug!("Getting restful API: {url}");
|
debug!("Getting restful API: {url}");
|
||||||
|
|
||||||
let future = client
|
let mut request_builder = client
|
||||||
.get(url)
|
.get(url)
|
||||||
.header("Accept", "application/vnd.github+json")
|
.header("Accept", "application/vnd.github+json")
|
||||||
.header("X-GitHub-Api-Version", "2022-11-28")
|
.header("X-GitHub-Api-Version", "2022-11-28");
|
||||||
.send(false);
|
|
||||||
|
if let Some(auth_token) = auth_token {
|
||||||
|
request_builder = request_builder.bearer_auth(&auth_token);
|
||||||
|
}
|
||||||
|
|
||||||
|
let future = request_builder.send(false);
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
let response = check_http_status_and_header(future.await?)?;
|
let response = check_http_status_and_header(future.await?)?;
|
||||||
|
|
|
@ -73,8 +73,13 @@ pub(super) fn fetch_release_artifacts_restful_api(
|
||||||
repo: GhRepo { owner, repo },
|
repo: GhRepo { owner, repo },
|
||||||
tag,
|
tag,
|
||||||
}: &GhRelease,
|
}: &GhRelease,
|
||||||
|
auth_token: Option<&str>,
|
||||||
) -> impl Future<Output = Result<Artifacts, GhApiError>> + Send + Sync + 'static {
|
) -> impl Future<Output = Result<Artifacts, GhApiError>> + Send + Sync + 'static {
|
||||||
issue_restful_api(client, &["repos", owner, repo, "releases", "tags", tag])
|
issue_restful_api(
|
||||||
|
client,
|
||||||
|
&["repos", owner, repo, "releases", "tags", tag],
|
||||||
|
auth_token,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
|
|
@ -44,8 +44,9 @@ impl RepoInfo {
|
||||||
pub(super) fn fetch_repo_info_restful_api(
|
pub(super) fn fetch_repo_info_restful_api(
|
||||||
client: &remote::Client,
|
client: &remote::Client,
|
||||||
GhRepo { owner, repo }: &GhRepo,
|
GhRepo { owner, repo }: &GhRepo,
|
||||||
|
auth_token: Option<&str>,
|
||||||
) -> impl Future<Output = Result<Option<RepoInfo>, GhApiError>> + Send + Sync + 'static {
|
) -> impl Future<Output = Result<Option<RepoInfo>, GhApiError>> + Send + Sync + 'static {
|
||||||
issue_restful_api(client, &["repos", owner, repo])
|
issue_restful_api(client, &["repos", owner, repo], auth_token)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
|
|
Loading…
Add table
Reference in a new issue