use super::*;
use crate::opslog;
use crate::test_support::{OkProbe, spawn_guard};
use crate::world::toolgate::{self, Refusal, ToolProbe, ToolState};
use std::fs;
use std::os::unix::fs::PermissionsExt;
use std::path::PathBuf;
use std::sync::MutexGuard;
use tempfile::{TempDir, tempdir};
mod dispatch;
mod edit;
fn ok_gate() -> ToolchainState {
toolgate::probe(&OkProbe)
}
struct Stale;
impl ToolProbe for Stale {
fn status(&self, binary: Binary, args: &[&str]) -> std::io::Result<i32> {
let bad = (binary == Binary::Lernie && args.first() == Some(&"prime"))
|| (binary == Binary::Bl && args.first() == Some(&"create"));
Ok(i32::from(bad) * 2)
}
}
#[test]
fn a_mismatch_gate_refuses_the_dispatch_before_spawning() {
let (w, _g) = World::new("bl", OK_BODY);
let gate = toolgate::probe(&Stale);
let err = close(&gate, &w.cli, w.state.path(), "TS", &w.cwd, "bl-7", "me").unwrap_err();
let refusal = err.get_ref().unwrap().downcast_ref::<Refusal>().unwrap();
assert_eq!(refusal.tool, "bl");
assert!(matches!(&refusal.state, ToolState::Mismatch { verb, .. } if verb == "create"));
let e = w.logged();
assert_eq!(e.argv, ["yog-step", "gate"]);
assert!(e.stderr.contains("bl: missing verb `create`"));
}
fn recorder(dir: &Path, name: &str, body: &str) -> (PathBuf, MutexGuard<'static, ()>) {
let guard = spawn_guard();
let path = dir.join(name);
fs::write(&path, body).unwrap();
let mut perms = fs::metadata(&path).unwrap().permissions();
perms.set_mode(0o755);
fs::set_permissions(&path, perms).unwrap();
(path, guard)
}
const OK_BODY: &str = "#!/bin/sh\nprintf 'OUT\\n'\nprintf 'ERR\\n' 1>&2\nexit 0\n";
struct World {
_dir: TempDir,
state: TempDir,
cli: Cli,
cwd: PathBuf,
}
impl World {
fn new(name: &str, body: &str) -> (Self, MutexGuard<'static, ()>) {
let dir = tempdir().unwrap();
let (bin, guard) = recorder(dir.path(), name, body);
let cwd = dir.path().to_path_buf();
(
Self {
_dir: dir,
state: tempdir().unwrap(),
cli: Cli::new(bin),
cwd,
},
guard,
)
}
fn logged(&self) -> crate::opslog::OpEntry {
let mut entries = opslog::tail(self.state.path(), 8);
assert_eq!(entries.len(), 1, "exactly one op logged");
entries.pop().unwrap()
}
}
fn args_of(e: &crate::opslog::OpEntry) -> Vec<String> {
e.argv[1..].to_vec()
}
#[test]
fn message_builds_argv_and_runs_in_the_workspace() {
let (w, _g) = World::new("lernie", OK_BODY);
let ws = &w.cwd;
message(
&ok_gate(),
&w.cli,
w.state.path(),
"TS",
ws,
"a-1",
"hi there",
)
.unwrap();
let e = w.logged();
assert_eq!(
args_of(&e),
vec!["message", &ws.display().to_string(), "a-1", "hi there"]
);
assert_eq!(e.cwd, ws.display().to_string());
}
#[test]
fn stop_omits_and_includes_the_children_flag() {
let (w, _g) = World::new("lernie", OK_BODY);
let ws = w.cwd.clone();
stop(&ok_gate(), &w.cli, w.state.path(), "TS", &ws, "a-1", false).unwrap();
assert_eq!(
args_of(&w.logged()),
vec!["stop", &ws.display().to_string(), "a-1"]
);
stop(&ok_gate(), &w.cli, w.state.path(), "TS", &ws, "a-1", true).unwrap();
let e = opslog::tail(w.state.path(), 8).pop().unwrap();
assert_eq!(
args_of(&e),
vec!["stop", &ws.display().to_string(), "a-1", "--stop-children"]
);
}
#[test]
fn scan_builds_argv() {
let (w, _g) = World::new("lernie", OK_BODY);
let ws = &w.cwd;
scan(&ok_gate(), &w.cli, w.state.path(), "TS", ws).unwrap();
assert_eq!(
args_of(&w.logged()),
vec!["scan", &ws.display().to_string()]
);
}
#[test]
fn close_and_unclaim_build_argv_in_the_project() {
let (w, _g) = World::new("bl", OK_BODY);
let proj = &w.cwd;
close(
&ok_gate(),
&w.cli,
w.state.path(),
"TS",
proj,
"bl-7",
"filtered",
)
.unwrap();
let e = w.logged();
assert_eq!(args_of(&e), vec!["close", "bl-7", "--as", "filtered"]);
assert_eq!(e.cwd, proj.display().to_string());
unclaim(
&ok_gate(),
&w.cli,
w.state.path(),
"TS",
proj,
"bl-7",
"filtered",
)
.unwrap();
let e = opslog::tail(w.state.path(), 8).pop().unwrap();
assert_eq!(args_of(&e), vec!["unclaim", "bl-7", "--as", "filtered"]);
}
#[test]
fn assign_claims_the_ball_for_the_target_workspace() {
let (w, _g) = World::new("bl", OK_BODY);
let proj = &w.cwd;
assign(
&ok_gate(),
&w.cli,
w.state.path(),
"TS",
proj,
"bl-7",
"cobalt-gecko",
)
.unwrap();
let e = w.logged();
assert_eq!(args_of(&e), vec!["claim", "bl-7", "--as", "cobalt-gecko"]);
assert_eq!(e.cwd, proj.display().to_string());
}
#[test]
fn reassign_unclaims_from_then_claims_to_both_logged() {
let (w, _g) = World::new("bl", OK_BODY);
let proj = &w.cwd;
reassign(
&ok_gate(),
&w.cli,
(w.state.path(), "TS"),
proj,
"bl-7",
"amber-toad",
"cobalt-gecko",
)
.unwrap();
let ops = opslog::tail(w.state.path(), 8);
assert_eq!(ops.len(), 2, "both spawns logged");
assert_eq!(
args_of(&ops[0]),
vec!["unclaim", "bl-7", "--as", "amber-toad"]
);
assert_eq!(
args_of(&ops[1]),
vec!["claim", "bl-7", "--as", "cobalt-gecko"]
);
}