diff --git a/examples/integration_testing.rs b/examples/integration_testing.rs index abe29b34e6..8004835269 100644 --- a/examples/integration_testing.rs +++ b/examples/integration_testing.rs @@ -12,25 +12,76 @@ use std::error::Error as StdError; use std::{env, str}; use std::sync::atomic::{self, AtomicUsize}; +// Example trait to allow custom databases to be used within the integration tests +// to test different behaviours without touching a real database. +trait Database : Send + Sync + 'static { + fn get_users(&self) -> Vec; +} + +impl Database for Vec { + fn get_users(&self) -> Vec { + self.clone() + } +} + +struct ServerData { + hits: AtomicUsize, + + // FIXME: The `middleware` macro typehinting doesn't support hinting with + // typeparams, i.e. the hint can't be `< ServerData >` where T is a + // typeparam from the enclosing function, e.g. `start_server`. + // + // To counter this limitation, we've boxed the trait so that specifying the + // typeparam is unnecessary. + database: Box +} + +impl ServerData { + fn hitcount(&self) -> usize { + self.hits.load(atomic::Ordering::Relaxed) + } + + fn log_hit(&self) -> usize { + self.hits.fetch_add(1, atomic::Ordering::Relaxed) + } + + fn get_users(&self) -> Vec { + self.database.get_users() + } +} + fn main() { let port = env::var("PORT").map(|s| s.parse().unwrap()).unwrap_or(3000); + let address = &format!("0.0.0.0:{}", port); + let database = vec![]; - start_server(&format!("0.0.0.0:{}", port)).unwrap(); + start_server(address, database).unwrap(); } -fn start_server(address: &str) -> Result> { - let hits = AtomicUsize::new(0); - let mut server = Nickel::with_data(hits); +fn start_server(address: &str, database: D) -> Result> +where D: Database { + let server_data = ServerData { + hits: AtomicUsize::new(0), + database: Box::new(database), + }; + + let mut server = Nickel::with_data(server_data); // Track all hits to the server - server.utilize(middleware! { |_req, res| - let _hits = res.data().fetch_add(1, atomic::Ordering::Relaxed); + server.utilize(middleware! { |_req, res| + res.data().log_hit(); return res.next_middleware() }); // Core server server.get("/", middleware!( "Hello World" )); + server.get("/users", middleware! { |_, res| + let users = res.data().get_users(); + + Json::from_str(&format!(r#"{{ "users": {:?} }}"#, users)).unwrap() + }); + // Json example server.post("/", middleware! { |req, res| #[derive(RustcEncodable, RustcDecodable)] @@ -56,10 +107,8 @@ fn start_server(address: &str) -> Result> { }); // Get the hitcount - server.get("/hits", middleware!{ |_req, res| - let hits = res.data().load(atomic::Ordering::Relaxed); - - hits.to_string() + server.get("/hits", middleware!{ |_req, res| + res.data().hitcount().to_string() }); server.listen(address) @@ -135,13 +184,47 @@ mod tests { #[test] fn non_shared_server() { let test_local_server = { - let server = super::start_server("127.0.0.1:0").unwrap(); + let server = super::start_server("127.0.0.1:0", vec![]).unwrap(); Server::new(server) }; assert_eq!(get_hits_after_delay(&test_local_server), 1); } + #[test] + fn has_no_users_by_default() { + let mut response = get("/users"); + + let json = Json::from_str(&response.body()).unwrap(); + + assert_eq!(json["users"].as_array().unwrap().len(), 0); + assert_eq!(response.status, StatusCode::Ok); + assert_eq!( + response.headers.get::(), + Some(&header::ContentType::json()) + ); + } + + #[test] + fn non_shared_server_with_different_database() { + let server = { + let bots = vec!["bors".into(), "homu".into(), "highfive".into()]; + let server = super::start_server("127.0.0.1:0", bots).unwrap(); + Server::new(server) + }; + + let mut response = server.get("/users"); + + let json = Json::from_str(&response.body()).unwrap(); + + assert_eq!(json["users"].as_array().unwrap().len(), 3); + assert_eq!(response.status, StatusCode::Ok); + assert_eq!( + response.headers.get::(), + Some(&header::ContentType::json()) + ); + } + mod support { use hyper::client::{Client, Response as HyperResponse}; use nickel::ListeningServer; @@ -192,7 +275,7 @@ mod tests { lazy_static! { /// This is a shared instance of the server between all the tests pub static ref STATIC_SERVER: Server = { - let server = super::super::start_server("127.0.0.1:0").unwrap(); + let server = super::super::start_server("127.0.0.1:0", vec![]).unwrap(); Server::new(server) }; }