From 70cb99a0a038adf6fb7de1cfb19a8e1f602ce5ce Mon Sep 17 00:00:00 2001 From: Pierre Hubert Date: Wed, 14 Sep 2022 17:36:16 +0200 Subject: [PATCH] Start to write tests --- Cargo.lock | 92 +++++++++++++++++++++++++++++++++++++++ Cargo.toml | 8 +++- src/bot_client.rs | 25 +++++++++++ src/lib.rs | 4 ++ src/main.rs | 4 +- src/server.rs | 2 +- src/test/client.rs | 39 +++++++++++++++++ src/test/mod.rs | 29 ++++++++++++ src/test/network_utils.rs | 25 +++++++++++ 9 files changed, 223 insertions(+), 5 deletions(-) create mode 100644 src/bot_client.rs create mode 100644 src/test/client.rs create mode 100644 src/test/mod.rs create mode 100644 src/test/network_utils.rs diff --git a/Cargo.lock b/Cargo.lock index eb3c17b..d54aa6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -349,6 +349,12 @@ dependencies = [ "alloc-stdlib", ] +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + [[package]] name = "bytes" version = "1.2.1" @@ -585,9 +591,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0828a5471e340229c11c77ca80017937ce3c58cb788a17e5f1c2d5c485a9577" dependencies = [ "futures-core", + "futures-sink", "futures-task", "pin-project-lite", "pin-utils", + "slab", ] [[package]] @@ -1022,6 +1030,9 @@ dependencies = [ "rand", "serde", "serde_json", + "serde_urlencoded", + "tokio", + "tokio-tungstenite", "uuid", ] @@ -1074,6 +1085,17 @@ dependencies = [ "serde", ] +[[package]] +name = "sha-1" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + [[package]] name = "sha1" version = "0.10.1" @@ -1151,6 +1173,26 @@ version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb" +[[package]] +name = "thiserror" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "time" version = "0.3.13" @@ -1195,14 +1237,39 @@ dependencies = [ "libc", "memchr", "mio", + "num_cpus", "once_cell", "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", + "tokio-macros", "winapi", ] +[[package]] +name = "tokio-macros" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9724f9a975fb987ef7a3cd9be0350edcbe130698af5b8f7a631e23d42d052484" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f714dd15bead90401d77e04243611caec13726c2408afd5b31901dfcdcb3b181" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + [[package]] name = "tokio-util" version = "0.7.3" @@ -1238,6 +1305,25 @@ dependencies = [ "once_cell", ] +[[package]] +name = "tungstenite" +version = "0.17.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e27992fd6a8c29ee7eef28fc78349aa244134e10ad447ce3b9f0ac0ed0fa4ce0" +dependencies = [ + "base64", + "byteorder", + "bytes", + "http", + "httparse", + "log", + "rand", + "sha-1", + "thiserror", + "url", + "utf-8", +] + [[package]] name = "typenum" version = "1.15.0" @@ -1277,6 +1363,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + [[package]] name = "uuid" version = "1.1.2" diff --git a/Cargo.toml b/Cargo.toml index 60897cf..2ae08dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -17,4 +17,10 @@ actix = "0.13.0" actix-web-actors = "4.1.0" actix-rt = "2.7.0" uuid = { version = "1.1.2", features = ["v4"] } -rand = "0.8.5" \ No newline at end of file +rand = "0.8.5" + +[dev-dependencies] +tokio = { version = "1", features = ["full"] } +#reqwest = { version = "0.11.11", default-features = false, features = ["json", "rustls-tls"] } +tokio-tungstenite = "0.17.2" +serde_urlencoded = "0.7.1" \ No newline at end of file diff --git a/src/bot_client.rs b/src/bot_client.rs new file mode 100644 index 0000000..5e5dfe0 --- /dev/null +++ b/src/bot_client.rs @@ -0,0 +1,25 @@ +use std::error::Error; + +use tokio::net::TcpStream; +use tokio_tungstenite::tungstenite::handshake::client::Response; +use tokio_tungstenite::{MaybeTlsStream, WebSocketStream}; + +use crate::data::GameRules; + +pub async fn run_client(server: &str, rules: &GameRules) -> Result<(), Box> { + let url = format!( + "{}/play/bot?{}", + server.replace("http", "ws"), + serde_urlencoded::to_string(rules).unwrap() + ); + log::debug!("Connecting to {}...", url); + let (socket, _) = match tokio_tungstenite::connect_async(url).await { + Ok(s) => s, + Err(e) => { + log::error!("Failed to establish WebSocket connection! {:?}", e); + return Err(Box::new(e)); + } + }; + + Ok(()) +} diff --git a/src/lib.rs b/src/lib.rs index 72e0f76..7041afe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,6 @@ pub mod args; +#[cfg(test)] +pub mod bot_client; pub mod consts; pub mod data; pub mod game; @@ -6,3 +8,5 @@ pub mod human_player; pub mod human_player_ws; pub mod random_bot; pub mod server; +#[cfg(test)] +mod test; diff --git a/src/main.rs b/src/main.rs index 9a055cd..32434d7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -8,7 +8,5 @@ use sea_battle_backend::server::start_server; async fn main() -> std::io::Result<()> { env_logger::Builder::from_env(Env::default().default_filter_or("info")).init(); - let args: Args = Args::parse(); - - start_server(&args).await + start_server(Args::parse()).await } diff --git a/src/server.rs b/src/server.rs index b281ff0..13f6158 100644 --- a/src/server.rs +++ b/src/server.rs @@ -39,7 +39,7 @@ async fn start_bot_play( resp } -pub async fn start_server(args: &Args) -> std::io::Result<()> { +pub async fn start_server(args: Args) -> std::io::Result<()> { let args_clone = args.clone(); HttpServer::new(move || { diff --git a/src/test/client.rs b/src/test/client.rs new file mode 100644 index 0000000..1fc9d65 --- /dev/null +++ b/src/test/client.rs @@ -0,0 +1,39 @@ +use crate::args::Args; +use crate::bot_client; +use crate::data::GameRules; +use crate::server::start_server; +use crate::test::network_utils::wait_for_port; +use crate::test::TestPort; +use tokio::task; + +#[tokio::test] +async fn invalid_port() { + let _ = env_logger::builder().is_test(true).try_init(); + + bot_client::run_client( + &TestPort::ClientInvalidPort.as_url(), + &GameRules::random_players_rules(), + ) + .await + .unwrap_err(); +} + +#[tokio::test] +async fn invalid_rules() { + let _ = env_logger::builder().is_test(true).try_init(); + + let local_set = task::LocalSet::new(); + local_set + .run_until(async move { + let mut rules = GameRules::random_players_rules(); + rules.map_width = 0; + + task::spawn_local(start_server(Args::for_test(TestPort::ClientInvalidRules))); + wait_for_port(TestPort::ClientInvalidRules.port()).await; + + bot_client::run_client(&TestPort::ClientInvalidRules.as_url(), &rules) + .await + .unwrap_err(); + }) + .await; +} diff --git a/src/test/mod.rs b/src/test/mod.rs new file mode 100644 index 0000000..e4a1952 --- /dev/null +++ b/src/test/mod.rs @@ -0,0 +1,29 @@ +use crate::args::Args; + +#[derive(Copy, Clone)] +enum TestPort { + ClientInvalidPort = 20000, + ClientInvalidRules, +} + +impl TestPort { + pub fn port(&self) -> u16 { + (*self as u32) as u16 + } + + pub fn as_url(&self) -> String { + format!("http://127.0.0.1:{}", self.port()) + } +} + +impl Args { + fn for_test(port: TestPort) -> Self { + Self { + listen_address: format!("127.0.0.1:{}", port.port()), + cors: None, + } + } +} + +mod client; +mod network_utils; diff --git a/src/test/network_utils.rs b/src/test/network_utils.rs new file mode 100644 index 0000000..922abf9 --- /dev/null +++ b/src/test/network_utils.rs @@ -0,0 +1,25 @@ +use std::time::Duration; + +use tokio::net::TcpStream; +use tokio::time; + +/// Check whether a given port is open or not +pub async fn is_port_open(port: u16) -> bool { + match TcpStream::connect(("127.0.0.1", port)).await { + Ok(_) => true, + Err(_) => false, + } +} + +/// Wait for a port to become available +pub async fn wait_for_port(port: u16) { + for _ in 0..50 { + if is_port_open(port).await { + return; + } + time::sleep(Duration::from_millis(10)).await; + } + + eprintln!("Port {} did not open in time!", port); + std::process::exit(2); +}