diff --git a/tests/common/mod.rs b/tests/common/mod.rs index e1d8a54..914a6aa 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -1,12 +1,15 @@ -use anyhow::Result; +use anyhow::{anyhow, Result}; use log::*; use nostr_rs_relay::config; use nostr_rs_relay::server::start_server; +//use http::{Request, Response}; +use hyper::{Client, StatusCode, Uri}; use std::net::TcpListener; use std::sync::mpsc as syncmpsc; use std::sync::mpsc::{Receiver as MpscReceiver, Sender as MpscSender}; use std::thread; use std::thread::JoinHandle; +use std::time::Duration; pub struct Relay { pub port: u16, @@ -20,8 +23,9 @@ pub fn start_relay() -> Result { // replace default settings let mut settings = config::Settings::default(); // identify open port + info!("Checking for address..."); let port = get_available_port().unwrap(); - info!("Starting relay on port {}", port); + info!("Found open port: {}", port); // bind to local interface only settings.network.address = "127.0.0.1".to_owned(); settings.network.port = port; @@ -31,8 +35,10 @@ pub fn start_relay() -> Result { settings.database.max_conn = 8; let (shutdown_tx, shutdown_rx): (MpscSender<()>, MpscReceiver<()>) = syncmpsc::channel(); let handle = thread::spawn(|| { + // server will block the thread it is run on. let _ = start_server(settings, shutdown_rx); }); + // how do we know the relay has finished starting up? return Ok(Relay { port, handle, @@ -40,11 +46,50 @@ pub fn start_relay() -> Result { }); } +// check if the server is healthy via HTTP request +async fn server_ready(relay: &Relay) -> Result { + let uri: String = format!("http://127.0.0.1:{}/", relay.port.to_string()); + let client = Client::new(); + let uri: Uri = uri.parse().unwrap(); + let res = client.get(uri).await?; + Ok(res.status() == StatusCode::OK) +} + +pub async fn wait_for_healthy_relay(relay: &Relay) -> Result<()> { + // TODO: maximum time to wait for server to become healthy. + // give it a little time to start up before we start polling + tokio::time::sleep(Duration::from_millis(10)).await; + loop { + let server_check = server_ready(&relay).await; + match server_check { + Ok(true) => { + // server responded with 200-OK. + break; + } + Ok(false) => { + // server responded with an error, we're done. + return Err(anyhow!("Got non-200-OK from relay")); + } + Err(_) => { + // server is not yet ready, probably connection refused... + debug!("Got ERR from Relay!"); + tokio::time::sleep(Duration::from_millis(10)).await; + } + } + } + info!("relay is ready"); + Ok(()) + // simple message sent to web browsers + //let mut request = Request::builder() + // .uri("https://www.rust-lang.org/") + // .header("User-Agent", "my-awesome-agent/1.0"); +} + // from https://elliotekj.com/posts/2017/07/25/find-available-tcp-port-rust/ fn get_available_port() -> Option { - (4000..20000).find(|port| port_is_available(*port)) + (4030..20000).find(|port| port_is_available(*port)) } -fn port_is_available(port: u16) -> bool { +pub fn port_is_available(port: u16) -> bool { match TcpListener::bind(("127.0.0.1", port)) { Ok(_) => true, Err(_) => false, diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 1f9efae..aff89c1 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -1,12 +1,20 @@ use anyhow::Result; +use log::*; use std::thread; use std::time::Duration; mod common; -#[test] -fn startup() -> Result<()> { +#[tokio::test] +async fn start_and_stop() -> Result<()> { + // this will be the common pattern for acquiring a new relay: + // start a fresh relay, on a port to-be-provided back to us: let relay = common::start_relay()?; + // wait for the relay's webserver to start up and deliver a page: + common::wait_for_healthy_relay(&relay).await?; + + let relay = common::start_relay()?; + let port = relay.port; // just make sure we can startup and shut down. // if we send a shutdown message before the server is listening, // we will get a SendError. Keep sending until someone is @@ -25,5 +33,15 @@ fn startup() -> Result<()> { // wait for relay to shutdown let thread_join = relay.handle.join(); assert!(thread_join.is_ok()); + // assert that port is now available. + assert!(common::port_is_available(port)); + Ok(()) +} + +#[tokio::test] +async fn relay_home_page() -> Result<()> { + // get a relay and wait for startup... + let relay = common::start_relay()?; + common::wait_for_healthy_relay(&relay).await?; Ok(()) }