#![allow(clippy::unwrap_used)]
#![cfg(test)]
use argon2::{PasswordHash, PasswordVerifier};
use hnefatafl_copenhagen::accounts::{Account, Accounts};
use super::*;
use std::net::TcpStream;
use std::process::{Child, Stdio};
use std::thread;
use std::time::{Duration, Instant};
const ADDRESS: &str = "localhost:49152";
struct Server(Child);
impl Server {
fn new(release: bool) -> anyhow::Result<Server> {
let server = if release {
std::process::Command::new("./target/release/hnefatafl-server-full")
.stdout(Stdio::null())
.stderr(Stdio::null())
.arg("--skip-the-data-file")
.arg("--skip-advertising-updates")
.arg("--skip-message")
.spawn()?
} else {
std::process::Command::new("./target/debug/hnefatafl-server-full")
.stdout(Stdio::null())
.stderr(Stdio::null())
.arg("--skip-the-data-file")
.arg("--skip-advertising-updates")
.arg("--skip-message")
.spawn()?
};
Ok(Server(server))
}
}
impl Drop for Server {
fn drop(&mut self) {
self.0.kill().unwrap();
}
}
#[test]
fn capital_letters_fail() {
let mut accounts = Accounts::default();
let password = "A".to_string();
let ctx = Argon2::default();
let salt = SaltString::generate(&mut OsRng);
let password_hash = ctx
.hash_password(password.as_bytes(), &salt)
.unwrap()
.to_string();
let account = Account {
password: password_hash,
logged_in: Some(0),
..Default::default()
};
accounts.0.insert("testing".to_string(), account);
{
let account = accounts.0.get_mut("testing").unwrap();
let salt = SaltString::generate(&mut OsRng);
let password_hash = ctx
.hash_password(password.as_bytes(), &salt)
.unwrap()
.to_string();
account.password = password_hash;
}
{
let account = accounts.0.get_mut("testing").unwrap();
let hash = PasswordHash::try_from(account.password.as_str()).unwrap();
assert!(
Argon2::default()
.verify_password(password.as_bytes(), &hash)
.is_ok()
);
}
}
#[test]
fn server_full() -> anyhow::Result<()> {
std::process::Command::new("cargo")
.arg("build")
.arg("--bin")
.arg("hnefatafl-server-full")
.output()?;
let _server = Server::new(false);
thread::sleep(Duration::from_millis(10));
let mut buf = String::new();
let mut socket_1 = TcpStream::connect(ADDRESS)?;
let mut reader_1 = BufReader::new(socket_1.try_clone()?);
socket_1.write_all(format!("{VERSION_ID} create_account player-1\n").as_bytes())?;
reader_1.read_line(&mut buf)?;
assert_eq!(buf, "= login\n");
buf.clear();
socket_1.write_all(b"change_password\n")?;
reader_1.read_line(&mut buf)?;
assert_eq!(buf, "= change_password\n");
buf.clear();
socket_1.write_all(b"new_game attacker rated fischer 900000 10 11\n")?;
reader_1.read_line(&mut buf)?;
assert_eq!(
buf,
"= new_game game 0 player-1 _ rated fischer 900000 10 11 _ false {}\n"
);
buf.clear();
let mut socket_2 = TcpStream::connect(ADDRESS)?;
let mut reader_2 = BufReader::new(socket_2.try_clone()?);
socket_2.write_all(format!("{VERSION_ID} create_account player-2\n").as_bytes())?;
reader_2.read_line(&mut buf)?;
assert_eq!(buf, "= login\n");
buf.clear();
socket_2.write_all(b"join_game_pending 0\n")?;
reader_2.read_line(&mut buf)?;
assert_eq!(buf, "= join_game_pending 0\n");
buf.clear();
reader_1.read_line(&mut buf)?;
assert_eq!(buf, "= challenge_requested 0\n");
buf.clear();
socket_1.write_all(b"join_game 0\n")?;
reader_1.read_line(&mut buf)?;
assert_eq!(
buf,
"= join_game player-1 player-2 rated fischer 900000 10 11\n"
);
buf.clear();
reader_2.read_line(&mut buf)?;
assert_eq!(
buf,
"= join_game player-1 player-2 rated fischer 900000 10 11\n"
);
buf.clear();
reader_1.read_line(&mut buf)?;
assert_eq!(buf, "game 0 generate_move attacker\n");
buf.clear();
socket_1.write_all(b"game 0 play attacker resigns _\n")?;
reader_1.read_line(&mut buf)?;
assert_eq!(buf, "= game_over 0 defender_wins\n");
buf.clear();
reader_2.read_line(&mut buf)?;
assert_eq!(buf, "game 0 play attacker resigns \n");
buf.clear();
reader_2.read_line(&mut buf)?;
assert_eq!(buf, "= game_over 0 defender_wins\n");
buf.clear();
Ok(())
}
#[ignore = "too slow, too many tcp connections"]
#[test]
fn many_clients() -> anyhow::Result<()> {
std::process::Command::new("cargo")
.arg("build")
.arg("--release")
.arg("--bin")
.arg("hnefatafl-server-full")
.output()?;
let _server = Server::new(true);
thread::sleep(Duration::from_millis(10));
let t0 = Instant::now();
let mut handles = Vec::new();
for i in 0..1_000 {
handles.push(thread::spawn(move || {
let mut buf = String::new();
let mut tcp = TcpStream::connect(ADDRESS).unwrap();
let mut reader = BufReader::new(tcp.try_clone().unwrap());
tcp.write_all(format!("{VERSION_ID} create_account q-player-{i}\n").as_bytes())
.unwrap();
reader.read_line(&mut buf).unwrap();
buf.clear();
}));
}
for handle in handles {
handle.join().unwrap();
}
let t1 = Instant::now();
println!("many clients: {:?}", t1 - t0);
Ok(())
}