omaha_client/http_request/
mock.rs

1// Copyright 2019 The Fuchsia Authors
2//
3// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
4// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
5// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
6// This file may not be copied, modified, or distributed except according to
7// those terms.
8
9use crate::http_request::{Error, HttpRequest};
10use futures::future::BoxFuture;
11use futures::prelude::*;
12use http::StatusCode;
13use hyper::{Body, Request, Response};
14use pretty_assertions::assert_eq;
15use std::{cell::RefCell, collections::VecDeque, rc::Rc};
16
17#[cfg(test)]
18use futures::executor::block_on;
19
20#[derive(Debug, Default)]
21pub struct MockHttpRequest {
22    // The requests made using this mock.
23    requests: Rc<RefCell<Vec<Request<Body>>>>,
24    // The queue of fake responses for the upcoming requests.
25    responses: VecDeque<Result<Response<Vec<u8>>, Error>>,
26}
27
28impl HttpRequest for MockHttpRequest {
29    fn request(&mut self, req: Request<Body>) -> BoxFuture<'_, Result<Response<Vec<u8>>, Error>> {
30        self.requests.borrow_mut().push(req);
31
32        future::ready(if let Some(resp) = self.responses.pop_front() {
33            resp
34        } else {
35            // No response to return, generate a 500 internal server error
36            Ok(Response::builder()
37                .status(StatusCode::INTERNAL_SERVER_ERROR)
38                .body(vec![])
39                .unwrap())
40        })
41        .boxed()
42    }
43}
44
45impl MockHttpRequest {
46    pub fn new(res: Response<Vec<u8>>) -> Self {
47        Self {
48            responses: vec![Ok(res)].into(),
49            ..Default::default()
50        }
51    }
52
53    pub fn empty() -> Self {
54        Default::default()
55    }
56
57    pub fn from_request_cell(request: Rc<RefCell<Vec<Request<Body>>>>) -> Self {
58        Self {
59            requests: request,
60            ..Default::default()
61        }
62    }
63
64    pub fn get_request_cell(&self) -> Rc<RefCell<Vec<Request<Body>>>> {
65        Rc::clone(&self.requests)
66    }
67
68    pub fn add_response(&mut self, res: Response<Vec<u8>>) {
69        self.responses.push_back(Ok(res));
70    }
71
72    pub fn add_error(&mut self, error: Error) {
73        self.responses.push_back(Err(error));
74    }
75
76    pub fn assert_method(&self, method: &hyper::Method) {
77        assert_eq!(method, self.requests.borrow().last().unwrap().method());
78    }
79
80    pub fn assert_uri(&self, uri: &str) {
81        assert_eq!(
82            &uri.parse::<hyper::Uri>().unwrap(),
83            self.requests.borrow().last().unwrap().uri()
84        );
85    }
86
87    pub fn assert_header(&self, key: &str, value: &str) {
88        let requests = self.requests.borrow();
89        let request = requests.last().unwrap();
90        let headers = request.headers();
91        assert!(headers.contains_key(key));
92        assert_eq!(headers[key], value);
93    }
94
95    fn take_request(&self) -> Request<Body> {
96        self.requests.borrow_mut().pop().unwrap()
97    }
98
99    pub async fn assert_body(&self, body: &[u8]) {
100        let bytes = hyper::body::to_bytes(self.take_request()).await.unwrap();
101        assert_eq!(body, &bytes);
102    }
103
104    pub async fn assert_body_str(&self, body: &str) {
105        let bytes = hyper::body::to_bytes(self.take_request()).await.unwrap();
106        assert_eq!(body, String::from_utf8_lossy(&bytes));
107    }
108}
109
110#[test]
111fn test_mock() {
112    let res_body = vec![1, 2, 3];
113    let mut mock = MockHttpRequest::new(Response::new(res_body.clone()));
114
115    let req_body = vec![4, 5, 6];
116    let uri = "https://mock.uri/";
117    let req = Request::get(uri)
118        .header("X-Custom-Foo", "Bar")
119        .body(req_body.clone().into())
120        .unwrap();
121    block_on(async {
122        let response = mock.request(req).await.unwrap();
123        assert_eq!(res_body, response.into_body());
124
125        mock.assert_method(&hyper::Method::GET);
126        mock.assert_uri(uri);
127        mock.assert_header("X-Custom-Foo", "Bar");
128        mock.assert_body(req_body.as_slice()).await;
129    });
130}
131
132#[test]
133fn test_missing_response() {
134    let res_body = vec![1, 2, 3];
135    let mut mock = MockHttpRequest::new(Response::new(res_body.clone()));
136    block_on(async {
137        let response = mock.request(Request::default()).await.unwrap();
138        assert_eq!(res_body, response.into_body());
139
140        let response2 = mock.request(Request::default()).await.unwrap();
141        assert_eq!(response2.status(), hyper::StatusCode::INTERNAL_SERVER_ERROR);
142    });
143}
144
145#[test]
146fn test_multiple_responses() {
147    let res_body = vec![1, 2, 3];
148    let mut mock = MockHttpRequest::new(Response::new(res_body.clone()));
149    let res_body2 = vec![4, 5, 6];
150    mock.add_response(Response::new(res_body2.clone()));
151
152    block_on(async {
153        let response = mock.request(Request::default()).await.unwrap();
154        assert_eq!(res_body, response.into_body());
155
156        let response2 = mock.request(Request::default()).await.unwrap();
157        assert_eq!(res_body2, response2.into_body());
158    });
159}