use anyhow::Context as AnyhowContext; use log::error; use solstice_proto::server::RoomJoinRequest; use solstice_proto::ServerRequest; use crate::context::Context; use crate::control; use crate::message_handler::MessageHandler; #[derive(Debug, Default)] pub struct RoomJoinRequestHandler; fn start_joining(context: &mut Context, room_name: &str) -> anyhow::Result<()> { let room = context.state.rooms.get_mut_strict(room_name)?; room.start_joining().map_err(|err| { let response = control::Response::RoomJoinResponse(control::RoomJoinResponse { room_name: room_name.to_string(), room: room.clone_state(), }); if let Err(err) = context.control_response_tx.blocking_send(response) { error!( "Failed to send RoomJoinResponse for room {}: {}", room_name, err ); } err.into() }) } impl MessageHandler for RoomJoinRequestHandler { type Message = String; fn run( self, context: &mut Context, room_name: &Self::Message, ) -> anyhow::Result<()> { start_joining(context, room_name).context("joining room")?; context .server_request_tx .blocking_send(ServerRequest::RoomJoinRequest(RoomJoinRequest { room_name: room_name.clone(), })) .context("sending server request")?; Ok(()) } fn name() -> String { "RoomJoinRequestHandler".to_string() } } #[cfg(test)] mod tests { use anyhow::Context; use solstice_proto::server::RoomJoinRequest; use solstice_proto::ServerRequest; use crate::context::{ContextBundle, ContextOptions}; use crate::control; use crate::message_handler::MessageHandler; use crate::room::{RoomMembership, RoomState, RoomVisibility}; use super::RoomJoinRequestHandler; #[test] fn run_failure() -> anyhow::Result<()> { let mut bundle = ContextBundle::default(); RoomJoinRequestHandler::default() .run(&mut bundle.context, &"bleep".to_string()) .unwrap_err(); // Room state has not changed. assert_eq!(bundle.context.state.rooms.get_room_list(), vec![]); // Close the channel, so we can observe it was empty without hanging. drop(bundle.context.server_request_tx); assert_eq!(bundle.server_request_rx.blocking_recv(), None); Ok(()) } #[test] fn run_already_joined_responds_immediately() -> anyhow::Result<()> { let mut room = RoomState::new(RoomVisibility::Public, 3); room.membership = RoomMembership::Member; let mut options = ContextOptions::default(); options .initial_state .rooms .insert("bleep".to_string(), room.clone()); let mut bundle = ContextBundle::new(options); RoomJoinRequestHandler::default() .run(&mut bundle.context, &"bleep".to_string()) .unwrap_err(); // Room state has not changed. assert_eq!( bundle.context.state.rooms.get_room_list(), vec![("bleep".to_string(), room.clone())] ); assert_eq!( bundle.control_response_rx.blocking_recv(), Some(control::Response::RoomJoinResponse( control::RoomJoinResponse { room_name: "bleep".to_string(), room, } )) ); Ok(()) } #[test] fn run_success() -> anyhow::Result<()> { let mut options = ContextOptions::default(); options.initial_state.rooms.insert( "bleep".to_string(), RoomState::new(RoomVisibility::Public, 3), ); let mut bundle = ContextBundle::new(options); RoomJoinRequestHandler::default() .run(&mut bundle.context, &"bleep".to_string()) .context("running handler")?; let request = bundle.server_request_rx.blocking_recv().unwrap(); // Room state has been altered to reflect the request. assert_eq!( bundle .context .state .rooms .get_strict("bleep") .context("getting room")? .clone_state() .membership, RoomMembership::Joining ); // The request is forwarded onwards. assert_eq!( request, ServerRequest::RoomJoinRequest(RoomJoinRequest { room_name: "bleep".to_string(), }) ); Ok(()) } }