diff --git a/crates/repx-runner/tests/harness.rs b/crates/repx-runner/tests/harness.rs index a342daf..6425c96 100644 --- a/crates/repx-runner/tests/harness.rs +++ b/crates/repx-runner/tests/harness.rs @@ -3,6 +3,9 @@ use assert_cmd::Command as AssertCommand; use repx_test_utils::harness::TestContext; use std::ops::{Deref, DerefMut}; +use std::path::PathBuf; +use std::process::Command; +use std::sync::OnceLock; pub struct TestHarness { pub context: TestContext, @@ -27,10 +30,9 @@ impl TestHarness { } } - #[allow(clippy::expect_used, deprecated)] + #[allow(clippy::expect_used)] pub fn cmd(&self) -> AssertCommand { - let mut cmd = AssertCommand::cargo_bin("repx") - .expect("repx binary not found — ensure repx-cli is built"); + let mut cmd = AssertCommand::new(repx_binary_path()); cmd.env("XDG_CONFIG_HOME", &self.context.config_dir); cmd.env("RUST_BACKTRACE", "1"); cmd.arg("--lab").arg(&self.context.lab_path); @@ -40,6 +42,34 @@ impl TestHarness { } } +#[allow(clippy::expect_used)] +fn repx_binary_path() -> PathBuf { + static BINARY: OnceLock = OnceLock::new(); + BINARY + .get_or_init(|| { + let cargo = std::env::var_os("CARGO").unwrap_or_else(|| "cargo".into()); + let status = Command::new(&cargo) + .args(["build", "-p", "repx-cli", "--bin", "repx"]) + .status() + .expect("failed to invoke `cargo build -p repx-cli`"); + assert!(status.success(), "`cargo build -p repx-cli` failed"); + + let test_exe = std::env::current_exe().expect("current_exe"); + let target_profile = test_exe + .parent() + .and_then(|p| p.parent()) + .expect("test exe has no grandparent"); + let bin = target_profile.join(format!("repx{}", std::env::consts::EXE_SUFFIX)); + assert!( + bin.is_file(), + "repx binary missing after build: {}", + bin.display() + ); + bin + }) + .clone() +} + impl Default for TestHarness { fn default() -> Self { Self::new()