axum_test/
test_server_builder.rs

1use anyhow::Result;
2use std::net::IpAddr;
3
4use crate::transport_layer::IntoTransportLayer;
5use crate::TestServer;
6use crate::TestServerConfig;
7use crate::Transport;
8
9/// A builder for [`crate::TestServer`]. Inside is a [`crate::TestServerConfig`],
10/// configured by each method, and then turn into a server by [`crate::TestServerBuilder::build`].
11///
12/// The recommended way to make instances is to call [`crate::TestServer::builder`].
13///
14/// # Creating a [`crate::TestServer`]
15///
16/// ```rust
17/// # async fn test() -> Result<(), Box<dyn ::std::error::Error>> {
18/// #
19/// use axum::Router;
20/// use axum_test::TestServerBuilder;
21///
22/// let my_app = Router::new();
23/// let server = TestServerBuilder::new()
24///     .save_cookies()
25///     .default_content_type(&"application/json")
26///     .build(my_app)?;
27/// #
28/// # Ok(())
29/// # }
30/// ```
31///
32/// # Creating a [`crate::TestServerConfig`]
33///
34/// ```rust
35/// # async fn test() -> Result<(), Box<dyn ::std::error::Error>> {
36/// #
37/// use axum::Router;
38/// use axum_test::TestServer;
39/// use axum_test::TestServerBuilder;
40///
41/// let my_app = Router::new();
42/// let config = TestServerBuilder::new()
43///     .save_cookies()
44///     .default_content_type(&"application/json")
45///     .into_config();
46///
47/// // Build the Test Server
48/// let server = TestServer::new_with_config(my_app, config)?;
49/// #
50/// # Ok(())
51/// # }
52/// ```
53///
54/// These can be passed to [`crate::TestServer::new_with_config`].
55///
56#[derive(Debug, Clone)]
57pub struct TestServerBuilder {
58    config: TestServerConfig,
59}
60
61impl TestServerBuilder {
62    /// Creates a default `TestServerBuilder`.
63    pub fn new() -> Self {
64        Default::default()
65    }
66
67    pub fn from_config(config: TestServerConfig) -> Self {
68        Self { config }
69    }
70
71    pub fn http_transport(self) -> Self {
72        self.transport(Transport::HttpRandomPort)
73    }
74
75    pub fn http_transport_with_ip_port(self, ip: Option<IpAddr>, port: Option<u16>) -> Self {
76        self.transport(Transport::HttpIpPort { ip, port })
77    }
78
79    pub fn mock_transport(self) -> Self {
80        self.transport(Transport::MockHttp)
81    }
82
83    pub fn transport(mut self, transport: Transport) -> Self {
84        self.config.transport = Some(transport);
85        self
86    }
87
88    pub fn save_cookies(mut self) -> Self {
89        self.config.save_cookies = true;
90        self
91    }
92
93    pub fn do_not_save_cookies(mut self) -> Self {
94        self.config.save_cookies = false;
95        self
96    }
97
98    pub fn default_content_type(mut self, content_type: &str) -> Self {
99        self.config.default_content_type = Some(content_type.to_string());
100        self
101    }
102
103    pub fn default_scheme(mut self, scheme: &str) -> Self {
104        self.config.default_scheme = Some(scheme.to_string());
105        self
106    }
107
108    pub fn expect_success_by_default(mut self) -> Self {
109        self.config.expect_success_by_default = true;
110        self
111    }
112
113    pub fn restrict_requests_with_http_schema(mut self) -> Self {
114        self.config.restrict_requests_with_http_schema = true;
115        self
116    }
117
118    /// For turning this into a [`crate::TestServerConfig`] object,
119    /// with can be passed to [`crate::TestServer::new_with_config`].
120    ///
121    /// ```rust
122    /// # async fn test() -> Result<(), Box<dyn ::std::error::Error>> {
123    /// #
124    /// use axum::Router;
125    /// use axum_test::TestServer;
126    ///
127    /// let my_app = Router::new();
128    /// let config = TestServer::builder()
129    ///     .save_cookies()
130    ///     .default_content_type(&"application/json")
131    ///     .into_config();
132    ///
133    /// // Build the Test Server
134    /// let server = TestServer::new_with_config(my_app, config)?;
135    /// #
136    /// # Ok(())
137    /// # }
138    /// ```
139    pub fn into_config(self) -> TestServerConfig {
140        self.config
141    }
142
143    /// Creates a new [`crate::TestServer`], running the application given,
144    /// and with all settings from this `TestServerBuilder` applied.
145    ///
146    /// ```rust
147    /// use axum::Router;
148    /// use axum_test::TestServer;
149    ///
150    /// let app = Router::new();
151    /// let server = TestServer::builder()
152    ///     .save_cookies()
153    ///     .default_content_type(&"application/json")
154    ///     .build(app);
155    /// ```
156    ///
157    /// This is the equivalent to building [`crate::TestServerConfig`] yourself,
158    /// and calling [`crate::TestServer::new_with_config`].
159    pub fn build<A>(self, app: A) -> Result<TestServer>
160    where
161        A: IntoTransportLayer,
162    {
163        self.into_config().build(app)
164    }
165}
166
167impl Default for TestServerBuilder {
168    fn default() -> Self {
169        Self {
170            config: TestServerConfig::default(),
171        }
172    }
173}
174
175impl From<TestServerConfig> for TestServerBuilder {
176    fn from(config: TestServerConfig) -> Self {
177        TestServerBuilder::from_config(config)
178    }
179}
180
181#[cfg(test)]
182mod test_build {
183    use super::*;
184    use std::net::Ipv4Addr;
185
186    #[test]
187    fn it_should_build_default_config_by_default() {
188        let config = TestServer::builder().into_config();
189        let expected = TestServerConfig::default();
190
191        assert_eq!(config, expected);
192    }
193
194    #[test]
195    fn it_should_save_cookies_when_set() {
196        let config = TestServer::builder().save_cookies().into_config();
197
198        assert_eq!(config.save_cookies, true);
199    }
200
201    #[test]
202    fn it_should_not_save_cookies_when_set() {
203        let config = TestServer::builder().do_not_save_cookies().into_config();
204
205        assert_eq!(config.save_cookies, false);
206    }
207
208    #[test]
209    fn it_should_mock_transport_when_set() {
210        let config = TestServer::builder().mock_transport().into_config();
211
212        assert_eq!(config.transport, Some(Transport::MockHttp));
213    }
214
215    #[test]
216    fn it_should_use_random_http_transport_when_set() {
217        let config = TestServer::builder().http_transport().into_config();
218
219        assert_eq!(config.transport, Some(Transport::HttpRandomPort));
220    }
221
222    #[test]
223    fn it_should_use_http_transport_with_ip_port_when_set() {
224        let config = TestServer::builder()
225            .http_transport_with_ip_port(Some(IpAddr::V4(Ipv4Addr::new(123, 4, 5, 6))), Some(987))
226            .into_config();
227
228        assert_eq!(
229            config.transport,
230            Some(Transport::HttpIpPort {
231                ip: Some(IpAddr::V4(Ipv4Addr::new(123, 4, 5, 6))),
232                port: Some(987),
233            })
234        );
235    }
236
237    #[test]
238    fn it_should_set_default_content_type_when_set() {
239        let config = TestServer::builder()
240            .default_content_type("text/csv")
241            .into_config();
242
243        assert_eq!(config.default_content_type, Some("text/csv".to_string()));
244    }
245
246    #[test]
247    fn it_should_set_default_scheme_when_set() {
248        let config = TestServer::builder().default_scheme("ftps").into_config();
249
250        assert_eq!(config.default_scheme, Some("ftps".to_string()));
251    }
252
253    #[test]
254    fn it_should_set_expect_success_by_default_when_set() {
255        let config = TestServer::builder()
256            .expect_success_by_default()
257            .into_config();
258
259        assert_eq!(config.expect_success_by_default, true);
260    }
261
262    #[test]
263    fn it_should_set_restrict_requests_with_http_schema_when_set() {
264        let config = TestServer::builder()
265            .restrict_requests_with_http_schema()
266            .into_config();
267
268        assert_eq!(config.restrict_requests_with_http_schema, true);
269    }
270}