ntex_server/net/
test.rs

1//! Test server
2use std::{io, net, thread};
3
4use ntex_net::{tcp_connect, Io};
5use ntex_rt::System;
6use ntex_service::ServiceFactory;
7use socket2::{Domain, SockAddr, Socket, Type};
8
9use super::{Server, ServerBuilder};
10
11/// Start test server
12///
13/// `TestServer` is very simple test server that simplify process of writing
14/// integration tests cases for ntex web applications.
15///
16/// # Examples
17///
18/// ```rust
19/// use ntex::http;
20/// use ntex::http::client::Client;
21/// use ntex::server;
22/// use ntex::web::{self, App, HttpResponse};
23///
24/// async fn my_handler() -> Result<HttpResponse, std::io::Error> {
25///     Ok(HttpResponse::Ok().into())
26/// }
27///
28/// #[ntex::test]
29/// async fn test_example() {
30///     let mut srv = server::test_server(
31///         || http::HttpService::new(
32///             App::new().service(
33///                 web::resource("/").to(my_handler))
34///         )
35///     );
36///
37///     let req = Client::new().get("http://127.0.0.1:{}", srv.addr().port());
38///     let response = req.send().await.unwrap();
39///     assert!(response.status().is_success());
40/// }
41/// ```
42pub fn test_server<F, R>(factory: F) -> TestServer
43where
44    F: Fn() -> R + Send + Clone + 'static,
45    R: ServiceFactory<Io> + 'static,
46{
47    let (tx, rx) = oneshot::channel();
48    // run server in separate thread
49    thread::spawn(move || {
50        let sys = System::new("ntex-test-server");
51        let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap();
52        let local_addr = tcp.local_addr().unwrap();
53        let system = sys.system();
54
55        sys.run(move || {
56            let server = ServerBuilder::new()
57                .listen("test", tcp, move |_| factory())?
58                .set_tag("test", "TEST-SERVER")
59                .workers(1)
60                .disable_signals()
61                .run();
62
63            ntex_rt::spawn(async move {
64                ntex_util::time::sleep(ntex_util::time::Millis(75)).await;
65                tx.send((system, local_addr, server))
66                    .expect("Failed to send Server to TestServer");
67            });
68
69            Ok(())
70        })
71    });
72
73    let (system, addr, server) = rx.recv().unwrap();
74
75    TestServer {
76        addr,
77        server,
78        system,
79    }
80}
81
82/// Start new server with server builder
83pub fn build_test_server<F>(factory: F) -> TestServer
84where
85    F: FnOnce(ServerBuilder) -> ServerBuilder + Send + 'static,
86{
87    let (tx, rx) = oneshot::channel();
88    // run server in separate thread
89    thread::spawn(move || {
90        let sys = System::new("ntex-test-server");
91        let system = sys.system();
92
93        sys.run(|| {
94            let server = factory(super::build()).workers(1).disable_signals().run();
95            tx.send((system, server))
96                .expect("Failed to send Server to TestServer");
97            Ok(())
98        })
99    });
100    let (system, server) = rx.recv().unwrap();
101
102    TestServer {
103        system,
104        server,
105        addr: "127.0.0.1:0".parse().unwrap(),
106    }
107}
108
109#[derive(Debug)]
110/// Test server controller
111pub struct TestServer {
112    addr: net::SocketAddr,
113    system: System,
114    server: Server,
115}
116
117impl TestServer {
118    /// Test server socket addr
119    pub fn addr(&self) -> net::SocketAddr {
120        self.addr
121    }
122
123    pub fn set_addr(mut self, addr: net::SocketAddr) -> Self {
124        self.addr = addr;
125        self
126    }
127
128    /// Connect to server, return Io
129    pub async fn connect(&self) -> io::Result<Io> {
130        tcp_connect(self.addr).await
131    }
132
133    /// Stop http server by stopping the runtime.
134    pub fn stop(&self) {
135        self.system.stop();
136    }
137
138    /// Get first available unused address
139    pub fn unused_addr() -> net::SocketAddr {
140        let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap();
141        let socket = Socket::new(Domain::IPV4, Type::STREAM, None).unwrap();
142        socket.set_reuse_address(true).unwrap();
143        socket.bind(&SockAddr::from(addr)).unwrap();
144        let tcp = net::TcpListener::from(socket);
145        tcp.local_addr().unwrap()
146    }
147
148    /// Get access to the running Server
149    pub fn server(&self) -> Server {
150        self.server.clone()
151    }
152}
153
154impl Drop for TestServer {
155    fn drop(&mut self) {
156        self.stop()
157    }
158}