diff --git a/src/proto/testing.rs b/src/proto/testing.rs new file mode 100644 index 0000000..250df19 --- /dev/null +++ b/src/proto/testing.rs @@ -0,0 +1,62 @@ +//! Provides utilities for testing protocol code. + +use std::io; +use std::net::SocketAddr; + +use tokio::net::{TcpListener, TcpStream}; + +// use crate::proto::{FrameEncoder, FrameDecoder}; + +async fn process(_stream: TcpStream) -> io::Result<()> { + Ok(()) +} + +/// A fake server for connecting to in tests. +pub struct FakeServer { + listener: TcpListener, +} + +impl FakeServer { + /// Creates a new fake server and binds it to a port on localhost. + pub async fn new() -> io::Result { + let listener = TcpListener::bind("localhost:0").await?; + Ok(FakeServer { listener }) + } + + /// Returns the address to which this server is bound. + /// This is always localhost and a random port chosen by the OS. + pub fn address(&self) -> io::Result { + self.listener.local_addr() + } + + /// Runs the server: accepts incoming connections and responds to requests. + pub async fn run(&mut self) -> io::Result<()> { + loop { + let (socket, _peer_address) = self.listener.accept().await?; + tokio::spawn(async move { process(socket).await }); + } + } +} + +#[cfg(test)] +mod tests { + use tokio::net::TcpStream; + + use super::FakeServer; + + #[tokio::test] + async fn new_binds_to_localhost() { + let server = FakeServer::new().await.unwrap(); + assert!(server.address().unwrap().ip().is_loopback()); + } + + #[tokio::test] + async fn accepts_incoming_connections() { + let mut server = FakeServer::new().await.unwrap(); + let address = server.address().unwrap(); + tokio::spawn(async move { server.run().await.unwrap() }); + + // The connection succeeds. + let _ = TcpStream::connect(address).await.unwrap(); + } +}