tiny_http/
test.rs

1use crate::{request::new_request, HTTPVersion, Header, HeaderField, Method, Request};
2use ascii::AsciiString;
3use std::net::SocketAddr;
4use std::str::FromStr;
5
6/// A simpler version of [`Request`] that is useful for testing. No data actually goes anywhere.
7///
8/// By default, `TestRequest` pretends to be an insecure GET request for the server root (`/`)
9/// with no headers. To create a `TestRequest` with different parameters, use the builder pattern:
10///
11/// ```
12/// # use tiny_http::{Method, TestRequest};
13/// let request = TestRequest::new()
14///     .with_method(Method::Post)
15///     .with_path("/api/widgets")
16///     .with_body("42");
17/// ```
18///
19/// Then, convert the `TestRequest` into a real `Request` and pass it to the server under test:
20///
21/// ```
22/// # use tiny_http::{Method, Request, Response, Server, StatusCode, TestRequest};
23/// # use std::io::Cursor;
24/// # let request = TestRequest::new()
25/// #     .with_method(Method::Post)
26/// #     .with_path("/api/widgets")
27/// #     .with_body("42");
28/// # struct TestServer {
29/// #     listener: Server,
30/// # }
31/// # let server = TestServer {
32/// #     listener: Server::http("0.0.0.0:0").unwrap(),
33/// # };
34/// # impl TestServer {
35/// #     fn handle_request(&self, request: Request) -> Response<Cursor<Vec<u8>>> {
36/// #         Response::from_string("test")
37/// #     }
38/// # }
39/// let response = server.handle_request(request.into());
40/// assert_eq!(response.status_code(), StatusCode(200));
41/// ```
42pub struct TestRequest {
43    body: &'static str,
44    remote_addr: SocketAddr,
45    // true if HTTPS, false if HTTP
46    secure: bool,
47    method: Method,
48    path: String,
49    http_version: HTTPVersion,
50    headers: Vec<Header>,
51}
52
53impl From<TestRequest> for Request {
54    fn from(mut mock: TestRequest) -> Request {
55        // if the user didn't set the Content-Length header, then set it for them
56        // otherwise, leave it alone (it may be under test)
57        if !mock
58            .headers
59            .iter_mut()
60            .any(|h| h.field.equiv("Content-Length"))
61        {
62            mock.headers.push(Header {
63                field: HeaderField::from_str("Content-Length").unwrap(),
64                value: AsciiString::from_ascii(mock.body.len().to_string()).unwrap(),
65            });
66        }
67        new_request(
68            mock.secure,
69            mock.method,
70            mock.path,
71            mock.http_version,
72            mock.headers,
73            Some(mock.remote_addr),
74            mock.body.as_bytes(),
75            std::io::sink(),
76        )
77        .unwrap()
78    }
79}
80
81impl Default for TestRequest {
82    fn default() -> Self {
83        TestRequest {
84            body: "",
85            remote_addr: "127.0.0.1:23456".parse().unwrap(),
86            secure: false,
87            method: Method::Get,
88            path: "/".to_string(),
89            http_version: HTTPVersion::from((1, 1)),
90            headers: Vec::new(),
91        }
92    }
93}
94
95impl TestRequest {
96    pub fn new() -> Self {
97        TestRequest::default()
98    }
99    pub fn with_body(mut self, body: &'static str) -> Self {
100        self.body = body;
101        self
102    }
103    pub fn with_remote_addr(mut self, remote_addr: SocketAddr) -> Self {
104        self.remote_addr = remote_addr;
105        self
106    }
107    pub fn with_https(mut self) -> Self {
108        self.secure = true;
109        self
110    }
111    pub fn with_method(mut self, method: Method) -> Self {
112        self.method = method;
113        self
114    }
115    pub fn with_path(mut self, path: &str) -> Self {
116        self.path = path.to_string();
117        self
118    }
119    pub fn with_http_version(mut self, version: HTTPVersion) -> Self {
120        self.http_version = version;
121        self
122    }
123    pub fn with_header(mut self, header: Header) -> Self {
124        self.headers.push(header);
125        self
126    }
127}