diff --git a/contrib/src/json/mod.rs b/contrib/src/json/mod.rs index d1180445..89e2e7c5 100644 --- a/contrib/src/json/mod.rs +++ b/contrib/src/json/mod.rs @@ -4,7 +4,7 @@ extern crate serde_json; use std::ops::{Deref, DerefMut}; use std::io::Read; -use rocket::outcome::{Outcome, IntoOutcome}; +use rocket::outcome::Outcome; use rocket::request::Request; use rocket::data::{self, Data, FromData}; use rocket::response::{self, Responder, content}; @@ -78,7 +78,13 @@ impl FromData for JSON { } let reader = data.open().take(MAX_SIZE); - serde_json::from_reader(reader).map(|val| JSON(val)).into_outcome() + match serde_json::from_reader(reader).map(|val| JSON(val)) { + Ok(value) => Outcome::Success(value), + Err(e) => { + error_!("Couldn't parse JSON body: {:?}", e); + Outcome::Failure((Status::BadRequest, e)) + } + } } } diff --git a/examples/hello_person/src/main.rs b/examples/hello_person/src/main.rs index 43f08fc7..cb0c993d 100644 --- a/examples/hello_person/src/main.rs +++ b/examples/hello_person/src/main.rs @@ -6,7 +6,7 @@ extern crate rocket; #[cfg(test)] mod tests; #[get("/hello//")] -fn hello(name: &str, age: i8) -> String { +fn hello(name: &str, age: u8) -> String { format!("Hello, {} year old named {}!", age, name) } diff --git a/examples/hello_person/src/tests.rs b/examples/hello_person/src/tests.rs index 0452b455..e7768534 100644 --- a/examples/hello_person/src/tests.rs +++ b/examples/hello_person/src/tests.rs @@ -1,7 +1,7 @@ use super::rocket; use rocket::testing::MockRequest; use rocket::http::Method::*; - use rocket::http::Status; +use rocket::http::Status; fn test(uri: &str, expected: String) { let rocket = rocket::ignite().mount("/", routes![super::hello, super::hi]); @@ -29,8 +29,8 @@ fn test_hello() { #[test] fn test_failing_hello() { test_404("/hello/Mike/1000"); - test_404("/hello/Mike/128"); test_404("/hello/Mike/-129"); + test_404("/hello/Mike/-1"); } #[test] diff --git a/examples/json/Cargo.toml b/examples/json/Cargo.toml index 7d16802d..af4edb1b 100644 --- a/examples/json/Cargo.toml +++ b/examples/json/Cargo.toml @@ -16,3 +16,6 @@ lazy_static = "*" path = "../../contrib" default-features = false features = ["json"] + +[dev-dependencies] +rocket = { path = "../../lib", features = ["testing"] } diff --git a/examples/json/src/main.rs b/examples/json/src/main.rs index 2ffef3b0..43ef0855 100644 --- a/examples/json/src/main.rs +++ b/examples/json/src/main.rs @@ -7,6 +7,8 @@ extern crate serde_json; #[macro_use] extern crate rocket_contrib; #[macro_use] extern crate serde_derive; +#[cfg(test)] mod tests; + use rocket_contrib::JSON; use std::collections::HashMap; use std::sync::Mutex; @@ -27,22 +29,9 @@ struct Message { } // TODO: This example can be improved by using `route` with muliple HTTP verbs. -// To be precise, put/post could/should look like: -// #[route(PUT, POST, path = "/", format = "application/json")] -// fn f(method: Method, id: ID, message: JSON) -> Option> { -// let mut hashmap = MAP.lock().unwrap(); -// let exists = hashmap.contains_key(&id); -// if method == Method::Put && exists || method == Method::Post && !exists { -// hashmap.insert(id, message.0.contents); -// return Ok(JSON(map!{ "status" => "ok" })) -// } -// -// None -// } - #[post("/", format = "application/json", data = "")] fn new(id: ID, message: JSON) -> JSON { - let mut hashmap = MAP.lock().unwrap(); + let mut hashmap = MAP.lock().expect("map lock."); if hashmap.contains_key(&id) { JSON(map!{ "status" => "error", diff --git a/examples/json/src/tests.rs b/examples/json/src/tests.rs new file mode 100644 index 00000000..97ba7b48 --- /dev/null +++ b/examples/json/src/tests.rs @@ -0,0 +1,95 @@ +use rocket; +use rocket::testing::MockRequest; +use rocket::http::Method::*; +use rocket::http::{Status, ContentType}; +use rocket::Response; + +macro_rules! run_test { + ($req:expr, $test_fn:expr) => ({ + let rocket = rocket::ignite() + .mount("/message", routes![super::new, super::update, super::get]) + .catch(errors![super::not_found]); + + $test_fn($req.dispatch_with(&rocket)); + }) +} + +#[test] +fn bad_get_put() { + // Try to get a message with an ID that doesn't exist. + let mut req = MockRequest::new(Get, "/message/99").header(ContentType::JSON); + run_test!(req, |mut response: Response| { + assert_eq!(response.status(), Status::NotFound); + + let body = response.body().unwrap().into_string().unwrap(); + assert!(body.contains("error")); + assert!(body.contains("Resource was not found.")); + }); + + // Try to get a message with an invalid ID. + let mut req = MockRequest::new(Get, "/message/hi").header(ContentType::JSON); + run_test!(req, |mut response: Response| { + assert_eq!(response.status(), Status::NotFound); + let body = response.body().unwrap().into_string().unwrap(); + assert!(body.contains("error")); + }); + + // Try to put a message without a proper body. + let mut req = MockRequest::new(Put, "/message/80").header(ContentType::JSON); + run_test!(req, |response: Response| { + assert_eq!(response.status(), Status::BadRequest); + }); + + // Try to put a message for an ID that doesn't exist. + let mut req = MockRequest::new(Put, "/message/80") + .header(ContentType::JSON) + .body(r#"{ "contents": "Bye bye, world!" }"#); + + run_test!(req, |response: Response| { + assert_eq!(response.status(), Status::NotFound); + }); +} + +#[test] +fn post_get_put_get() { + // Check that a message with ID 1 doesn't exist. + let mut req = MockRequest::new(Get, "/message/1").header(ContentType::JSON); + run_test!(req, |response: Response| { + assert_eq!(response.status(), Status::NotFound); + }); + + // Add a new message with ID 1. + let mut req = MockRequest::new(Post, "/message/1") + .header(ContentType::JSON) + .body(r#"{ "contents": "Hello, world!" }"#); + + run_test!(req, |response: Response| { + assert_eq!(response.status(), Status::Ok); + }); + + // Check that the message exists with the correct contents. + let mut req = MockRequest::new(Get, "/message/1") .header(ContentType::JSON); + run_test!(req, |mut response: Response| { + assert_eq!(response.status(), Status::Ok); + let body = response.body().unwrap().into_string().unwrap(); + assert!(body.contains("Hello, world!")); + }); + + // Change the message contents. + let mut req = MockRequest::new(Put, "/message/1") + .header(ContentType::JSON) + .body(r#"{ "contents": "Bye bye, world!" }"#); + + run_test!(req, |response: Response| { + assert_eq!(response.status(), Status::Ok); + }); + + // Check that the message exists with the updated contents. + let mut req = MockRequest::new(Get, "/message/1") .header(ContentType::JSON); + run_test!(req, |mut response: Response| { + assert_eq!(response.status(), Status::Ok); + let body = response.body().unwrap().into_string().unwrap(); + assert!(!body.contains("Hello, world!")); + assert!(body.contains("Bye bye, world!")); + }); +} diff --git a/lib/src/response/responder.rs b/lib/src/response/responder.rs index c66b342f..3d090dde 100644 --- a/lib/src/response/responder.rs +++ b/lib/src/response/responder.rs @@ -224,13 +224,13 @@ impl<'r, R: Responder<'r>> Responder<'r> for Option { } } -/// If `self` is `Ok`, responds with the wrapped `Responder`. Otherwise prints a -/// warning message with the `Err` value returns an `Err` of +/// If `self` is `Ok`, responds with the wrapped `Responder`. Otherwise prints +/// an error message with the `Err` value returns an `Err` of /// `Status::InternalServerError`. impl<'r, R: Responder<'r>, E: fmt::Debug> Responder<'r> for Result { default fn respond(self) -> Result, Status> { self.map(|r| r.respond()).unwrap_or_else(|e| { - warn_!("Response was `Err`: {:?}.", e); + error_!("Response was `Err`: {:?}.", e); Err(Status::InternalServerError) }) } diff --git a/scripts/mk-docs.sh b/scripts/mk-docs.sh index e7926995..8bc15b26 100755 --- a/scripts/mk-docs.sh +++ b/scripts/mk-docs.sh @@ -25,3 +25,6 @@ cargo clean mk_doc $LIB_DIR mk_doc $CODEGEN_DIR mk_doc $CONTRIB_DIR --all-features + +# Blank index, for redirection. +touch ${DOC_DIR}/index.html