trillium_testing/test_conn.rs
1use std::{
2 fmt::Debug,
3 net::IpAddr,
4 ops::{Deref, DerefMut},
5 sync::Arc,
6};
7use trillium::{Conn, Handler, HeaderName, HeaderValues, Method, Transport};
8use trillium_http::{Conn as HttpConn, ServerConfig, Synthetic};
9
10type SyntheticConn = HttpConn<Synthetic>;
11
12/// A wrapper around a [`trillium::Conn`] for testing
13///
14/// Stability note: this may be replaced by an extension trait at some point.
15#[derive(Debug)]
16pub struct TestConn(Conn);
17
18impl TestConn {
19 /// constructs a new TestConn with the provided method, path, and body.
20 /// ```
21 /// use trillium_testing::{TestConn, prelude::*};
22 /// let mut conn = TestConn::build("get", "/", "body");
23 /// assert_eq!(conn.method(), Method::Get);
24 /// assert_eq!(conn.path(), "/");
25 /// assert_eq!(conn.take_request_body_string(), "body");
26 /// ```
27 pub fn build<M>(method: M, path: impl Into<String>, body: impl Into<Synthetic>) -> Self
28 where
29 M: TryInto<Method>,
30 <M as TryInto<Method>>::Error: Debug,
31 {
32 Self(HttpConn::new_synthetic(method.try_into().unwrap(), path.into(), body).into())
33 }
34
35 /// assigns a shared server config to this test conn
36 pub fn with_server_config(self, server_config: Arc<ServerConfig>) -> Self {
37 let inner = self
38 .0
39 .into_inner::<Synthetic>()
40 .with_server_config(server_config)
41 .into();
42
43 Self(inner)
44 }
45
46 /// chainable constructor to append a request header to the TestConn
47 /// ```
48 /// use trillium_testing::TestConn;
49 /// let conn = TestConn::build("get", "/", "body").with_request_header("some-header", "value");
50 /// assert_eq!(conn.request_headers().get_str("some-header"), Some("value"));
51 /// ```
52 pub fn with_request_header(
53 self,
54 header_name: impl Into<HeaderName<'static>>,
55 header_value: impl Into<HeaderValues>,
56 ) -> Self {
57 let mut inner: SyntheticConn = self.into();
58 inner
59 .request_headers_mut()
60 .append(header_name, header_value);
61 Self(inner.into())
62 }
63
64 /// chainable constructor to replace the request body. this is useful
65 /// when chaining with a [`trillium_testing::methods`](crate::methods)
66 /// builder, as they do not provide a way to specify the body.
67 ///
68 /// ```
69 /// use trillium_testing::{TestConn, methods::post};
70 /// let mut conn = post("/").with_request_body("some body");
71 /// assert_eq!(conn.take_request_body_string(), "some body");
72 ///
73 /// let mut conn = TestConn::build("post", "/", "some body").with_request_body("once told me");
74 /// assert_eq!(conn.take_request_body_string(), "once told me");
75 /// ```
76 pub fn with_request_body(self, body: impl Into<Synthetic>) -> Self {
77 let mut inner: SyntheticConn = self.into();
78 inner.replace_body(body);
79 Self(inner.into())
80 }
81
82 /// sets the peer ip for this test conn
83 pub fn with_peer_ip(mut self, ip: IpAddr) -> Self {
84 self.set_peer_ip(Some(ip));
85 self
86 }
87
88 /// set the test conn to be secure
89 pub fn secure(mut self) -> Self {
90 AsMut::<trillium_http::Conn<Box<dyn Transport>>>::as_mut(&mut self.0).set_secure(true);
91 self
92 }
93
94 /// set state on the test conn
95 pub fn with_state<S>(mut self, state: S) -> Self
96 where
97 S: Send + Sync + 'static,
98 {
99 self.0.insert_state(state);
100 self
101 }
102
103 /// blocks on running this conn against a handler and finalizes
104 /// response headers. also aliased as [`TestConn::on`]
105 ///
106 /// ```
107 /// use trillium_testing::prelude::*;
108 ///
109 /// async fn handler(conn: Conn) -> Conn {
110 /// conn.ok("hello trillium")
111 /// }
112 ///
113 /// let conn = get("/").run(&handler);
114 /// assert_ok!(conn, "hello trillium", "content-length" => "14");
115 /// ```
116 pub fn run(self, handler: &impl Handler) -> Self {
117 crate::block_on(self.run_async(handler))
118 }
119
120 /// runs this conn against a handler and finalizes
121 /// response headers.
122 ///
123 /// ```
124 /// use trillium_testing::prelude::*;
125 ///
126 /// async fn handler(conn: Conn) -> Conn {
127 /// conn.ok("hello trillium")
128 /// }
129 ///
130 /// block_on(async move {
131 /// let conn = get("/").run_async(&handler).await;
132 /// assert_ok!(conn, "hello trillium", "content-length" => "14");
133 /// });
134 /// ```
135 pub async fn run_async(self, handler: &impl Handler) -> Self {
136 let conn = handler.run(self.into()).await;
137 let mut conn = handler.before_send(conn).await;
138 AsMut::<trillium_http::Conn<Box<dyn Transport>>>::as_mut(&mut conn).finalize_headers();
139 Self(conn)
140 }
141
142 /// blocks on running this conn against a handler and finalizes
143 /// response headers. also aliased as [`TestConn::run`].
144 ///
145 /// ```
146 /// use trillium_testing::prelude::*;
147 ///
148 /// async fn handler(conn: Conn) -> Conn {
149 /// conn.ok("hello trillium")
150 /// }
151 ///
152 /// let conn = get("/").on(&handler);
153 /// assert_ok!(conn, "hello trillium", "content-length" => "14");
154 /// ```
155 pub fn on(self, handler: &impl Handler) -> Self {
156 self.run(handler)
157 }
158
159 /// Reads the response body to string and returns it, if set.
160 pub fn take_response_body_string(&mut self) -> Option<String> {
161 match self.take_response_body() {
162 Some(body) => String::from_utf8(
163 futures_lite::future::block_on(body.into_bytes())
164 .unwrap()
165 .to_vec(),
166 )
167 .ok(),
168 _ => None,
169 }
170 }
171
172 /// Reads the request body to string and returns it
173 pub fn take_request_body_string(&mut self) -> String {
174 futures_lite::future::block_on(async {
175 self.request_body().await.read_string().await.unwrap()
176 })
177 }
178}
179
180impl From<Conn> for TestConn {
181 fn from(conn: Conn) -> Self {
182 Self(conn)
183 }
184}
185
186impl From<TestConn> for Conn {
187 fn from(tc: TestConn) -> Self {
188 tc.0
189 }
190}
191
192impl From<TestConn> for SyntheticConn {
193 fn from(tc: TestConn) -> Self {
194 tc.0.into_inner()
195 }
196}
197
198impl Deref for TestConn {
199 type Target = Conn;
200
201 fn deref(&self) -> &Self::Target {
202 &self.0
203 }
204}
205
206impl DerefMut for TestConn {
207 fn deref_mut(&mut self) -> &mut Self::Target {
208 &mut self.0
209 }
210}