| // Copyright 2022 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| //! Mock HttpsClient for use in testing. |
| //! |
| //! Usage: |
| //! ``` |
| //! let mut https_client = HttpsClient::mock(); |
| //! // Set up expected requests and responses (as many as needed, in the order |
| //! // they should happen). |
| //! https_client.expect(req, res); |
| //! // Use the https_client in-place of the normal https_client. As matching |
| //! // requests come in, the premade responses will be returned. |
| //! ``` |
| |
| use hyper::{Body, Request}; |
| use std::collections::VecDeque; |
| use std::sync::Mutex; |
| |
| #[derive(Debug)] |
| struct HttpsClientEvent { |
| req: Request<Body>, |
| res: http::Result<http::Response<Body>>, |
| } |
| |
| #[derive(Debug)] |
| pub struct HttpsClient { |
| expected: Mutex<VecDeque<HttpsClientEvent>>, |
| } |
| |
| impl HttpsClient { |
| /// Create a new mock https client. |
| /// |
| /// Consider adding expected events with `.expect(req, res)`. |
| pub fn mock() -> Self { |
| Self { expected: Mutex::new(VecDeque::new()) } |
| } |
| |
| /// Append an expected request and response. |
| /// |
| /// Note that `res` is actually a Result<> type. |
| pub fn expect(&mut self, req: Request<Body>, res: http::Result<http::Response<Body>>) { |
| self.expected.lock().expect("locking").push_back(HttpsClientEvent { req, res }); |
| } |
| |
| pub async fn request(&self, req: Request<Body>) -> http::Result<http::Response<Body>> { |
| let expected = self.expected.lock().expect("locking").pop_front().unwrap_or_else(|| { |
| panic!( |
| "Error: received more https requests than expected. \ |
| No response available for req: {:?}", |
| req |
| ) |
| }); |
| |
| assert_eq!( |
| expected.req.uri(), |
| req.uri(), |
| "mock_https_client actual {:?}, expected {:?}, related response {:?}", |
| req, |
| expected.req, |
| expected.res |
| ); |
| assert_eq!(expected.req.method(), req.method()); |
| let expected_bytes = |
| hyper::body::to_bytes(expected.req.into_body()).await.expect("expected.req.into_body"); |
| let req_bytes = hyper::body::to_bytes(req.into_body()).await.expect("req.into_body"); |
| assert_eq!(expected_bytes, req_bytes); |
| |
| expected.res |
| } |
| } |
| |
| pub fn new_https_client() -> HttpsClient { |
| HttpsClient::mock() |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| use hyper::Method; |
| |
| #[fuchsia_async::run_singlethreaded(test)] |
| async fn test_mock() { |
| let mut https_client = HttpsClient::mock(); |
| |
| let req = Request::builder() |
| .method(Method::POST) |
| .uri("https://example.com") |
| .body(Body::from("alpha")) |
| .expect("Request::builder"); |
| let builder = http::Response::builder().status(http::StatusCode::OK); |
| let res = builder.body(Body::from("beta")); |
| https_client.expect(req, res); |
| |
| let req = Request::builder() |
| .method(Method::POST) |
| .uri("https://example.com") |
| .body(Body::from("alpha")) |
| .expect("Request::builder"); |
| let res = https_client.request(req).await.expect("https_client.request"); |
| assert_eq!(res.status(), http::StatusCode::OK); |
| let bytes = hyper::body::to_bytes(res.into_body()).await.expect("body::to_bytes"); |
| assert_eq!(b"beta", &bytes[..]); |
| } |
| |
| #[should_panic(expected = "actual Request")] |
| #[fuchsia_async::run_singlethreaded(test)] |
| async fn test_mock_fail() { |
| let mut https_client = HttpsClient::mock(); |
| |
| let req = Request::builder() |
| .method(Method::POST) |
| .uri("https://example.com") |
| .body(Body::from("alpha")) |
| .expect("Request::builder"); |
| let builder = http::Response::builder().status(http::StatusCode::OK); |
| let res = builder.body(Body::from("beta")); |
| https_client.expect(req, res); |
| |
| let req = Request::builder() |
| .method(Method::POST) |
| .uri("https://not-same.com") |
| .body(Body::from("alpha")) |
| .expect("Request::builder"); |
| let res = https_client.request(req).await.expect("https_client.request"); |
| assert_eq!(res.status(), http::StatusCode::OK); |
| } |
| |
| #[test] |
| fn test_new_https_client() { |
| let https_client = new_https_client(); |
| assert!(https_client.expected.lock().expect("locking").is_empty()); |
| } |
| } |