Start to write e2e tests

This commit is contained in:
2022-09-01 15:03:20 +02:00
parent caa62b8e49
commit 43a6d2c3a2
9 changed files with 369 additions and 29 deletions

View File

@ -0,0 +1,250 @@
use std::time::Duration;
use rand::Rng;
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::{TcpListener, TcpStream};
use tokio::time;
use crate::test::LOCALHOST;
pub struct DummyTCPServer(TcpListener);
impl DummyTCPServer {
pub async fn start(port: u16) -> Self {
let addr = format!("{}:{}", LOCALHOST, port);
println!("[DUMMY TCP] Listen on {}", addr);
let listener = TcpListener::bind(addr)
.await
.expect("Failed to bind dummy TCP listener!");
Self(listener)
}
/// Receive chunk of data from following connection
pub async fn read_next_connection(&self) -> Vec<u8> {
let (mut conn, _addr) = self
.0
.accept()
.await
.expect("Could not open next connection!");
let mut buff = Vec::with_capacity(100);
conn.read_to_end(&mut buff).await.unwrap();
buff
}
/// Receive chunk of data from following connection
pub async fn read_then_write_next_connection(&self, to_send: &[u8]) -> Vec<u8> {
let (mut conn, _addr) = self
.0
.accept()
.await
.expect("Could not open next connection!");
let mut buff: [u8; 100] = [0; 100];
let size = conn.read(&mut buff).await.unwrap();
conn.write_all(to_send).await.unwrap();
buff[0..size].to_vec()
}
/// Receive chunk of data from following connection
pub async fn write_next_connection(&self, to_send: &[u8]) {
let (mut conn, _addr) = self
.0
.accept()
.await
.expect("Could not open next connection!");
conn.write_all(to_send).await.unwrap()
}
/// Perform complex exchange: receive numbers from client and respond with their square
pub async fn next_conn_square_operations(&self) {
let (mut conn, _addr) = self
.0
.accept()
.await
.expect("Could not open next connection!");
let mut buff: [u8; 100] = [0; 100];
loop {
let size = conn.read(&mut buff).await.unwrap();
if size == 0 {
break;
}
let content = String::from_utf8_lossy(&buff[0..size])
.to_string()
.parse::<i64>()
.unwrap();
conn.write_all((content * content).to_string().as_bytes())
.await
.unwrap();
}
}
}
pub async fn dummy_tcp_client_read_conn(port: u16) -> Vec<u8> {
let mut socket = TcpStream::connect(format!("127.0.0.1:{}", port))
.await
.expect("Failed to connect to dummy TCP server!");
let mut buff = Vec::with_capacity(100);
socket.read_to_end(&mut buff).await.unwrap();
buff
}
pub async fn dummy_tcp_client_write_then_read_conn(port: u16, data: &[u8]) -> Vec<u8> {
let mut socket = TcpStream::connect(format!("127.0.0.1:{}", port))
.await
.expect("Failed to connect to dummy TCP server!");
socket.write_all(data).await.unwrap();
let mut buff: [u8; 100] = [0; 100];
let size = socket.read(&mut buff).await.unwrap();
buff[0..size].to_vec()
}
pub async fn dummy_tcp_client_write_conn(port: u16, data: &[u8]) {
let mut socket = TcpStream::connect(format!("127.0.0.1:{}", port))
.await
.expect("Failed to connect to dummy TCP server!");
socket.write_all(data).await.unwrap()
}
pub async fn dummy_tcp_client_square_root_requests(port: u16, num_exchanges: usize) {
let mut socket = TcpStream::connect(format!("127.0.0.1:{}", port))
.await
.expect("Failed to connect to dummy TCP server!");
let mut rng = rand::thread_rng();
let mut buff: [u8; 100] = [0; 100];
for _ in 0..num_exchanges {
let num = rng.gen::<i32>() % 100;
socket.write_all(num.to_string().as_bytes()).await.unwrap();
let size = socket.read(&mut buff).await.unwrap();
if size == 0 {
panic!("Got empty response!");
}
let got = String::from_utf8_lossy(&buff[0..size])
.to_string()
.parse::<i64>()
.unwrap();
println!("{} * {} = {} (based on server response)", num, num, got);
assert_eq!((num * num) as i64, got);
}
}
/// 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);
}
mod test {
use crate::test::dummy_tcp_sockets::{
dummy_tcp_client_read_conn, dummy_tcp_client_square_root_requests,
dummy_tcp_client_write_conn, dummy_tcp_client_write_then_read_conn, DummyTCPServer,
};
use crate::test::{get_port_number, PortsAllocation};
fn port(index: u16) -> u16 {
get_port_number(PortsAllocation::DummyTCPServer, index)
}
#[tokio::test]
async fn socket_read_from_server() {
const DATA: &[u8] = "Hello world!!!".as_bytes();
let listener = DummyTCPServer::start(port(0)).await;
let handle = tokio::spawn(async move {
listener.write_next_connection(DATA).await;
});
let data = dummy_tcp_client_read_conn(port(0)).await;
assert_eq!(data, DATA);
handle.await.unwrap();
}
#[tokio::test]
async fn socket_write_to_server() {
const DATA: &[u8] = "Hello world 2".as_bytes();
let listener = DummyTCPServer::start(port(1)).await;
tokio::spawn(async move {
dummy_tcp_client_write_conn(port(1), DATA).await;
});
let data = listener.read_next_connection().await;
assert_eq!(data, DATA);
}
#[tokio::test]
async fn socket_read_and_write_to_server() {
const DATA_1: &[u8] = "Hello world 3a".as_bytes();
const DATA_2: &[u8] = "Hello world 3b".as_bytes();
let listener = DummyTCPServer::start(port(2)).await;
let handle = tokio::spawn(async move {
println!("client handle");
let data = dummy_tcp_client_write_then_read_conn(port(2), DATA_1).await;
assert_eq!(data, DATA_2);
});
let h2 = tokio::spawn(async move {
println!("server handle");
let data = listener.read_then_write_next_connection(DATA_2).await;
assert_eq!(data, DATA_1);
});
handle.await.unwrap();
h2.await.unwrap();
}
#[tokio::test]
async fn socket_dummy_root_calculator() {
let listener = DummyTCPServer::start(port(3)).await;
let handle = tokio::spawn(async move {
listener.next_conn_square_operations().await;
});
let data = dummy_tcp_client_write_then_read_conn(port(3), "5".as_bytes()).await;
assert_eq!(data, "25".as_bytes());
handle.await.unwrap();
}
#[tokio::test]
async fn socket_dummy_root_calculator_multiple() {
let listener = DummyTCPServer::start(port(4)).await;
let handle = tokio::spawn(async move {
listener.next_conn_square_operations().await;
});
dummy_tcp_client_square_root_requests(port(4), 10).await;
handle.await.unwrap();
}
}

