diff --git a/proto/src/server/testing/mod.rs b/proto/src/server/testing/mod.rs new file mode 100644 index 0000000..341eb0f --- /dev/null +++ b/proto/src/server/testing/mod.rs @@ -0,0 +1,12 @@ +//! Testing utilities for server protocol code. + +mod server; +mod user_status; + +pub use server::{ + ClientHandler, ClientHandlerFactory, LogHandler, LogHandlerFactory, + RequestHandler, Server, ServerBuilder, ServerHandle, ShutdownType, +}; +pub use user_status::{ + UserStatusHandler, UserStatusHandlerFactory, UserStatusMap, +}; diff --git a/proto/src/server/testing.rs b/proto/src/server/testing/server.rs similarity index 82% rename from proto/src/server/testing.rs rename to proto/src/server/testing/server.rs index aed1ca1..3b984de 100644 --- a/proto/src/server/testing.rs +++ b/proto/src/server/testing/server.rs @@ -1,13 +1,10 @@ //! Provides utilities for testing protocol code. -use std::collections::HashMap; use std::io; use std::net::{IpAddr, Ipv4Addr, SocketAddr}; -use std::sync::Arc; use anyhow::{bail, Context}; use log::{info, warn}; -use parking_lot::Mutex; use tokio::net::tcp::{OwnedReadHalf, OwnedWriteHalf}; use tokio::net::{TcpListener, TcpStream}; use tokio::sync::mpsc; @@ -15,10 +12,7 @@ use tokio::sync::oneshot; use tokio::sync::watch; use crate::core::{FrameReader, FrameWriter, Worker}; -use crate::server::{ - LoginResponse, ServerRequest, ServerResponse, UserStatusRequest, - UserStatusResponse, -}; +use crate::server::{LoginResponse, ServerRequest, ServerResponse}; /// A handler for a single client connected to the server. pub trait ClientHandler: Send { @@ -374,98 +368,11 @@ impl ClientHandlerFactory for LogHandlerFactory { } } -// USER STATUS HANDLER - -/// A map of user names to user status responses to serve. -#[derive(Debug, Default)] -pub struct UserStatusMap { - map: HashMap, -} - -// IDEA: impl FromIterator for UserStatusMap. - -impl UserStatusMap { - /// Inserts the given `response` in this map. - /// Overwrites the previous entry for the same user name, if any. - pub fn insert(&mut self, response: UserStatusResponse) { - self.map.insert(response.user_name.clone(), response); - } - - /// Returns a clone of the response for the given user name. - pub fn get(&self, user_name: &str) -> Option { - self.map.get(user_name).map(|response| response.clone()) - } -} - -/// A handler that can serve responses to user status requests. -/// Responses are sent only for users who appear in `user_status_map`. -/// All other requests are ignored. -#[derive(Clone, Default)] -pub struct UserStatusHandler { - user_status_map: Arc>, -} - -impl RequestHandler for UserStatusHandler { - fn handle( - &mut self, - request: &ServerRequest, - ) -> anyhow::Result> { - let user_name = match request { - ServerRequest::UserStatusRequest(UserStatusRequest { ref user_name }) => { - user_name - } - _ => { - warn!("Unhandled request: {:?}", request); - return Ok(None); - } - }; - - let entry = self.user_status_map.lock().get(user_name); - if let Some(response) = entry { - Ok(Some(ServerResponse::UserStatusResponse(response))) - } else { - warn!("Received UserStatusRequest for unknown user {}", user_name); - Ok(None) - } - } -} - -/// A factory for `UserStatusHandler`. All handlers share `user_status_map`. -#[derive(Default)] -pub struct UserStatusHandlerFactory { - /// The status map from which responses are served. - /// - /// Testing code may wish to retain a copy of this in order to mutate the map - /// concurrently with requests being handled. - pub user_status_map: Arc>, -} - -impl ClientHandlerFactory for UserStatusHandlerFactory { - type Handler = UserStatusHandler; - - fn make(&self) -> Self::Handler { - Self::Handler { - user_status_map: self.user_status_map.clone(), - } - } -} - -impl UserStatusHandlerFactory { - /// Convenience function to create a new factory wrapping the given `map`. - pub fn new(map: UserStatusMap) -> Self { - Self { - user_status_map: Arc::new(Mutex::new(map)), - } - } -} - #[cfg(test)] mod tests { use tokio::net::TcpStream; - use super::{ - LogHandlerFactory, ServerBuilder, ShutdownType, UserStatusHandlerFactory, - }; + use super::{LogHandlerFactory, ServerBuilder, ShutdownType}; // Enable capturing logs in tests. fn init() { diff --git a/proto/src/server/testing/user_status.rs b/proto/src/server/testing/user_status.rs new file mode 100644 index 0000000..426738b --- /dev/null +++ b/proto/src/server/testing/user_status.rs @@ -0,0 +1,93 @@ +use std::collections::HashMap; +use std::sync::Arc; + +use log::warn; +use parking_lot::Mutex; + +use crate::server::testing::{ClientHandlerFactory, RequestHandler}; +use crate::server::{ + ServerRequest, ServerResponse, UserStatusRequest, UserStatusResponse, +}; + +/// A map of user names to user status responses to serve. +#[derive(Debug, Default)] +pub struct UserStatusMap { + map: HashMap, +} + +// IDEA: impl FromIterator for UserStatusMap. + +impl UserStatusMap { + /// Inserts the given `response` in this map. + /// Overwrites the previous entry for the same user name, if any. + pub fn insert(&mut self, response: UserStatusResponse) { + self.map.insert(response.user_name.clone(), response); + } + + /// Returns a clone of the response for the given user name. + pub fn get(&self, user_name: &str) -> Option { + self.map.get(user_name).map(|response| response.clone()) + } +} + +/// A handler that can serve responses to user status requests. +/// Responses are sent only for users who appear in `user_status_map`. +/// All other requests are ignored. +#[derive(Clone, Default)] +pub struct UserStatusHandler { + user_status_map: Arc>, +} + +impl RequestHandler for UserStatusHandler { + fn handle( + &mut self, + request: &ServerRequest, + ) -> anyhow::Result> { + let user_name = match request { + ServerRequest::UserStatusRequest(UserStatusRequest { ref user_name }) => { + user_name + } + _ => { + warn!("Unhandled request: {:?}", request); + return Ok(None); + } + }; + + let entry = self.user_status_map.lock().get(user_name); + if let Some(response) = entry { + Ok(Some(ServerResponse::UserStatusResponse(response))) + } else { + warn!("Received UserStatusRequest for unknown user {}", user_name); + Ok(None) + } + } +} + +/// A factory for `UserStatusHandler`. All handlers share `user_status_map`. +#[derive(Default)] +pub struct UserStatusHandlerFactory { + /// The status map from which responses are served. + /// + /// Testing code may wish to retain a copy of this in order to mutate the map + /// concurrently with requests being handled. + pub user_status_map: Arc>, +} + +impl ClientHandlerFactory for UserStatusHandlerFactory { + type Handler = UserStatusHandler; + + fn make(&self) -> Self::Handler { + Self::Handler { + user_status_map: self.user_status_map.clone(), + } + } +} + +impl UserStatusHandlerFactory { + /// Convenience function to create a new factory wrapping the given `map`. + pub fn new(map: UserStatusMap) -> Self { + Self { + user_status_map: Arc::new(Mutex::new(map)), + } + } +}