|
|
|
@ -22,6 +22,7 @@ pub struct Client { |
|
|
|
}
|
|
|
|
|
|
|
|
/// Contents of a successful login response.
|
|
|
|
#[derive(Debug, Eq, PartialEq)]
|
|
|
|
pub struct LoginInfo {
|
|
|
|
/// The server's message of the day.
|
|
|
|
pub motd: String,
|
|
|
|
@ -121,19 +122,18 @@ impl Client { |
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use tokio::net::TcpStream;
|
|
|
|
use std::net::Ipv4Addr;
|
|
|
|
use tokio::net::{TcpListener, TcpStream};
|
|
|
|
use tokio::sync::mpsc;
|
|
|
|
|
|
|
|
use crate::server::testing::{
|
|
|
|
ServerBuilder, ShutdownType, UserStatusHandlerFactory, UserStatusMap,
|
|
|
|
};
|
|
|
|
use crate::core::{FrameReader, FrameWriter};
|
|
|
|
use crate::server::{
|
|
|
|
Credentials, ServerRequest, ServerResponse, UserStatusRequest,
|
|
|
|
UserStatusResponse,
|
|
|
|
Credentials, LoginRequest, LoginResponse, ServerRequest, ServerResponse,
|
|
|
|
UserStatusRequest, UserStatusResponse,
|
|
|
|
};
|
|
|
|
use crate::UserStatus;
|
|
|
|
|
|
|
|
use super::Client;
|
|
|
|
use super::{Client, LoginInfo};
|
|
|
|
|
|
|
|
// Enable capturing logs in tests.
|
|
|
|
fn init() {
|
|
|
|
@ -159,82 +159,89 @@ mod tests { |
|
|
|
async fn login_success() {
|
|
|
|
init();
|
|
|
|
|
|
|
|
let (server, handle) =
|
|
|
|
ServerBuilder::new(UserStatusHandlerFactory::default())
|
|
|
|
.bind()
|
|
|
|
let listener = TcpListener::bind("localhost:0").await.expect("binding");
|
|
|
|
let address = listener.local_addr().expect("getting local address");
|
|
|
|
|
|
|
|
let server_task = tokio::spawn(async move {
|
|
|
|
let (mut stream, _) = listener.accept().await.expect("accepting");
|
|
|
|
let (read_half, write_half) = stream.split();
|
|
|
|
let mut reader = FrameReader::new(read_half);
|
|
|
|
let mut writer = FrameWriter::new(write_half);
|
|
|
|
|
|
|
|
let frame = reader.read().await.expect("reading login request");
|
|
|
|
assert_eq!(
|
|
|
|
frame,
|
|
|
|
Some(ServerRequest::LoginRequest(LoginRequest {
|
|
|
|
user_name: "alice".to_string(),
|
|
|
|
password: "sekrit".to_string(),
|
|
|
|
digest: "286da88eb442032bdd3913979af76e8a".to_string(),
|
|
|
|
major: 181,
|
|
|
|
minor: 100,
|
|
|
|
}))
|
|
|
|
);
|
|
|
|
|
|
|
|
writer
|
|
|
|
.write(&ServerResponse::LoginResponse(LoginResponse::LoginOk {
|
|
|
|
ip: Ipv4Addr::new(1, 2, 3, 4),
|
|
|
|
motd: "motd".to_string(),
|
|
|
|
password_md5_opt: None,
|
|
|
|
}))
|
|
|
|
.await
|
|
|
|
.expect("binding server");
|
|
|
|
let server_task = tokio::spawn(server.serve());
|
|
|
|
|
|
|
|
let stream = TcpStream::connect(handle.address())
|
|
|
|
.await
|
|
|
|
.expect("connecting");
|
|
|
|
|
|
|
|
let worker = Client::new(stream)
|
|
|
|
.login(credentials())
|
|
|
|
.await
|
|
|
|
.expect("logging in");
|
|
|
|
|
|
|
|
drop(worker);
|
|
|
|
|
|
|
|
handle.shutdown(ShutdownType::LameDuck);
|
|
|
|
server_task
|
|
|
|
.await
|
|
|
|
.expect("joining server")
|
|
|
|
.expect("running server");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
async fn simple_exchange() {
|
|
|
|
init();
|
|
|
|
|
|
|
|
let response = UserStatusResponse {
|
|
|
|
user_name: "shruti".to_string(),
|
|
|
|
status: UserStatus::Online,
|
|
|
|
is_privileged: false,
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut user_status_map = UserStatusMap::default();
|
|
|
|
user_status_map.insert(response.clone());
|
|
|
|
|
|
|
|
let (server, handle) =
|
|
|
|
ServerBuilder::new(UserStatusHandlerFactory::new(user_status_map))
|
|
|
|
.bind()
|
|
|
|
.expect("writing login response");
|
|
|
|
|
|
|
|
let frame = reader.read().await.expect("reading user status request");
|
|
|
|
assert_eq!(
|
|
|
|
frame,
|
|
|
|
Some(ServerRequest::UserStatusRequest(UserStatusRequest {
|
|
|
|
user_name: "shruti".to_string(),
|
|
|
|
}))
|
|
|
|
);
|
|
|
|
|
|
|
|
writer
|
|
|
|
.write(&ServerResponse::UserStatusResponse(UserStatusResponse {
|
|
|
|
user_name: "shruti".to_string(),
|
|
|
|
status: UserStatus::Online,
|
|
|
|
is_privileged: false,
|
|
|
|
}))
|
|
|
|
.await
|
|
|
|
.expect("binding server");
|
|
|
|
let server_task = tokio::spawn(server.serve());
|
|
|
|
.expect("writing user status response");
|
|
|
|
});
|
|
|
|
|
|
|
|
let stream = TcpStream::connect(handle.address())
|
|
|
|
.await
|
|
|
|
.expect("connecting");
|
|
|
|
let stream = TcpStream::connect(address).await.expect("connecting");
|
|
|
|
|
|
|
|
let (_, mut worker) = Client::new(stream)
|
|
|
|
let (info, mut worker) = Client::new(stream)
|
|
|
|
.login(credentials())
|
|
|
|
.await
|
|
|
|
.expect("logging in");
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
info,
|
|
|
|
LoginInfo {
|
|
|
|
motd: "motd".to_string(),
|
|
|
|
public_ip: Ipv4Addr::new(1, 2, 3, 4),
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
let (request_tx, request_rx) = mpsc::channel(100);
|
|
|
|
let (response_tx, mut response_rx) = mpsc::channel(100);
|
|
|
|
|
|
|
|
let worker_task =
|
|
|
|
tokio::spawn(async move { worker.run(response_tx, request_rx).await });
|
|
|
|
|
|
|
|
request_tx
|
|
|
|
.send(ServerRequest::UserStatusRequest(UserStatusRequest {
|
|
|
|
user_name: "shruti".to_string(),
|
|
|
|
}))
|
|
|
|
.await
|
|
|
|
.expect("sending shruti");
|
|
|
|
request_tx
|
|
|
|
.send(ServerRequest::UserStatusRequest(UserStatusRequest {
|
|
|
|
user_name: "karandeep".to_string(),
|
|
|
|
}))
|
|
|
|
.await
|
|
|
|
.expect("sending karandeep");
|
|
|
|
|
|
|
|
let worker_task =
|
|
|
|
tokio::spawn(async move { worker.run(response_tx, request_rx).await });
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
response_rx.recv().await,
|
|
|
|
Some(ServerResponse::UserStatusResponse(response))
|
|
|
|
Some(ServerResponse::UserStatusResponse(UserStatusResponse {
|
|
|
|
user_name: "shruti".to_string(),
|
|
|
|
status: UserStatus::Online,
|
|
|
|
is_privileged: false,
|
|
|
|
}))
|
|
|
|
);
|
|
|
|
|
|
|
|
drop(request_tx);
|
|
|
|
@ -243,10 +250,6 @@ mod tests { |
|
|
|
.expect("joining worker")
|
|
|
|
.expect("running worker");
|
|
|
|
|
|
|
|
handle.shutdown(ShutdownType::LameDuck);
|
|
|
|
server_task
|
|
|
|
.await
|
|
|
|
.expect("joining server")
|
|
|
|
.expect("running server");
|
|
|
|
server_task.await.expect("joining server");
|
|
|
|
}
|
|
|
|
}
|