This repository has been archived on 2026-10-08. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
maestro/crates/maestro-e2e/src/build.rs
T
naps62-yolo 91f252b4e2
ci / rust (push) Successful in 2m8s
feat(maestro): add maestro-e2e cross-process test harness (NAP-217)
New `crates/maestro-e2e` exercises the full headless stack — registry,
two daemons, real HTTP+WS — as actual subprocesses.

Harness pieces:

- `Registry::spawn` and `Daemon::spawn` launch the real built binaries
  with `env_clear()` plus an isolated `$HOME`, `XDG_CONFIG_HOME`,
  `XDG_STATE_HOME`, and (for daemons) `TMUX_TMPDIR`. Drop guards kill
  the child and clean the temp dirs.
- `Daemon::stop` + `Daemon::restart` swap the process without losing
  the state dirs, so the recovery test exercises a real tmux
  re-adoption.
- A `build::workspace_target_bin` helper re-invokes cargo from the
  test process to build `maestrod` / `maestro-registry` on demand
  (cargo only ships libs as path-dep of e2e by default).
- `TestClient` wraps reqwest + tokio-tungstenite for the public
  HTTP+WS surface — list/spawn/kill/connect-info/attach + events WS.

Scenarios covered (`tests/e2e.rs`):

- list sessions from two daemons through one registry
- multi-attach: two WS clients see the same PTY output
- worktree with `cleanup_on_kill: true` is removed after kill
- daemon restart: catalog drops then repopulates from `recover()`
- concurrent spawns all get distinct ids

All five pass in ~0.7s in parallel; cleanup confirmed (no leftover
processes or temp dirs).
2026-05-26 07:38:19 +00:00

73 lines
2.7 KiB
Rust

//! Locate / build the maestro binaries needed by the harness.
//!
//! `cargo test -p maestro-e2e` does not, by default, rebuild bins from other
//! workspace crates. So we re-invoke cargo from the first test that needs a
//! binary and cache the resolved paths. The re-invocation acquires its own
//! workspace lock — by the time tests start, the outer cargo has already
//! released it.
use std::path::{Path, PathBuf};
use std::sync::OnceLock;
/// Absolute path of the workspace root (the dir containing the top-level
/// `Cargo.toml` whose body has `[workspace]`).
pub fn workspace_root() -> &'static Path {
static ROOT: OnceLock<PathBuf> = OnceLock::new();
ROOT.get_or_init(|| {
let manifest = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let mut cur: &Path = manifest.as_path();
loop {
let candidate = cur.join("Cargo.toml");
if candidate.exists() {
if let Ok(body) = std::fs::read_to_string(&candidate) {
if body.contains("[workspace]") {
return cur.to_path_buf();
}
}
}
match cur.parent() {
Some(p) => cur = p,
None => panic!(
"could not find workspace root starting from {}",
PathBuf::from(env!("CARGO_MANIFEST_DIR")).display()
),
}
}
})
}
/// Resolve `target/<profile>/<name>` under the workspace root, after running
/// `cargo build --bin <name> -p <package>` to make sure it exists.
pub fn workspace_target_bin(package: &str, bin: &str) -> PathBuf {
static BUILDS: OnceLock<std::sync::Mutex<Vec<String>>> = OnceLock::new();
let builds = BUILDS.get_or_init(|| std::sync::Mutex::new(Vec::new()));
let already = {
let lock = builds.lock().unwrap();
lock.iter().any(|b| b == bin)
};
let release = !cfg!(debug_assertions);
if !already {
let mut cmd = std::process::Command::new(env!("CARGO"));
cmd.args(["build", "--quiet", "-p", package, "--bin", bin]);
if release {
cmd.arg("--release");
}
let status = cmd
.current_dir(workspace_root())
.status()
.unwrap_or_else(|e| panic!("failed to invoke cargo build for {bin}: {e}"));
assert!(status.success(), "cargo build for `{bin}` failed");
builds.lock().unwrap().push(bin.to_string());
}
let profile = if release { "release" } else { "debug" };
let path = workspace_root().join("target").join(profile).join(bin);
assert!(
path.exists(),
"expected built binary at {} after `cargo build`",
path.display()
);
path
}