test(cli): extract the shared demo-project fixture helpers into tests/common (#223)

built_project/project_lock/ScratchPath/ScratchGuard move from their three
per-binary copies (cli_run.rs, research_docs.rs, project_load.rs's
built_fixture near-copy) into tests/common/mod.rs; project_load's one
shared-store-mutating test now takes the lock it previously lacked. The
module doc states the boundary plainly: the Mutex serializes threads
within one process only — process-parallel runners still race on the
shared fixture store (documented, dormant under cargo test's sequential
binaries; see the issue thread for the observed process-parallel repro).

closes #223
This commit is contained in:
2026-07-11 18:18:27 +02:00
parent 18f8e72946
commit 2532e8fcc3
4 changed files with 96 additions and 111 deletions
+4 -30
View File
@@ -3,7 +3,10 @@
use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::{Mutex, MutexGuard, OnceLock};
use std::sync::MutexGuard;
mod common;
use common::{built_project, project_lock};
/// Path to the freshly-built `aura` binary (Cargo sets this env var for the test
/// crate; the binary is named `aura` in `Cargo.toml`).
@@ -19,35 +22,6 @@ fn temp_cwd(name: &str) -> std::path::PathBuf {
dir
}
/// The demo-project fixture (Aura.toml + a built cdylib present), built once —
/// mirrors research_docs.rs's helper (a separate test binary, so it needs its
/// own `OnceLock`); `cargo build` is idempotent, so a second binary building
/// the same fixture is a no-op.
fn built_project() -> &'static PathBuf {
static BUILT: OnceLock<PathBuf> = OnceLock::new();
BUILT.get_or_init(|| {
let dir = Path::new(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures/demo-project");
let out = std::process::Command::new("cargo")
.arg("build")
.current_dir(&dir)
.output()
.expect("spawn cargo build for the fixture project");
assert!(
out.status.success(),
"fixture build failed:\n{}",
String::from_utf8_lossy(&out.stderr)
);
dir
})
}
/// Serializes every test that runs a verb inside the shared fixture (they reset
/// `<fixture>/runs`, so parallel threads would race). Poison-tolerant.
fn project_lock() -> MutexGuard<'static, ()> {
static LOCK: Mutex<()> = Mutex::new(());
LOCK.lock().unwrap_or_else(|e| e.into_inner())
}
/// Removes `<fixture>/runs` on drop (including mid-panic) so a migrated test never
/// leaks its content-addressed store into the git-tracked fixture tree.
struct RunsCleanup(PathBuf);