use super::*;
use crate::tool_host::tests::{budget, impatient, scripted, site, tool};
use serde_json::json;
use std::time::Duration;
use tempfile::TempDir;
fn entry() -> loaded::Entry {
loaded::Entry {
client: "laptop".to_owned(),
tool: tool("Bash"),
}
}
fn quiet() -> AtomicBool {
AtomicBool::new(false)
}
#[test]
fn the_tool_bound_is_longer_than_the_engine_bound() {
let (tool_bound, engine_bound) = (patience(), Budget::default());
assert!(tool_bound.tick * tool_bound.waits > engine_bound.tick * engine_bound.waits);
}
#[test]
fn the_poll_waits_for_a_capture_that_is_not_there_yet() {
let root = TempDir::new().expect("tmp");
let (handle, seen) = scripted(
root.path(),
&[
json!({"ok": true, "kind": "routed", "invocation": "inv-7"}),
json!({"ok": true, "kind": "routed", "invocation": "inv-7"}),
json!({"ok": true, "kind": "routed", "invocation": "inv-7",
"capture": {"stdout": "done", "stderr": "", "exit_code": 0}}),
],
);
let mut s = site(root.path(), budget());
s.patience = Budget {
waits: 8,
tick: Duration::from_millis(1),
};
let got = invoke(&s, &entry(), &json!({"command": "ls"}), None, &quiet());
handle.join().expect("engine");
assert_eq!(
got,
Ok(Capture {
stdout: "done".to_owned(),
stderr: String::new(),
exit_code: 0,
})
);
assert_eq!(seen.iter().count(), 3, "one queue, two polls");
}
#[test]
fn a_machine_that_never_answers_runs_out_and_says_so() {
let root = TempDir::new().expect("tmp");
let (handle, _seen) = scripted(
root.path(),
&[
json!({"ok": true, "kind": "routed", "invocation": "inv-7"}),
json!({"ok": true, "kind": "routed", "invocation": "inv-7"}),
],
);
let mut s = site(root.path(), budget());
s.patience = Budget {
waits: 1,
tick: Duration::ZERO,
};
let e = invoke(&s, &entry(), &json!({}), None, &quiet()).expect_err("nothing answered");
handle.join().expect("engine");
assert!(e.contains("inv-7") && e.contains("laptop"), "{e}");
}
#[test]
fn a_stop_ends_the_wait_on_the_tool() {
let root = TempDir::new().expect("tmp");
let stop = AtomicBool::new(false);
let mut s = site(root.path(), budget());
s.patience = Budget {
waits: 400,
tick: Duration::from_millis(1),
};
let got = std::thread::scope(|scope| {
scope.spawn(|| {
for served in 0..2 {
for _ in 0..40_000 {
if let Some((id, _)) = crate::boundary::deposit::pending(root.path())
.into_iter()
.next()
{
let _ = crate::boundary::deposit::claim(root.path(), &id);
if served == 1 {
stop.store(true, Ordering::Relaxed);
}
let _ = crate::boundary::deposit::write_reply(
root.path(),
&id,
&json!({"ok": true, "kind": "routed", "invocation": "inv-7"}),
);
break;
}
std::thread::sleep(Duration::from_millis(1));
}
}
});
invoke(&s, &entry(), &json!({}), None, &stop)
});
let said = format!("{got:?}");
assert!(
got.is_err_and(|e| e == "stopped while waiting on laptop"),
"the stop is named, and named the one way: {said}"
);
}
#[test]
fn an_answer_that_is_not_a_routed_invocation_names_itself() {
let root = TempDir::new().expect("tmp");
for (reply, needle) in [
(json!({"ok": true, "kind": "acked"}), "not a routed"),
(json!({"ok": false, "error": "no such client"}), "no such"),
(json!({"ok": true, "kind": "teleported"}), "undecodable"),
] {
let (handle, _seen) = scripted(root.path(), &[reply]);
let e = invoke(
&site(root.path(), budget()),
&entry(),
&json!({}),
None,
&quiet(),
)
.expect_err("not an invocation");
handle.join().expect("engine");
assert!(e.contains(needle), "{e}");
}
}
#[test]
fn no_engine_is_a_sentence_at_the_first_ask() {
let root = TempDir::new().expect("tmp");
let e = invoke(
&site(root.path(), impatient()),
&entry(),
&json!({}),
None,
&quiet(),
)
.expect_err("no consumer");
assert!(e.contains("no engine answered"), "{e}");
}