From 69d950f308e0ddf478bd401be1be2170cb63571a Mon Sep 17 00:00:00 2001 From: Tom Niget Date: Sun, 3 Jul 2022 23:28:45 +0200 Subject: [PATCH 1/4] Add additional methods to RequestResult - deserialize_body: Deserializes the body without performing checks on status code or content. - ensure_status_ignore_body: Checks the status code while ignoring body. - expect_status_ignore_body: Checks the status code while ignoring body, panic on failure. - ensure_empty_body: Checks that the body is empty. - ensure_status_empty_body: Checks the status code and the emptiness of the body. - expect_status_empty_body: Checks the status code and the emptiness of the body, panic on failure. --- examples/user_server.rs | 4 +- examples/user_server_test.rs | 4 +- src/request.rs | 128 +++++++++++++++++++++++++++++++++-- 3 files changed, 125 insertions(+), 11 deletions(-) diff --git a/examples/user_server.rs b/examples/user_server.rs index 527c252..1e58ed0 100644 --- a/examples/user_server.rs +++ b/examples/user_server.rs @@ -76,8 +76,8 @@ impl Database { fn delete_route(self) -> impl Filter + Clone { method::delete().and(path::param()).map(move |id| { match self.users.lock().unwrap().remove(&id) { - Some(_) => with_status(json(&"User deleted"), StatusCode::OK), - None => with_status(json(&"Failed to delete user"), StatusCode::NOT_FOUND), + Some(_) => StatusCode::OK, + None => StatusCode::NOT_FOUND, } }) } diff --git a/examples/user_server_test.rs b/examples/user_server_test.rs index 38d9e06..5aba03d 100644 --- a/examples/user_server_test.rs +++ b/examples/user_server_test.rs @@ -180,7 +180,7 @@ pub async fn delete_user() { CONTEXT .run(&request) .await - .expect_status::(StatusCode::OK) + .expect_status_empty_body(StatusCode::OK) .await; // We try to delete the same user again and ensure that the server @@ -188,7 +188,7 @@ pub async fn delete_user() { CONTEXT .run(request) .await - .expect_status::(StatusCode::NOT_FOUND) + .expect_status_empty_body(StatusCode::NOT_FOUND) .await; } diff --git a/src/request.rs b/src/request.rs index 88c9e62..308d439 100644 --- a/src/request.rs +++ b/src/request.rs @@ -304,13 +304,53 @@ impl RequestResult { /// /// # Error /// - /// This method return an error if the server response status is not equal to + /// This method returns an error if the server response status is not equal to /// `status` or if the body can not be deserialized to the specified type. #[track_caller] pub async fn ensure_status(self, status: StatusCode) -> Result where T: DeserializeOwned, { + self.ensure_status_ignore_body(status) + .await? + .deserialize_body() + .await + } + + /// Deserializes the body of the response into a concrete type. + /// + /// This method uses `serde` internally, so the output type must implement + /// [`DeserializeOwned`]. + /// + /// # Error + /// + /// This method returns an error if the body can not be deserialized to the + /// specified type. + #[track_caller] + pub async fn deserialize_body(self) -> Result + where + T: DeserializeOwned, + { + self.response.json().await.map_err(|err| { + format!( + "Failed to deserialize body for request '{}': {}", + self.context_description, err + ) + }) + } + + /// Checks if the response status meets an expected status code. No check is + /// performed on the body. + /// + /// # Error + /// + /// This method returns an error if the server response status is not equal to + /// `status`. + #[track_caller] + pub async fn ensure_status_ignore_body( + self, + status: StatusCode, + ) -> Result { if self.response.status() != status { return Err(format!("Unexpected server response code for request '{}'. Body is {}", self.context_description, @@ -321,11 +361,85 @@ impl RequestResult { )?)); } - self.response.json().await.map_err(|err| { - format!( - "Failed to deserialize body for request '{}': {}", - self.context_description, err - ) - }) + Ok(self) + } + + /// Checks if the response status meets an expected status code. No check is + /// performed on the body. + /// + /// # Panics + /// + /// This method panics if the server response status is not equal to `status`. + #[track_caller] + pub async fn expect_status_ignore_body(self, status: StatusCode) { + match self.ensure_status_ignore_body(status).await { + Ok(_) => {} + Err(err) => panic!("{}", err), + } + } + + /// Checks if the response body is empty. + /// + /// The `Content-length` header is first checked; if it is missing (e.g. in case of + /// a compressed payload or a chunked transfer encoding), the body is fetched and its + /// length is checked. + /// + /// # Error + /// + /// This method returns an error if the body could not be checked to be empty. + #[track_caller] + pub async fn ensure_empty_body(self) -> Result<(), String> { + if matches!(self.response.content_length(), Some(0)) + || self + .response + .text() + .await + .map_err(|err| { + format!( + "Failed to check body for request '{}': {}", + self.context_description, err + ) + })? + .is_empty() + { + Ok(()) + } else { + Err(format!( + "Unexpected body for request '{}'", + self.context_description + )) + } + } + + /// Checks if the response body meets an expected status code and the body is empty. + /// + /// See `ensure_empty_body` for more details. + /// + /// # Error + /// + /// This method returns an error if the server response status is not equal to + /// `status` or if the body could not be checked to be empty. + #[track_caller] + pub async fn ensure_status_empty_body(self, status: StatusCode) -> Result<(), String> { + self.ensure_status_ignore_body(status) + .await? + .ensure_empty_body() + .await + } + + /// Checks if the response body meets an expected status code and the body is empty. + /// + /// See `ensure_empty_body` for more details. + /// + /// # Panics + /// + /// This method panics if the server response status is not equal to `status` or if the + /// body could not be checked to be empty. + #[track_caller] + pub async fn expect_status_empty_body(self, status: StatusCode) -> () { + match self.ensure_status_empty_body(status).await { + Ok(()) => (), + Err(err) => panic!("{}", err), + } } } From b0fd7cc2223e84f958043e8e3a457a61b7f0e94c Mon Sep 17 00:00:00 2001 From: Tom Niget Date: Sun, 3 Jul 2022 23:34:09 +0200 Subject: [PATCH 2/4] Where we're going, we don't need `-> ()` --- src/request.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/request.rs b/src/request.rs index 308d439..e735990 100644 --- a/src/request.rs +++ b/src/request.rs @@ -436,7 +436,7 @@ impl RequestResult { /// This method panics if the server response status is not equal to `status` or if the /// body could not be checked to be empty. #[track_caller] - pub async fn expect_status_empty_body(self, status: StatusCode) -> () { + pub async fn expect_status_empty_body(self, status: StatusCode) { match self.ensure_status_empty_body(status).await { Ok(()) => (), Err(err) => panic!("{}", err), From aa8f07a2eb377d8e332e7c3951c7dfbde07b4281 Mon Sep 17 00:00:00 2001 From: Tom Niget Date: Mon, 4 Jul 2022 12:10:18 +0200 Subject: [PATCH 3/4] Add test cases for new methods --- examples/user_server_test.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/examples/user_server_test.rs b/examples/user_server_test.rs index 5aba03d..96093e1 100644 --- a/examples/user_server_test.rs +++ b/examples/user_server_test.rs @@ -180,7 +180,7 @@ pub async fn delete_user() { CONTEXT .run(&request) .await - .expect_status_empty_body(StatusCode::OK) + .expect_status_empty_body(StatusCode::OK) // Panicking version .await; // We try to delete the same user again and ensure that the server @@ -188,8 +188,9 @@ pub async fn delete_user() { CONTEXT .run(request) .await - .expect_status_empty_body(StatusCode::NOT_FOUND) - .await; + .ensure_status_empty_body(StatusCode::NOT_FOUND) // Result<_, _> version + .await + .unwrap(); } /// Test for the PUT route. @@ -233,6 +234,15 @@ pub async fn put_user() { response, User { year_of_birth: 2001, .. }, }; + + let request = Request::get(path!["users", id]); + + let response = CONTEXT.run(request).await.deserialize_body().await; + + assert_body_matches! { + response, + Ok(User { year_of_birth: 2001, ..}) + }; } fn main() { From 56728a85192db1feb9494526445196b1d02f88f5 Mon Sep 17 00:00:00 2001 From: Tom Niget Date: Mon, 4 Jul 2022 12:22:06 +0200 Subject: [PATCH 4/4] Add tests for *_status_ignore_body --- examples/user_server_test.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/examples/user_server_test.rs b/examples/user_server_test.rs index 96093e1..8c4b8a8 100644 --- a/examples/user_server_test.rs +++ b/examples/user_server_test.rs @@ -140,13 +140,20 @@ pub async fn ensure_status_failing() { // Similarly, execute the said request and get the output. // This time, trying to make the ensure_status fail with a wrong status code let response = CONTEXT - .run(request) + .run(&request) .await - .ensure_status::(StatusCode::ACCEPTED) + .ensure_status_ignore_body(StatusCode::ACCEPTED) // Result<_, _> version .await; // testing if it is returning an error assert!(response.is_err()); + + // Now we check the correct status code + CONTEXT + .run(request) + .await + .expect_status_ignore_body(StatusCode::CREATED) // Panicking version + .await; } /// Test for the DELETE route.