use crate::app::tests::Harness;
use crate::boundary::{Action, Gesture, codec};
use crate::watch::Mark;
use std::time::Duration;
fn posted(outbox: &crate::wire::post::Outbox) -> Vec<Action> {
let mut out = Vec::new();
while let Some((_, envelope)) = outbox.try_next() {
if let Ok(Gesture::Act(action)) = codec::decode(&envelope) {
out.push(action);
}
}
out
}
#[test]
fn a_send_at_the_unresolved_mint_is_taken_and_every_other_send_is_not() {
let h = Harness::new();
let (_c, mut rig) = h.model();
assert!(
!rig.model.hold_send(&h.ws, "stench-pug", "too early"),
"with nothing started there is no mint to aim at"
);
rig.model
.await_conversation(&h.ws, "stench-pug", "open the gate");
assert!(
!rig.model.hold_send(&h.ws, "c-1", "hello"),
"a conversation the world carries is addressed the ordinary way"
);
assert!(
!rig.model
.hold_send(std::path::Path::new("/elsewhere"), "stench-pug", "hello"),
"an echo belongs to the workspace it was fired in"
);
assert!(
rig.model
.hold_send(&h.ws, "stench-pug", "and another thing")
);
let queued = rig.model.echoed_pending("stench-pug", Vec::new());
let said: Vec<String> = queued.iter().map(|e| e.deposit.body.clone()).collect();
assert_eq!(said, ["open the gate", "and another thing"]);
assert!(
queued.iter().all(crate::inboxview::InboxEntry::in_memory),
"both are yog's own word, not disk's"
);
}
#[test]
fn the_held_sends_go_out_when_the_start_resolves_and_in_order() {
let h = Harness::new();
let (clock, mut rig) = h.model();
let (post, outbox) = crate::wire::post::pair();
rig.model.adopt_post(post);
rig.model
.await_conversation(&h.ws, "stench-pug", "open the gate");
assert!(rig.model.hold_send(&h.ws, "stench-pug", "first"));
assert!(rig.model.hold_send(&h.ws, "stench-pug", "second"));
rig.model.refresh();
assert!(
posted(&outbox).is_empty(),
"nothing is fired at a name that resolves nowhere"
);
h.build_more("c-2", "open the gate");
h.fx.name_agent("c-2", "stench-pug");
rig.model
.dirty_handle()
.mark_all([(h.ws.clone(), Mark::Watch)]);
rig.tick();
clock.advance(Duration::from_millis(150));
assert!(rig.tick(), "the roster carries the started root");
let acts = posted(&outbox);
assert_eq!(acts.len(), 2, "both, and neither twice: {acts:?}");
for (action, said) in acts.iter().zip(["first", "second"]) {
match action {
Action::Message { agent, content, .. } => {
assert_eq!(agent, "c-2", "addressed by the id, never the minted name");
assert_eq!(content, said, "and in the order the operator said them");
}
other => panic!("a held send is a message: {other:?}"),
}
}
rig.model.refresh();
assert!(
posted(&outbox).is_empty(),
"the hold is spent: a later frame posts nothing again"
);
}
#[test]
fn the_name_to_id_swap_is_readable_for_exactly_the_echos_remaining_life() {
let h = Harness::new();
let (clock, mut rig) = h.model();
assert_eq!(rig.model.adopted_names(), None, "nothing started");
rig.model
.await_conversation(&h.ws, "stench-pug", "open the gate");
rig.model.refresh();
assert_eq!(
rig.model.adopted_names(),
None,
"unresolved: the box is still keyed by the name and nothing has moved"
);
h.build_more("c-2", "open the gate");
h.fx.name_agent("c-2", "stench-pug");
rig.model
.dirty_handle()
.mark_all([(h.ws.clone(), Mark::Watch)]);
rig.tick();
clock.advance(Duration::from_millis(150));
rig.tick();
assert_eq!(
rig.model.adopted_names(),
Some(("stench-pug".to_owned(), "c-2".to_owned())),
"the swap, for as long as the echo it belongs to is alive"
);
rig.model.await_message(&h.ws, "c-1", "and again", 0);
assert_eq!(rig.model.adopted_names(), None);
}