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}