diff --git a/crates/mergify-ci/src/detector.rs b/crates/mergify-ci/src/detector.rs index ab0830c8..5bb4109b 100644 --- a/crates/mergify-ci/src/detector.rs +++ b/crates/mergify-ci/src/detector.rs @@ -144,32 +144,7 @@ fn read_github_event_pull_request_number() -> Result, CliError> { #[cfg(test)] mod tests { use super::*; - - /// Clear every CI-provider env var the detector inspects, then - /// apply the test-specific overrides on top. Without this, a - /// test running on a real CI host inherits provider state and - /// the detector picks the wrong branch. - pub(crate) fn with_ci_env R, R>(extra: &[(&str, Option<&str>)], f: F) -> R { - let mut vars: Vec<(String, Option)> = [ - "JENKINS_URL", - "GITHUB_ACTIONS", - "GITHUB_REPOSITORY", - "GITHUB_EVENT_PATH", - "CIRCLECI", - "CIRCLE_REPOSITORY_URL", - "BUILDKITE", - "BUILDKITE_REPO", - "BUILDKITE_PULL_REQUEST", - "GIT_URL", - ] - .into_iter() - .map(|k| (k.to_string(), None)) - .collect(); - for (k, v) in extra { - vars.push((k.to_string(), v.map(ToString::to_string))); - } - temp_env::with_vars(vars, f) - } + use crate::testing::with_ci_env; #[test] fn ci_provider_jenkins_takes_precedence() { diff --git a/crates/mergify-ci/src/lib.rs b/crates/mergify-ci/src/lib.rs index b06c9f3e..c197ee7f 100644 --- a/crates/mergify-ci/src/lib.rs +++ b/crates/mergify-ci/src/lib.rs @@ -13,3 +13,6 @@ pub mod github_event; pub mod queue_info; pub mod queue_metadata; pub mod scopes_send; + +#[cfg(test)] +mod testing; diff --git a/crates/mergify-ci/src/scopes_send.rs b/crates/mergify-ci/src/scopes_send.rs index 52bb3666..bd1b3dd7 100644 --- a/crates/mergify-ci/src/scopes_send.rs +++ b/crates/mergify-ci/src/scopes_send.rs @@ -156,58 +156,8 @@ mod tests { use wiremock::matchers::path; use super::*; - - /// Clear every CI-provider env var the resolver inspects, then - /// apply the test-specific overrides on top. Without this, a test - /// running on a real CI host (Buildkite, Actions, …) inherits - /// provider env vars and the new provider-aware resolver picks - /// the wrong branch. - fn with_ci_env R, R>(extra: &[(&str, Option<&str>)], f: F) -> R { - let mut vars: Vec<(String, Option)> = [ - "JENKINS_URL", - "GITHUB_ACTIONS", - "GITHUB_REPOSITORY", - "GITHUB_EVENT_PATH", - "CIRCLECI", - "CIRCLE_REPOSITORY_URL", - "BUILDKITE", - "BUILDKITE_REPO", - "BUILDKITE_PULL_REQUEST", - "GIT_URL", - ] - .into_iter() - .map(|k| (k.to_string(), None)) - .collect(); - for (k, v) in extra { - vars.push((k.to_string(), v.map(ToString::to_string))); - } - temp_env::with_vars(vars, f) - } - - async fn with_ci_env_async, R>( - extra: &[(&str, Option<&str>)], - f: F, - ) -> R { - let mut vars: Vec<(String, Option)> = [ - "JENKINS_URL", - "GITHUB_ACTIONS", - "GITHUB_REPOSITORY", - "GITHUB_EVENT_PATH", - "CIRCLECI", - "CIRCLE_REPOSITORY_URL", - "BUILDKITE", - "BUILDKITE_REPO", - "BUILDKITE_PULL_REQUEST", - "GIT_URL", - ] - .into_iter() - .map(|k| (k.to_string(), None)) - .collect(); - for (k, v) in extra { - vars.push((k.to_string(), v.map(ToString::to_string))); - } - temp_env::async_with_vars(vars, f).await - } + use crate::testing::with_ci_env; + use crate::testing::with_ci_env_async; #[test] fn resolve_repository_prefers_flag_over_env() { diff --git a/crates/mergify-ci/src/testing.rs b/crates/mergify-ci/src/testing.rs new file mode 100644 index 00000000..979c2ce7 --- /dev/null +++ b/crates/mergify-ci/src/testing.rs @@ -0,0 +1,56 @@ +//! Test-only helpers shared between `detector` and `scopes_send`. +//! +//! Both modules test CI-provider-aware code paths and need to scrub +//! the host's CI env vars before running each case — otherwise a +//! test running on a real Buildkite/Actions/Circle/Jenkins host +//! inherits provider state and the detector picks the wrong branch. +//! Two flavors: a sync `with_ci_env` and an async `with_ci_env_async` +//! (used by the `#[tokio::test]` cases in `scopes_send`). + +use std::future::Future; + +/// Env vars the CI-provider detection chain inspects. Clear every +/// one of them before applying the test-specific overrides, so the +/// host environment can't leak into the test. +const CI_ENV_VARS: &[&str] = &[ + "JENKINS_URL", + "GITHUB_ACTIONS", + "GITHUB_REPOSITORY", + "GITHUB_EVENT_PATH", + "CIRCLECI", + "CIRCLE_REPOSITORY_URL", + "BUILDKITE", + "BUILDKITE_REPO", + "BUILDKITE_PULL_REQUEST", + "GIT_URL", +]; + +fn merged_overrides(extra: &[(&str, Option<&str>)]) -> Vec<(String, Option)> { + let mut vars: Vec<(String, Option)> = CI_ENV_VARS + .iter() + .map(|k| ((*k).to_string(), None)) + .collect(); + for (k, v) in extra { + vars.push(((*k).to_string(), v.map(ToString::to_string))); + } + vars +} + +/// Run `f` with the CI-provider env vars cleared, plus the +/// `extra` overrides applied on top. +pub(crate) fn with_ci_env(extra: &[(&str, Option<&str>)], f: F) -> R +where + F: FnOnce() -> R, +{ + temp_env::with_vars(merged_overrides(extra), f) +} + +/// Async counterpart to [`with_ci_env`]. Used by `#[tokio::test]` +/// cases in `scopes_send` — the sync variant can't bridge `.await` +/// points. +pub(crate) async fn with_ci_env_async(extra: &[(&str, Option<&str>)], f: F) -> R +where + F: Future, +{ + temp_env::async_with_vars(merged_overrides(extra), f).await +}