diff --git a/Cargo.lock b/Cargo.lock index 68660db..8e1ec7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -349,12 +349,11 @@ dependencies = [ "serde_json", "serial_test", "tempfile", - "test-support", - "test-utils", "thiserror 1.0.69", "tokio", "tracing", "tracing-subscriber", + "wiremock", "yaque", ] diff --git a/crates/comenqd/Cargo.toml b/crates/comenqd/Cargo.toml index 181c610..a54dd01 100644 --- a/crates/comenqd/Cargo.toml +++ b/crates/comenqd/Cargo.toml @@ -23,5 +23,4 @@ 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 8ff9b63..6f0bb6d 100644 --- a/crates/comenqd/src/config.rs +++ b/crates/comenqd/src/config.rs @@ -20,7 +20,7 @@ const DEFAULT_QUEUE_PATH: &str = "/var/lib/comenq/queue"; const DEFAULT_COOLDOWN: u64 = 960; /// Runtime configuration for the daemon. -#[derive(Debug, Deserialize, Serialize, PartialEq, Eq)] +#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, Clone)] pub struct Config { /// GitHub Personal Access Token. pub github_token: String, @@ -119,17 +119,52 @@ mod tests { use tempfile::tempdir; mod env_guard { - include!(concat!( - env!("CARGO_MANIFEST_DIR"), - "/../../tests/support/env_guard.rs" - )); - } + //! 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, + } + } + } - pub mod support { - pub use super::env_guard::{EnvVarGuard, remove_env_var, set_env_var}; + 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 support::{EnvVarGuard, remove_env_var}; + use env_guard::{EnvVarGuard, remove_env_var}; #[rstest] #[serial_test::serial] diff --git a/crates/comenqd/src/daemon.rs b/crates/comenqd/src/daemon.rs index 24fd3f2..90b8018 100644 --- a/crates/comenqd/src/daemon.rs +++ b/crates/comenqd/src/daemon.rs @@ -281,15 +281,27 @@ pub async fn run_worker( mod tests { //! Tests for the daemon tasks. use super::*; - use tempfile::tempdir; - use test_support::wait_for_file; - use test_utils::{octocrab_for, temp_config}; + use octocrab::Octocrab; + use std::fs as stdfs; + use std::path::Path; + 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}; use wiremock::{Mock, MockServer, ResponseTemplate}; + use yaque::Receiver; + fn temp_config(tmp: &TempDir) -> Config { + Config { + github_token: String::from("t"), + socket_path: tmp.path().join("sock"), + queue_path: tmp.path().join("q"), + cooldown_period_seconds: 1, + } + } + fn cfg_with_cooldown(dir: &TempDir, secs: u64) -> Config { Config { cooldown_period_seconds: secs, @@ -297,6 +309,28 @@ mod tests { } } + #[expect(clippy::expect_used, reason = "simplify test helper setup")] + fn octocrab_for(server: &MockServer) -> Arc { + Arc::new( + Octocrab::builder() + .personal_token("t".to_string()) + .base_uri(server.uri()) + .expect("base_uri") + .build() + .expect("build octocrab"), + ) + } + + async fn wait_for_file(path: &Path, tries: u32, delay: Duration) -> bool { + for _ in 0..tries { + if path.exists() { + return true; + } + sleep(delay).await; + } + path.exists() + } + async fn setup_run_worker(status: u16) -> (MockServer, Arc, Receiver, Arc) { let dir = tempdir().expect("tempdir"); let cfg = Arc::new(Config { @@ -364,7 +398,7 @@ mod tests { let dir = tempdir().expect("tempdir"); let queue_path = dir.path().join("q"); let (sender, mut receiver) = channel(&queue_path).expect("channel"); - let (client_tx, mut writer_rx) = mpsc::unbounded_channel(); + let (client_tx, writer_rx) = mpsc::unbounded_channel(); let writer = tokio::spawn(queue_writer(sender, writer_rx)); let (mut client, server) = UnixStream::pair().expect("pair"); diff --git a/crates/comenqd/src/logging.rs b/crates/comenqd/src/logging.rs index 14162c0..3c0ea55 100644 --- a/crates/comenqd/src/logging.rs +++ b/crates/comenqd/src/logging.rs @@ -20,7 +20,6 @@ where fmt() .with_env_filter(EnvFilter::from_default_env()) .with_writer(writer) - .json() .init(); } @@ -66,7 +65,7 @@ mod tests { #[test] fn init_logging() { let buf = Arc::new(Mutex::new(Vec::new())); - std::env::set_var("RUST_LOG", "info"); + unsafe { std::env::set_var("RUST_LOG", "info") }; init_with_writer(BufMakeWriter { buf: buf.clone() }); info!("captured"); let output = String::from_utf8(buf.lock().expect("Failed to lock log buffer").clone())