15
src/test/mod.rs Normal file
View File

@ -0,0 +1,15 @@
#[non_exhaustive]
enum PortsAllocation {
DummyTCPServer,
ValidWithTokenAuth,
}
fn get_port_number(alloc: PortsAllocation, index: u16) -> u16 {
2100 + 20 * (alloc as u16) + index
}
const LOCALHOST: &str = "127.0.0.1";
mod dummy_tcp_sockets;
mod valid_with_token_auth;

View File

@ -0,0 +1,61 @@
use tokio::task;
use crate::tcp_relay_client::client_config::ClientConfig;
use crate::tcp_relay_server::server_config::ServerConfig;
use crate::test::dummy_tcp_sockets::{
dummy_tcp_client_square_root_requests, dummy_tcp_client_write_then_read_conn, wait_for_port,
DummyTCPServer,
};
use crate::test::{get_port_number, PortsAllocation, LOCALHOST};
const VALID_TOKEN: &str = "AvalidTOKEN";
const DATA_1: &[u8] = "DATA1".as_bytes();
const DATA_2: &[u8] = "DATA2".as_bytes();
fn port(index: u16) -> u16 {
get_port_number(PortsAllocation::ValidWithTokenAuth, index)
}
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
async fn valid_with_token_auth() {
let _ = env_logger::builder().is_test(true).try_init();
tokio::spawn(async move {
// Start internal service
let local_server = DummyTCPServer::start(port(1)).await;
local_server.next_conn_square_operations().await;
});
let local_set = task::LocalSet::new();
local_set
.run_until(async move {
wait_for_port(port(1)).await;
// Start server relay
task::spawn_local(crate::tcp_relay_server::run_app(ServerConfig {
tokens: vec![VALID_TOKEN.to_string()],
tokens_file: None,
ports: vec![port(1)],
upstream_server: "127.0.0.1".to_string(),
listen_address: format!("127.0.0.1:{}", port(0)),
increment_ports: 1,
tls_cert: None,
tls_key: None,
tls_client_auth_root_cert: None,
tls_revocation_list: None,
}));
wait_for_port(port(0)).await;
// Start client relay
task::spawn(crate::tcp_relay_client::run_app(ClientConfig {
token: Some(VALID_TOKEN.to_string()),
relay_url: format!("http://{}:{}", LOCALHOST, port(0)),
listen_address: LOCALHOST.to_string(),
root_certificate: None,
..Default::default()
}));
wait_for_port(port(2)).await;
})
.await;
}