Add test for joining an existing room

- Send affiliation/role in occupant presence. Always the same for now.
- Send one presence per occupant (might changer again later on)
- Store subject in Room. Store participant/date alongside subject

Signed-off-by: Maxime “pep” Buquet <pep@bouah.net>
This commit is contained in:
Maxime “pep” Buquet 2022-09-11 16:33:44 +02:00
parent d1d7aea45b
commit b7ee30c3f7
Signed by: pep
GPG key ID: DEDA74AEECA9D0F2
2 changed files with 134 additions and 24 deletions

View file

@ -39,6 +39,9 @@ pub type Nick = String;
pub struct Room {
pub jid: BareJid,
pub occupants: HashMap<BareJid, Occupant>,
// TODO: Subject struct.
// TODO: Store subject lang
pub subject: Option<(String, Occupant, DateTime)>,
}
impl Room {
@ -46,6 +49,7 @@ impl Room {
Room {
jid,
occupants: HashMap::new(),
subject: None,
}
}
@ -61,6 +65,8 @@ impl Room {
} else {
debug!("{} is joining {}", realjid, self.jid);
let new_occupant = Occupant::new(&self, realjid.clone(), nick.clone());
// Ensure nick isn't already assigned
let _ = self.occupants.iter().try_for_each(|(_, occupant)| {
let nick = nick.clone();
@ -72,18 +78,21 @@ impl Room {
// Send occupants
debug!("Sending occupants for {}", realjid);
let presence = Presence::new(PresenceType::None).with_to(realjid.clone());
let presence = Presence::new(PresenceType::None)
// New occupant with a single session
.with_to(new_occupant.sessions[0].clone())
.with_payloads(vec![MucUser {
status: Vec::new(),
items: vec![MucItem::new(Affiliation::Owner, Role::Moderator)],
}.into()]);
for (_, occupant) in self.occupants.iter() {
for session in occupant.iter() {
let presence = presence.clone().with_from(session.clone());
component.send_stanza(presence).await?;
}
let presence = presence.clone()
.with_from(occupant.participant.clone());
component.send_stanza(presence).await?;
}
// Add into occupants
let _ = self
.occupants
.insert(bare.clone(), Occupant::new(realjid.clone(), nick.clone()));
let _ = self.occupants.insert(bare.clone(), new_occupant.clone());
// Self-presence
debug!("Sending self-presence for {}", realjid);
@ -98,15 +107,25 @@ impl Room {
// Send subject
debug!("Sending subject!");
if self.subject.is_none() {
let subject = String::from("");
let setter = new_occupant;
let stamp = DateTime::from_utc(chrono::Utc::now());
self.subject = Some((subject, setter, stamp));
}
let mut subject = Message::new(Some(Jid::Full(realjid)));
subject.from = Some(Jid::Full(participant));
subject
.subjects
.insert(String::from("en"), Subject(String::from("")));
subject.from = Some(Jid::Full(
self.subject.as_ref().unwrap().1.participant.clone(),
));
subject.subjects.insert(
String::from("en"),
Subject(self.subject.as_ref().unwrap().0.clone()),
);
subject.type_ = MessageType::Groupchat;
subject.payloads = vec![Delay {
from: Some(Jid::Bare(self.jid.clone())),
stamp: DateTime::from_utc(chrono::Utc::now()),
stamp: self.subject.as_ref().unwrap().2.clone(),
data: None,
}
.into()];
@ -119,23 +138,26 @@ impl Room {
#[derive(Debug, Clone)]
pub struct Occupant {
jid: BareJid,
/// Public Jid for the Occupant
real: BareJid,
participant: FullJid,
nick: Nick,
sessions: Vec<FullJid>,
}
impl Occupant {
fn new(fulljid: FullJid, nick: Nick) -> Occupant {
fn new(room: &Room, real: FullJid, nick: Nick) -> Occupant {
Occupant {
jid: BareJid::from(fulljid.clone()),
real: BareJid::from(real.clone()),
participant: room.jid.clone().with_resource(nick.clone()),
nick,
sessions: vec![fulljid],
sessions: vec![real],
}
}
pub fn add_session(&mut self, fulljid: FullJid) -> Result<(), Error> {
if BareJid::from(fulljid.clone()) != self.jid {
return Err(Error::MismatchJids(Jid::from(fulljid.clone())));
pub fn add_session(&mut self, real: FullJid) -> Result<(), Error> {
if BareJid::from(real.clone()) != self.real {
return Err(Error::MismatchJids(Jid::from(real.clone())));
}
Ok(())
@ -161,13 +183,14 @@ impl Occupant {
mod tests {
use super::*;
use std::str::FromStr;
use xmpp_parsers::FullJid;
use xmpp_parsers::{BareJid, FullJid};
#[test]
fn occupant_ignore_dup_session() {
let fulljid = FullJid::from_str("foo@bar/meh").unwrap();
let mut occupant = Occupant::new(fulljid.clone(), String::from("nick"));
occupant.add_session(fulljid.clone()).unwrap();
let room = Room::new(BareJid::from_str("room@muc").unwrap());
let real = FullJid::from_str("foo@bar/meh").unwrap();
let mut occupant = Occupant::new(&room, real.clone(), String::from("nick"));
occupant.add_session(real.clone()).unwrap();
assert_eq!(occupant.iter().count(), 1);
}
}

View file

@ -201,3 +201,90 @@ async fn test_join_presence_nick_already_assigned() {
None => panic!(),
}
}
#[tokio::test]
async fn test_join_presence_existing_room() {
let realjid1 = FullJid::from_str("foo@bar/qxx").unwrap();
let realjid2 = FullJid::from_str("qxx@bar/foo").unwrap();
let roomjid = COMPONENT_JID.clone().with_node("room");
let participant1 = roomjid.clone().with_resource("nick1");
let participant2 = roomjid.clone().with_resource("nick2");
let join1: Element = Presence::new(PresenceType::None)
.with_from(Jid::Full(realjid1.clone()))
.with_to(Jid::Full(participant1.clone()))
.with_payloads(vec![Muc::new().into()])
.into();
let join2: Element = Presence::new(PresenceType::None)
.with_from(Jid::Full(realjid2.clone()))
.with_to(Jid::Full(participant2.clone()))
.with_payloads(vec![Muc::new().into()])
.into();
let mut component = TestComponent::new(vec![join1, join2]);
let mut rooms: HashMap<BareJid, Room> = HashMap::new();
// Ignore self-presence for first participant
component.expect_with(|_| ());
// Ignore message subject for first participant
component.expect_with(|_| ());
// Occupant presences for participant2
component.expect(
Presence::new(PresenceType::None)
.with_from(Jid::Full(participant1.clone()))
.with_to(Jid::Full(realjid2.clone()))
.with_payloads(vec![MucUser {
status: Vec::new(),
items: vec![MucItem::new(Affiliation::Owner, Role::Moderator)],
}.into()])
);
// self-presence for participant2
component.expect(
Presence::new(PresenceType::None)
.with_from(Jid::Full(participant2))
.with_to(Jid::Full(realjid2.clone()))
.with_payloads(vec![MucUser {
status: vec![MucStatus::SelfPresence, MucStatus::AssignedNick],
items: vec![MucItem::new(Affiliation::Owner, Role::Moderator)],
}.into()])
);
// Subject for participant2
component.expect_with(|el| {
let mut subjects = BTreeMap::new();
subjects.insert(String::from("en"), MessageSubject::from_str("").unwrap());
let expected: Element = Message {
// Set by the first participant
from: Some(Jid::Full(participant1)),
to: Some(Jid::Full(realjid2)),
id: None,
type_: MessageType::Groupchat,
bodies: BTreeMap::new(),
subjects,
thread: None,
payloads: Vec::new(),
}.into();
let mut out = Message::try_from(el).unwrap();
// Check that <delay/> is present. Don't check content as it's checked in a test earlier
assert_eq!(out.payloads.len(), 1);
let _delay = Delay::try_from(out.payloads[0].clone()).unwrap();
out.payloads = vec![];
assert_eq!(
String::from(&expected),
String::from(&Into::<Element>::into(out))
);
});
handle_stanza(&mut component, &mut rooms).await.unwrap();
component.assert();
match rooms.get(&roomjid) {
Some(room) => assert_eq!(room.occupants.len(), 2),
None => panic!(),
}
}