diff --git a/src/bin/websurfx.rs b/src/bin/websurfx.rs index 88602cb..a79876f 100644 --- a/src/bin/websurfx.rs +++ b/src/bin/websurfx.rs @@ -3,15 +3,11 @@ //! This module contains the main function which handles the logging of the application to the //! stdout and handles the command line arguments provided and launches the `websurfx` server. -use std::ops::RangeInclusive; +use std::{ops::RangeInclusive, net::TcpListener}; -use websurfx::server::routes; - -use actix_files as fs; -use actix_web::{middleware::Logger, web, App, HttpServer}; use clap::{command, Parser}; use env_logger::Env; -use handlebars::Handlebars; +use websurfx::run; /// A commandline arguments struct. #[derive(Parser, Debug, Default)] @@ -67,30 +63,7 @@ async fn main() -> std::io::Result<()> { log::info!("started server on port {}", args.port); - let mut handlebars: Handlebars = Handlebars::new(); + let listener = TcpListener::bind(("127.0.0.1", args.port))?; - handlebars - .register_templates_directory(".html", "./public/templates") - .unwrap(); - - let handlebars_ref: web::Data = web::Data::new(handlebars); - - HttpServer::new(move || { - App::new() - .app_data(handlebars_ref.clone()) - .wrap(Logger::default()) // added logging middleware for logging. - // Serve images and static files (css and js files). - .service(fs::Files::new("/static", "./public/static").show_files_listing()) - .service(fs::Files::new("/images", "./public/images").show_files_listing()) - .service(routes::robots_data) // robots.txt - .service(routes::index) // index page - .service(routes::search) // search page - .service(routes::about) // about page - .service(routes::settings) // settings page - .default_service(web::route().to(routes::not_found)) // error page - }) - // Start server on 127.0.0.1 with the user provided port number. for example 127.0.0.1:8080. - .bind(("127.0.0.1", args.port))? - .run() - .await + run(listener)?.await } diff --git a/src/lib.rs b/src/lib.rs index 2baeca2..e5ed0e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,60 @@ pub mod engines; pub mod server; pub mod search_results_handler; + +use std::net::TcpListener; + +use crate::server::routes; + +use actix_files as fs; +use actix_web::{middleware::Logger, web, App, HttpServer, dev::Server}; +use handlebars::Handlebars; + + +/// Runs the web server on the provided TCP listener and returns a `Server` instance. +/// +/// # Arguments +/// +/// * `listener` - A `TcpListener` instance representing the address and port to listen on. +/// +/// # Returns +/// +/// Returns a `Result` containing a `Server` instance on success, or an `std::io::Error` on failure. +/// +/// # Example +/// +/// ``` +/// use std::net::TcpListener; +/// use web_server::Server; +/// +/// let listener = TcpListener::bind("127.0.0.1:8080").expect("Failed to bind address"); +/// let server = Server::run(listener).expect("Failed to start server"); +/// ``` +pub fn run(listener: TcpListener) -> std::io::Result { + let mut handlebars: Handlebars = Handlebars::new(); + + handlebars + .register_templates_directory(".html", "./public/templates") + .unwrap(); + + let handlebars_ref: web::Data = web::Data::new(handlebars); + + let server = HttpServer::new(move || { + App::new() + .app_data(handlebars_ref.clone()) + .wrap(Logger::default()) // added logging middleware for logging. + // Serve images and static files (css and js files). + .service(fs::Files::new("/static", "./public/static").show_files_listing()) + .service(fs::Files::new("/images", "./public/images").show_files_listing()) + .service(routes::robots_data) // robots.txt + .service(routes::index) // index page + .service(routes::search) // search page + .service(routes::about) // about page + .service(routes::settings) // settings page + .default_service(web::route().to(routes::not_found)) // error page + }) + // Start server on 127.0.0.1 with the user provided port number. for example 127.0.0.1:8080. + .listen(listener)? + .run(); + Ok(server) +} diff --git a/tests/index.rs b/tests/index.rs new file mode 100644 index 0000000..d0a61fe --- /dev/null +++ b/tests/index.rs @@ -0,0 +1,42 @@ +use std::net::TcpListener; + +use handlebars::Handlebars; +use websurfx::run; + + +// Starts a new instance of the HTTP server, bound to a random available port +fn spawn_app() -> String { + // Binding to port 0 will trigger the OS to assign a port for us. + let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind random port"); + let port = listener.local_addr().unwrap().port(); + let server = run(listener).expect("Failed to bind address"); + + tokio::spawn(server); + format!("http://127.0.0.1:{}/", port) +} + +// Creates a new instance of Handlebars and registers the templates directory. +// This is used to compare the rendered template with the response body. +fn handlebars() -> Handlebars<'static> { + let mut handlebars = Handlebars::new(); + + handlebars + .register_templates_directory(".html", "./public/templates") + .unwrap(); + + handlebars +} + + +#[tokio::test] +async fn test_index() { + let address = spawn_app(); + + let client = reqwest::Client::new(); + let res = client.get(address).send().await.unwrap(); + assert_eq!(res.status(), 200); + + let handlebars = handlebars(); + let template = handlebars.render("index", &()).unwrap(); + assert_eq!(res.text().await.unwrap(), template); +} \ No newline at end of file