gatewayd: a turn's speaker, the username of the one person who wrote its burst

Co-Authored-By: ornith-1.5-35b-a3b <agent@wntrmute.dev>
This commit is contained in:
2026-09-28 10:51:17 -07:00
co-authored by ornith-1.5-35b-a3b
parent 4866353ac9
commit fae12c3fe2
12 changed files with 503 additions and 15 deletions
+91 -4
View File
@@ -1,5 +1,6 @@
//! Which posts become turns, in which session, and how messages wait for a running turn (M4a spec,
//! section 7, including its table of examples). Do not edit.
//! section 7, including its table of examples), with the author of each (M5c spec, section 11).
//! Do not edit.
use bxcore::SessionId;
use gatewayd::mm::Post;
@@ -179,6 +180,7 @@ fn sessions_and_threads() {
);
assert!(!top.resume && !top.joins_thread);
assert_eq!(top.text, "hello");
assert_eq!(top.user, KYLE);
let reply = queued(r.route(&post(DM, ROOT, "more"), "D", &unknown));
assert_eq!(reply.session.as_str(), format!("mm-{ROOT}"));
assert_eq!(reply.thread.root, ROOT);
@@ -244,6 +246,10 @@ fn commands() {
}
fn message(root: &str, resume: bool, text: &str) -> Message {
message_by(KYLE, root, resume, text)
}
fn message_by(user: &str, root: &str, resume: bool, text: &str) -> Message {
Message {
session: SessionId::new(&format!("mm-{root}")).unwrap(),
thread: Thread {
@@ -253,9 +259,14 @@ fn message(root: &str, resume: bool, text: &str) -> Message {
resume,
text: text.to_string(),
joins_thread: false,
user: user.to_string(),
}
}
fn senders(batch: &Batch) -> Vec<&str> {
batch.senders.iter().map(String::as_str).collect()
}
#[test]
fn messages_wait_for_the_running_turn_and_go_together() {
let mut q = Queues::new(3);
@@ -269,7 +280,8 @@ fn messages_wait_for_the_running_turn_and_go_together() {
session: first.session.clone(),
thread: first.thread.clone(),
resume: false,
text: "one".to_string()
text: "one".to_string(),
senders: vec![KYLE.to_string()],
}
);
assert_eq!(q.push(message(ROOT, true, "two")), Pushed::Waiting);
@@ -305,10 +317,85 @@ fn a_full_queue_drops_the_message() {
assert_eq!(q.push(message(ROOT, true, "a")), Pushed::Waiting);
assert_eq!(q.push(message(ROOT, true, "b")), Pushed::Waiting);
assert_eq!(
q.push(message(ROOT, true, "c")),
q.push(message_by(EVE, ROOT, true, "c")),
Pushed::Full(m.thread.clone())
);
assert_eq!(q.finish(&m.session).unwrap().text, "a\n\nb");
let next = q.finish(&m.session).unwrap();
assert_eq!(next.text, "a\n\nb");
assert_eq!(
senders(&next),
[KYLE, KYLE],
"the dropped message's author is not kept"
);
assert_eq!(next.sender(), Some(KYLE));
assert!(!BUSY.is_empty());
assert_eq!(q.finish(&SessionId::new("mm-never").unwrap()), None);
}
#[test]
fn a_queued_message_names_its_author() {
let r = Router::new(
BOT,
"boxmaker-straylight",
&[KYLE.to_string(), EVE.to_string()],
&[SHARED.to_string()],
);
for (text, expected) in [
("hello", "hello"),
("!!approve is a word", "!approve is a word"),
] {
let mut from_eve = post(DM, ROOT, text);
from_eve.user_id = EVE.to_string();
let m = queued(r.route(&from_eve, "D", &unknown));
assert_eq!(
(m.user.as_str(), m.text.as_str()),
(EVE, expected),
"{text}"
);
}
}
#[test]
fn a_batch_carries_the_author_of_each_message() {
let mut q = Queues::new(5);
let Pushed::Start(first) = q.push(message(ROOT, false, "one")) else {
panic!("not started")
};
assert_eq!((senders(&first), first.sender()), (vec![KYLE], Some(KYLE)));
assert_eq!(q.push(message_by(EVE, ROOT, true, "two")), Pushed::Waiting);
assert_eq!(q.push(message(ROOT, true, "three")), Pushed::Waiting);
let session = SessionId::new(&format!("mm-{ROOT}")).unwrap();
let mixed = q.finish(&session).unwrap();
assert_eq!(mixed.text, "two\n\nthree", "the text is joined as before");
assert_eq!(senders(&mixed), [EVE, KYLE]);
assert_eq!(mixed.sender(), None, "two people: no one sender");
assert_eq!(q.push(message_by(EVE, ROOT, true, "four")), Pushed::Waiting);
assert_eq!(q.push(message_by(EVE, ROOT, true, "five")), Pushed::Waiting);
let one = q.finish(&session).unwrap();
assert_eq!(
(senders(&one), one.sender()),
(vec![EVE, EVE], Some(EVE)),
"each batch has only its own messages' authors"
);
assert_eq!(q.finish(&session), None);
}
#[test]
fn a_batch_with_no_authors_has_no_sender() {
let batch = Batch {
session: SessionId::new(&format!("mm-{ROOT}")).unwrap(),
thread: Thread {
channel: DM.to_string(),
root: ROOT.to_string(),
},
resume: false,
text: "orphan".to_string(),
senders: Vec::new(),
};
assert_eq!(batch.sender(), None);
let three = Batch {
senders: vec![KYLE.to_string(), KYLE.to_string(), EVE.to_string()],
..batch
};
assert_eq!(three.sender(), None, "the last one differs");
}