use super::follow;
use crate::cli::Stream;
use crate::render::Form;
use crate::test_support::Scratch;
use crate::test_support::wire::{flat, wired};
use serde_json::{Value, json};
fn agent(state: &str) -> Value {
json!({"ok": true, "kind": "agent", "agent": "c-1", "root": "c-1",
"display": "PelicanQuiet", "display_only": false, "tip": "abc",
"state": state, "refused": false, "present": true,
"nudgeable": true, "stoppable": false, "stop_children": false,
"spend": {"tokens": {"input": 0, "output": 0, "cache_read": 0,
"cache_write": 0, "total": 0},
"attribution": {"kind": "own"}}})
}
fn inbox(deposits: usize) -> Value {
let rows: Vec<Value> = (0..deposits)
.map(|n| {
json!({"name": format!("{n}.md"), "raw": "body",
"deposit": {"body": "body"}})
})
.collect();
json!({"ok": true, "kind": "inbox", "rows": rows})
}
fn tail(text: &str) -> Value {
json!({"ok": true, "kind": "follow", "tools": [], "stream": {"delta": "text", "text": text}})
}
fn asked(engine: &crate::test_support::engine::Engine) -> Vec<Value> {
engine
.heard()
.into_iter()
.filter(|frame| frame.get("op").is_some())
.collect()
}
fn watched(scratch: &Scratch) -> (crate::cli::Verdict, Vec<String>) {
formed(scratch, Form::Rendered)
}
fn formed(scratch: &Scratch, form: Form) -> (crate::cli::Verdict, Vec<String>) {
let mut said: Vec<String> = Vec::new();
let verdict = follow(scratch.path(), "home", "PelicanQuiet", form, &mut |line| {
said.push(line.to_owned());
});
(verdict, said)
}
#[test]
fn a_quiescent_conversation_answers_at_once_and_succeeds() {
let scratch = Scratch::new();
let engine = wired(
&scratch,
&flat(),
vec![vec![agent("quiescent")], vec![inbox(0)]],
);
let (verdict, said) = watched(&scratch);
assert_eq!(verdict.code, 0);
assert_eq!(verdict.stream, Stream::Out);
assert!(
verdict.text.contains("is at rest (quiescent)"),
"{}",
verdict.text
);
assert!(
verdict.text.contains("nudged or messaged"),
"{}",
verdict.text
);
assert!(verdict.text.starts_with("┊ "), "{}", verdict.text);
let held = verdict
.text
.split(" after ")
.nth(1)
.and_then(|rest| rest.split("s — ").next())
.and_then(|secs| secs.parse::<u64>().ok());
assert!(held.is_some(), "{}", verdict.text);
assert!(said.is_empty(), "no tail was held: {said:?}");
assert_eq!(
asked(&engine),
vec![
json!({"op": "agent", "workspace": "home", "agent": "PelicanQuiet"}),
json!({"op": "inbox", "workspace": "home", "agent": "PelicanQuiet"}),
],
"it asked what the conversation was doing, and whether anything was waiting"
);
}
#[test]
fn a_read_that_ends_at_a_step_boundary_is_taken_up_again() {
let scratch = Scratch::new();
let engine = wired(
&scratch,
&flat(),
vec![
vec![agent("in-flight")],
vec![tail("The"), tail(" answer")],
vec![agent("in-flight")],
vec![tail(" is 391.")],
vec![agent("quiescent")],
],
);
let (verdict, said) = watched(&scratch);
assert_eq!(verdict.code, 0);
assert!(
verdict.text.contains("is at rest (quiescent)"),
"{}",
verdict.text
);
assert_eq!(said.len(), 3, "one printing per frame: {said:?}");
assert!(said.concat().contains(" is 391."), "{said:?}");
assert_eq!(
asked(&engine).len(),
5,
"three state reads and two held reads: {:?}",
asked(&engine)
);
}
#[test]
fn a_read_that_brought_nothing_waits_before_asking_again() {
let scratch = Scratch::new();
wired(
&scratch,
&flat(),
vec![vec![agent("live")], Vec::new(), vec![agent("stopped")]],
);
let began = std::time::Instant::now();
let (verdict, said) = watched(&scratch);
assert!(began.elapsed() >= super::SETTLE, "it did not settle");
assert_eq!(verdict.code, 0);
assert!(
verdict.text.contains("is at rest (stopped)"),
"{}",
verdict.text
);
assert!(said.is_empty(), "{said:?}");
}
#[test]
fn a_state_this_seat_does_not_know_ends_the_watch_and_says_the_word() {
let scratch = Scratch::new();
wired(&scratch, &flat(), vec![vec![agent("hibernating")]]);
let (verdict, _) = watched(&scratch);
assert_eq!(verdict.code, 0);
assert!(verdict.text.contains("(hibernating)"), "{}", verdict.text);
}
#[test]
fn an_answer_with_no_state_in_it_is_printed_and_fails() {
let scratch = Scratch::new();
wired(
&scratch,
&flat(),
vec![vec![
json!({"ok": false, "error": "unknown workspace \"hoem\""}),
]],
);
let (verdict, said) = watched(&scratch);
assert_eq!(verdict.code, 1);
assert_eq!(verdict.stream, Stream::Out);
assert!(
verdict.text.contains("unknown workspace"),
"{}",
verdict.text
);
assert!(said.is_empty(), "{said:?}");
}
#[test]
fn a_channel_that_will_not_answer_says_so_on_either_read() {
let scratch = Scratch::new();
let (verdict, _) = watched(&scratch);
assert_eq!(verdict.code, 1);
assert_eq!(verdict.stream, Stream::Err);
assert!(
verdict.text.contains("no wire provisioned"),
"{}",
verdict.text
);
let held = Scratch::new();
wired(
&held,
&flat(),
vec![
crate::test_support::engine::Answer::Frames(vec![agent("in-flight")]),
crate::test_support::engine::Answer::Hangup,
],
);
let (verdict, _) = watched(&held);
assert_eq!(verdict.code, 1);
assert_eq!(verdict.stream, Stream::Err);
}
mod waiting;