From e1960c4f3a7b3ef896752390a7765959a656930a Mon Sep 17 00:00:00 2001 From: Leynos Date: Sat, 2 Aug 2025 10:34:00 +0100 Subject: [PATCH 1/7] Refactor env guard and fix compilation Move environment variable helpers into the test-support crate so crates can share them without include hacks. Add missing dev dependencies and derive Clone for configuration to resolve compilation errors. Clean up unused imports and fix release workflow step. --- Cargo.lock | 1 + crates/comenq/src/lib.rs | 2 +- crates/comenqd/Cargo.toml | 2 + crates/comenqd/src/config.rs | 48 +------------------ test-support/Cargo.toml | 3 ++ .../support => test-support/src}/env_guard.rs | 0 test-support/src/lib.rs | 2 + tests/cucumber.rs | 1 - tests/steps/config_steps.rs | 2 +- tests/steps/release_steps.rs | 5 +- tests/support/mod.rs | 3 -- 11 files changed, 12 insertions(+), 57 deletions(-) rename {tests/support => test-support/src}/env_guard.rs (100%) delete mode 100644 tests/support/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 8e1ec7e..49982b6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2452,6 +2452,7 @@ version = "0.1.0" dependencies = [ "comenqd", "octocrab", + "serial_test", "tempfile", "tokio", "wiremock", diff --git a/crates/comenq/src/lib.rs b/crates/comenq/src/lib.rs index f575521..78e66b9 100644 --- a/crates/comenq/src/lib.rs +++ b/crates/comenq/src/lib.rs @@ -39,7 +39,7 @@ fn validate_repo_slug(s: &str) -> Result { mod tests { use super::Args; use clap::Parser; - use rstest::rstest; + use rstest::{case, rstest}; #[rstest] #[case("octocat/hello-world", 1, "Hi")] diff --git a/crates/comenqd/Cargo.toml b/crates/comenqd/Cargo.toml index a54dd01..aa6a747 100644 --- a/crates/comenqd/Cargo.toml +++ b/crates/comenqd/Cargo.toml @@ -23,4 +23,6 @@ figment = { version = "0.10", default-features = false, features = ["env", "toml rstest = { workspace = true } tempfile = { workspace = true } # latest 3.x at time of writing; update as new patch versions release serial_test = "2" +test-support = { path = "../../test-support" } +test-utils = { path = "../test-utils" } wiremock = "0.6" diff --git a/crates/comenqd/src/config.rs b/crates/comenqd/src/config.rs index 6f0bb6d..ca9bf29 100644 --- a/crates/comenqd/src/config.rs +++ b/crates/comenqd/src/config.rs @@ -118,53 +118,7 @@ mod tests { use std::fs; use tempfile::tempdir; - mod env_guard { - //! Test helpers for managing environment variables. - - #[derive(Debug)] - pub struct EnvVarGuard { - key: String, - original: Option, - } - - impl EnvVarGuard { - /// Set an environment variable for the lifetime of the returned guard. - pub fn set(key: &str, value: &str) -> Self { - let original = std::env::var(key).ok(); - set_env_var(key, value); - Self { - key: key.to_string(), - original, - } - } - } - - impl Drop for EnvVarGuard { - fn drop(&mut self) { - match &self.original { - Some(v) => set_env_var(&self.key, v), - None => remove_env_var(&self.key), - } - } - } - - /// Set an environment variable for tests. - /// - /// The nightly compiler marks `std::env::set_var` as `unsafe`. - /// Tests run serially so using it is acceptable here. - pub fn set_env_var(key: &str, value: &str) { - unsafe { std::env::set_var(key, value) }; - } - - /// Remove an environment variable for tests. - /// - /// `std::env::remove_var` is also `unsafe` on nightly. - pub fn remove_env_var(key: &str) { - unsafe { std::env::remove_var(key) }; - } - } - - use env_guard::{EnvVarGuard, remove_env_var}; + use test_support::env_guard::{EnvVarGuard, remove_env_var}; #[rstest] #[serial_test::serial] diff --git a/test-support/Cargo.toml b/test-support/Cargo.toml index ac68fa2..f2cab36 100644 --- a/test-support/Cargo.toml +++ b/test-support/Cargo.toml @@ -9,3 +9,6 @@ comenqd = { path = "../crates/comenqd" } octocrab = { workspace = true } tempfile = { workspace = true } wiremock = "^0.6" + +[dev-dependencies] +serial_test = "^2" diff --git a/tests/support/env_guard.rs b/test-support/src/env_guard.rs similarity index 100% rename from tests/support/env_guard.rs rename to test-support/src/env_guard.rs diff --git a/test-support/src/lib.rs b/test-support/src/lib.rs index e879eb7..52cbe23 100644 --- a/test-support/src/lib.rs +++ b/test-support/src/lib.rs @@ -1,10 +1,12 @@ //! Test support utilities. pub mod daemon; +pub mod env_guard; pub mod util; // Re-exports from daemon module (added in main) pub use daemon::{octocrab_for, temp_config}; +pub use env_guard::{EnvVarGuard, remove_env_var, set_env_var}; // Re-exports from util module with documentation (from your branch) /// Maximum number of times to poll for an expected file. diff --git a/tests/cucumber.rs b/tests/cucumber.rs index 5def2b1..b8ca602 100644 --- a/tests/cucumber.rs +++ b/tests/cucumber.rs @@ -4,7 +4,6 @@ //! parallel. mod steps; -mod support; mod util; use cucumber::World as _; use steps::{ diff --git a/tests/steps/config_steps.rs b/tests/steps/config_steps.rs index a03fbab..f6e43bf 100644 --- a/tests/steps/config_steps.rs +++ b/tests/steps/config_steps.rs @@ -5,8 +5,8 @@ use std::fs; use std::path::PathBuf; use tempfile::TempDir; -use crate::support::env_guard::{EnvVarGuard, remove_env_var}; use comenqd::config::Config; +use test_support::env_guard::{EnvVarGuard, remove_env_var}; #[derive(Debug, Default, World)] pub struct ConfigWorld { diff --git a/tests/steps/release_steps.rs b/tests/steps/release_steps.rs index 5726318..5cd5981 100644 --- a/tests/steps/release_steps.rs +++ b/tests/steps/release_steps.rs @@ -40,8 +40,5 @@ fn triggers_on_tags(world: &mut ReleaseWorld) { .expect("tags") .as_sequence() .expect("sequence"); - assert!( - tags.iter() - .any(|t| t.as_str() == Some("v[0-9]*.[0-9]*.[0-9]*")) - ); + assert!(tags.iter().any(|t| t.as_str() == Some("v*.*.*"))); } diff --git a/tests/support/mod.rs b/tests/support/mod.rs deleted file mode 100644 index 0ab1eed..0000000 --- a/tests/support/mod.rs +++ /dev/null @@ -1,3 +0,0 @@ -//! Support utilities shared by tests. - -pub mod env_guard; From 9d2eef2941a69f8e496ba28300c01b65a961a76b Mon Sep 17 00:00:00 2001 From: Leynos Date: Sun, 3 Aug 2025 17:34:31 +0100 Subject: [PATCH 2/7] Remove unused rstest imports --- crates/comenq/src/lib.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/crates/comenq/src/lib.rs b/crates/comenq/src/lib.rs index 78e66b9..754dd4a 100644 --- a/crates/comenq/src/lib.rs +++ b/crates/comenq/src/lib.rs @@ -39,11 +39,15 @@ fn validate_repo_slug(s: &str) -> Result { mod tests { use super::Args; use clap::Parser; - use rstest::{case, rstest}; + use rstest::rstest; #[rstest] - #[case("octocat/hello-world", 1, "Hi")] - fn parses_valid_arguments(#[case] slug: &str, #[case] pr: u64, #[case] body: &str) { + #[rstest::case("octocat/hello-world", 1, "Hi")] + fn parses_valid_arguments( + #[rstest::case] slug: &str, + #[rstest::case] pr: u64, + #[rstest::case] body: &str, + ) { let pr_str = pr.to_string(); let args = Args::try_parse_from(["comenq", slug, &pr_str, body]); let args = args.expect("valid arguments should parse"); @@ -53,11 +57,11 @@ mod tests { } #[rstest] - #[case("octocat")] - #[case("/repo")] - #[case("owner/")] - #[case("owner/repo/extra")] - fn rejects_invalid_slug(#[case] slug: &str) { + #[rstest::case("octocat")] + #[rstest::case("/repo")] + #[rstest::case("owner/")] + #[rstest::case("owner/repo/extra")] + fn rejects_invalid_slug(#[rstest::case] slug: &str) { let result = Args::try_parse_from(["comenq", slug, "1", "Hi"]); assert!(result.is_err()); } From 47fbcdb637362b557f7d08bf2cacd6b51ec48144 Mon Sep 17 00:00:00 2001 From: Leynos Date: Sun, 3 Aug 2025 17:56:39 +0100 Subject: [PATCH 3/7] Align test deps and tag validation --- Cargo.lock | 1 + Cargo.toml | 7 +++++-- crates/comenq/src/lib.rs | 20 ++++++++------------ crates/comenqd/Cargo.toml | 2 +- crates/comenqd/src/daemon.rs | 2 +- tests/steps/release_steps.rs | 9 ++++++++- 6 files changed, 24 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 49982b6..74e0c0d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -324,6 +324,7 @@ dependencies = [ "cucumber", "octocrab", "ortho_config", + "regex", "serde", "serde_json", "serde_yaml", diff --git a/Cargo.toml b/Cargo.toml index 0fdfd2d..deaecef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,11 +18,12 @@ clap = { workspace = true } comenq = { path = "crates/comenq" } comenqd = { path = "crates/comenqd" } ortho_config = { git = "https://github.com/leynos/ortho-config.git", tag = "v0.4.0" } -tempfile = "3.10" # latest 3.x at time of writing; update as new patch versions release +tempfile = { workspace = true } # latest 3.x at time of writing; update as new patch versions release yaque = { workspace = true } -wiremock = "0.6" +wiremock = { workspace = true } octocrab = { workspace = true } test-support = { path = "test-support" } +regex = { workspace = true } [[test]] name = "cucumber" @@ -51,7 +52,9 @@ thiserror = "1.0" ortho_config = { git = "https://github.com/leynos/ortho-config.git", tag = "v0.4.0" } serde_yaml = "0.9" tempfile = "3.10" +regex = "1" rstest = "0.18.0" +wiremock = "0.6" [lints.clippy] pedantic = { level = "warn", priority = -1 } diff --git a/crates/comenq/src/lib.rs b/crates/comenq/src/lib.rs index 754dd4a..78e66b9 100644 --- a/crates/comenq/src/lib.rs +++ b/crates/comenq/src/lib.rs @@ -39,15 +39,11 @@ fn validate_repo_slug(s: &str) -> Result { mod tests { use super::Args; use clap::Parser; - use rstest::rstest; + use rstest::{case, rstest}; #[rstest] - #[rstest::case("octocat/hello-world", 1, "Hi")] - fn parses_valid_arguments( - #[rstest::case] slug: &str, - #[rstest::case] pr: u64, - #[rstest::case] body: &str, - ) { + #[case("octocat/hello-world", 1, "Hi")] + fn parses_valid_arguments(#[case] slug: &str, #[case] pr: u64, #[case] body: &str) { let pr_str = pr.to_string(); let args = Args::try_parse_from(["comenq", slug, &pr_str, body]); let args = args.expect("valid arguments should parse"); @@ -57,11 +53,11 @@ mod tests { } #[rstest] - #[rstest::case("octocat")] - #[rstest::case("/repo")] - #[rstest::case("owner/")] - #[rstest::case("owner/repo/extra")] - fn rejects_invalid_slug(#[rstest::case] slug: &str) { + #[case("octocat")] + #[case("/repo")] + #[case("owner/")] + #[case("owner/repo/extra")] + fn rejects_invalid_slug(#[case] slug: &str) { let result = Args::try_parse_from(["comenq", slug, "1", "Hi"]); assert!(result.is_err()); } diff --git a/crates/comenqd/Cargo.toml b/crates/comenqd/Cargo.toml index aa6a747..2a93bdf 100644 --- a/crates/comenqd/Cargo.toml +++ b/crates/comenqd/Cargo.toml @@ -25,4 +25,4 @@ tempfile = { workspace = true } # latest 3.x at time of writing; update as new p serial_test = "2" test-support = { path = "../../test-support" } test-utils = { path = "../test-utils" } -wiremock = "0.6" +wiremock = { workspace = true } diff --git a/crates/comenqd/src/daemon.rs b/crates/comenqd/src/daemon.rs index 4df9ac4..5d3fb28 100644 --- a/crates/comenqd/src/daemon.rs +++ b/crates/comenqd/src/daemon.rs @@ -287,7 +287,7 @@ mod tests { use std::sync::Arc; use tempfile::{TempDir, tempdir}; use tokio::io::AsyncWriteExt; - use tokio::net::UnixStream; + use tokio::net::{UnixListener, UnixStream}; use tokio::sync::{mpsc, watch}; use tokio::time::{Duration, sleep}; use wiremock::matchers::{method, path}; diff --git a/tests/steps/release_steps.rs b/tests/steps/release_steps.rs index 5cd5981..d9df276 100644 --- a/tests/steps/release_steps.rs +++ b/tests/steps/release_steps.rs @@ -3,6 +3,7 @@ use comenq_lib::workflow::uses_goreleaser as workflow_uses_goreleaser; use cucumber::{World, given, then, when}; +use regex::Regex; use serde_yaml::Value; use std::fs; @@ -40,5 +41,11 @@ fn triggers_on_tags(world: &mut ReleaseWorld) { .expect("tags") .as_sequence() .expect("sequence"); - assert!(tags.iter().any(|t| t.as_str() == Some("v*.*.*"))); + let pattern = Regex::new(r"^v\*\.\*\.\*$").expect("compile regex"); + assert!( + tags.iter() + .filter_map(|t| t.as_str()) + .any(|t| pattern.is_match(t)), + "missing semantic version tag pattern", + ); } From 032a791d98d64ab35883ff3cb408ecadc38e8652 Mon Sep 17 00:00:00 2001 From: Leynos Date: Sun, 3 Aug 2025 18:54:51 +0100 Subject: [PATCH 4/7] Update lock file --- Cargo.lock | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 74e0c0d..d8b8f94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -350,6 +350,8 @@ dependencies = [ "serde_json", "serial_test", "tempfile", + "test-support", + "test-utils", "thiserror 1.0.69", "tokio", "tracing", From b20f9aa26bb6926814edaaafcd5d28cbb7337de0 Mon Sep 17 00:00:00 2001 From: Leynos Date: Sun, 3 Aug 2025 18:55:11 +0100 Subject: [PATCH 5/7] Use correct version pattern --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 9c9850b..87b2816 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,7 +4,7 @@ on: push: tags: # Match semantic version tags (e.g. v1.2.3, v10.11.12, v12.3.7-beta7) - - 'v[0-9]*.[0-9]*.[0-9]*' + - 'v*.*.*' jobs: goreleaser: From a86e2727ad3462f97e005cd3c7d3572891709ac8 Mon Sep 17 00:00:00 2001 From: Leynos Date: Sun, 3 Aug 2025 18:59:33 +0100 Subject: [PATCH 6/7] Remove unneeded import --- crates/comenq/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/comenq/src/lib.rs b/crates/comenq/src/lib.rs index 78e66b9..f575521 100644 --- a/crates/comenq/src/lib.rs +++ b/crates/comenq/src/lib.rs @@ -39,7 +39,7 @@ fn validate_repo_slug(s: &str) -> Result { mod tests { use super::Args; use clap::Parser; - use rstest::{case, rstest}; + use rstest::rstest; #[rstest] #[case("octocat/hello-world", 1, "Hi")] From b33662ede60a79437f0fe20fe34c69621935379c Mon Sep 17 00:00:00 2001 From: Payton McIntosh Date: Sun, 3 Aug 2025 19:14:29 +0100 Subject: [PATCH 7/7] Remove unused UnixListener import Remove correct UnixListener --- crates/comenqd/src/daemon.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/comenqd/src/daemon.rs b/crates/comenqd/src/daemon.rs index 5d3fb28..4df9ac4 100644 --- a/crates/comenqd/src/daemon.rs +++ b/crates/comenqd/src/daemon.rs @@ -287,7 +287,7 @@ mod tests { use std::sync::Arc; use tempfile::{TempDir, tempdir}; use tokio::io::AsyncWriteExt; - use tokio::net::{UnixListener, UnixStream}; + use tokio::net::UnixStream; use tokio::sync::{mpsc, watch}; use tokio::time::{Duration, sleep}; use wiremock::matchers::{method, path};