use std::fs;
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf};
use std::sync::mpsc;
use std::time::Duration;
use tempfile::tempdir;
use super::Runs;
use crate::cli_outbound::{Chunk, Cli, ExitInfo, Streamed};
use crate::login::{LoginRun, LoginView};
fn workspace() -> PathBuf {
crate::test_support::world::fixture_workspace()
}
fn script(dir: &Path, name: &str, body: &str) -> PathBuf {
let path = dir.join(name);
fs::write(&path, body).expect("write");
fs::set_permissions(&path, fs::Permissions::from_mode(0o755)).expect("chmod");
path
}
fn wired(state_root: &Path) -> (LoginRun, mpsc::Sender<Chunk>) {
let (tx, rx) = mpsc::channel();
(
LoginRun::from_streamed(Streamed::from_rx(rx), vec!["bz".to_owned()], state_root),
tx,
)
}
fn settled(runs: &Runs, provider: &str) -> LoginView {
for _ in 0..600 {
let view = runs.standing(&workspace(), provider);
if view.outcome.is_some() {
return view;
}
std::thread::sleep(Duration::from_millis(5));
}
panic!("the reader thread never settled {provider}'s run");
}
#[test]
fn a_started_run_carries_the_wall_streams_to_the_buffer_and_settles_itself() {
let dir = tempdir().expect("tmp");
let world = crate::test_support::world::world_under(dir.path());
let state = tempdir().expect("tmp");
let bz = script(
dir.path(),
"bz",
"#!/bin/sh\nprintf '%s\\n' \"$YOG_WALL\" 1>&2\nexit 0\n",
);
let runs = Runs::of(Cli::new(bz));
let receipt = runs
.start(&world, &workspace(), "openai", state.path(), "100")
.expect("started");
assert_eq!(receipt.outcome, None, "the act never waits for the run");
let view = settled(&runs, "openai");
assert_eq!(view.outcome, Some(0));
assert_eq!(
view.lines
.iter()
.map(|l| l.text.clone())
.collect::<Vec<_>>(),
[crate::world::wall::root_of(&world, &workspace())
.display()
.to_string()],
"the child runs inside the NAMED workspace's wall (bl-fcd5)"
);
assert!(view.lines.iter().all(|l| l.err), "bz speaks on stderr");
assert_eq!(crate::opslog::tail(state.path(), 8).len(), 1);
let again = runs
.start(&world, &workspace(), "openai", state.path(), "101")
.expect("restarted");
assert_eq!(again.outcome, None, "the settled run was replaced");
assert_eq!(settled(&runs, "openai").outcome, Some(0));
}
#[test]
fn a_spawn_that_cannot_start_refuses_and_seats_nothing() {
let dir = tempdir().expect("tmp");
let world = crate::test_support::world::world_under(dir.path());
let state = tempdir().expect("tmp");
let runs = Runs::of(Cli::new("/definitely/not/a/real/bz-xyz"));
let refusal = runs
.start(&world, &workspace(), "openai", state.path(), "100")
.expect_err("an absent binary cannot start a sign-in");
assert!(!refusal.is_empty(), "the refusal names something");
assert_eq!(
runs.standing(&workspace(), "openai"),
LoginView::default(),
"a failed spawn leaves no run to follow"
);
}
#[test]
fn a_reader_reads_only_its_own_run_and_stops_when_it_is_replaced() {
let dir = tempdir().expect("tmp");
let (run, tx) = wired(dir.path());
let runs = Runs::default();
let first = runs.seat(&workspace(), "openai", run, 0);
tx.send(Chunk::Stderr(b"open https://x/auth\n".to_vec()))
.expect("send");
assert!(runs.read_once(&workspace(), "openai", first));
assert_eq!(
runs.standing(&workspace(), "openai").lines.len(),
1,
"the look put the line in the buffer"
);
let (second_run, second_tx) = wired(dir.path());
let second = runs.seat(&workspace(), "openai", second_run, 0);
assert!(!runs.read_once(&workspace(), "openai", first));
assert!(runs.read_once(&workspace(), "openai", second));
second_tx
.send(Chunk::Exited(ExitInfo::Code(0)))
.expect("send");
assert!(!runs.read_once(&workspace(), "openai", second));
assert_eq!(runs.standing(&workspace(), "openai").outcome, Some(0));
assert!(!runs.read_once(Path::new("/other"), "openai", second));
assert!(runs.frame(Path::new("/other"), "openai", 0).is_none());
assert_eq!(
runs.standing(Path::new("/other"), "openai"),
LoginView::default(),
"a pair with no run is emptiness, never a refusal"
);
}
#[test]
fn a_frame_is_what_the_standing_gained_since_the_frame_before_it() {
let dir = tempdir().expect("tmp");
let (run, tx) = wired(dir.path());
let runs = Runs::default();
let serial = runs.seat(&workspace(), "openai", run, 0);
tx.send(Chunk::Stderr(b"one\ntwo\n".to_vec()))
.expect("send");
runs.read_once(&workspace(), "openai", serial);
let whole = runs.frame(&workspace(), "openai", 0).expect("a run stands");
assert_eq!(whole.lines.len(), 2, "from zero, the whole buffer");
let tail = runs.frame(&workspace(), "openai", 1).expect("a run stands");
assert_eq!(tail.lines.len(), 1, "from one, the append");
assert!(
runs.frame(&workspace(), "openai", 9)
.expect("a run stands")
.lines
.is_empty()
);
}
#[test]
fn a_run_older_than_an_hour_is_swept_at_the_next_start() {
let dir = tempdir().expect("tmp");
let world = crate::test_support::world::world_under(dir.path());
let state = tempdir().expect("tmp");
let bz = script(dir.path(), "bz", "#!/bin/sh\nexit 0\n");
let runs = Runs::of(Cli::new(bz));
runs.seat(&workspace(), "stale", wired(dir.path()).0, 0);
runs.seat(&workspace(), "recent", wired(dir.path()).0, 1_000);
runs.start(&world, &workspace(), "openai", state.path(), "4000")
.expect("started");
assert_eq!(
runs.standing(&workspace(), "stale"),
LoginView::default(),
"an hour-old run is gone"
);
assert!(
runs.frame(&workspace(), "recent", 0).is_some(),
"a run inside the bound is untouched"
);
assert_eq!(settled(&runs, "openai").outcome, Some(0));
}