Files
boxmaker/crates/gatewayd/tests/deliver_speaker.rs
T

122 lines
3.7 KiB
Rust

//! The speaker `deliver` is given goes on the turn it sends to `loopd`, and nowhere else: not into
//! the turn's text (M5c spec, section 11). Do not edit.
#[path = "support/fake_loop.rs"]
mod fake_loop;
#[path = "support/tmp.rs"]
mod tmp;
use std::sync::Mutex;
use std::time::Duration;
use bxcore::{ErrorCode, SessionId, Turn};
use fake_loop::{done, error, serve_loop};
use gatewayd::deliver::{Notice, Poster, deliver};
use gatewayd::mm::MmError;
use gatewayd::sessions::{Batch, Thread};
use tmp::TempDir;
const DM: &str = "d0000000000000000000000000";
const ROOT: &str = "r0000000000000000000000000";
const KYLE: &str = "k0000000000000000000000000";
const WAIT: Duration = Duration::from_secs(5);
#[derive(Default)]
struct Record(Mutex<Vec<String>>);
impl Poster for Record {
fn post(&self, _: &str, _: &str, text: &str) -> Result<(), MmError> {
self.0.lock().unwrap().push(text.to_string());
Ok(())
}
}
/// One `deliver` of "one\n\ntwo" with `speaker`, against a fake `loopd` whose n-th turn gets
/// `script(n)`; the turns it received and what was posted.
fn deliver_with(
tag: &str,
speaker: Option<&str>,
script: fn(usize) -> Vec<fake_loop::Reply>,
) -> (Vec<Turn>, Vec<String>) {
let dir = TempDir::new(tag);
let socket = dir.path().join("loop.sock");
let turns = serve_loop(&socket, move |n, _| script(n));
let batch = Batch {
session: SessionId::new(&format!("mm-{ROOT}")).unwrap(),
thread: Thread {
channel: DM.to_string(),
root: ROOT.to_string(),
},
resume: true,
text: "one\n\ntwo".to_string(),
senders: vec![KYLE.to_string(), KYLE.to_string()],
};
let never = |_: u64| false;
let notice = Notice {
posted: &never,
wait: Duration::ZERO,
};
let poster = Record::default();
let log = Mutex::new(Vec::new());
deliver(
&poster,
&socket,
&batch,
speaker,
&|line| log.lock().unwrap().push(line.to_string()),
&notice,
);
assert!(log.lock().unwrap().is_empty(), "{:?}", log.lock().unwrap());
// The fake reports each turn before it answers, so every turn is in by now.
let mut got = vec![turns.recv_timeout(WAIT).unwrap()];
while let Ok(turn) = turns.try_recv() {
got.push(turn);
}
(got, poster.0.into_inner().unwrap())
}
fn answer(_: usize) -> Vec<fake_loop::Reply> {
vec![done("fine")]
}
#[test]
fn the_speaker_goes_on_the_turn_and_not_into_its_text() {
let (turns, posts) = deliver_with("speaker-some", Some("first.last"), answer);
assert_eq!(turns.len(), 1);
let turn = &turns[0];
assert_eq!(turn.speaker.as_deref(), Some("first.last"));
assert_eq!(
(turn.content.as_str(), turn.resume),
("one\n\ntwo", true),
"the text is the batch's, unchanged"
);
assert_eq!(posts, ["fine"]);
}
#[test]
fn without_a_speaker_the_turn_has_none() {
let (turns, posts) = deliver_with("speaker-none", None, answer);
assert_eq!(turns.len(), 1);
assert_eq!(turns[0].speaker, None, "never the user id");
assert_eq!(turns[0].content, "one\n\ntwo");
assert_eq!(posts, ["fine"]);
}
#[test]
fn the_turn_sent_again_for_an_unknown_session_keeps_the_speaker() {
let unknown_first = |n: usize| {
if n == 0 {
vec![error(ErrorCode::NoSuchSession, "no such session")]
} else {
vec![done("hello")]
}
};
let (turns, posts) = deliver_with("speaker-again", Some("kyle"), unknown_first);
let seen: Vec<(bool, Option<&str>)> = turns
.iter()
.map(|t| (t.resume, t.speaker.as_deref()))
.collect();
assert_eq!(seen, [(true, Some("kyle")), (false, Some("kyle"))]);
assert_eq!(posts, ["hello"]);
}