mirror of
https://github.com/fafhrd91/actix-web
synced 2024-11-27 09:42:57 +01:00
migrate integration testing to new crate (#2112)
This commit is contained in:
parent
50dc13f280
commit
c54a0713de
@ -15,11 +15,14 @@
|
|||||||
### Removed
|
### Removed
|
||||||
* The `client` mod was removed. Clients should now use `awc` directly.
|
* The `client` mod was removed. Clients should now use `awc` directly.
|
||||||
[871ca5e4](https://github.com/actix/actix-web/commit/871ca5e4ae2bdc22d1ea02701c2992fa8d04aed7)
|
[871ca5e4](https://github.com/actix/actix-web/commit/871ca5e4ae2bdc22d1ea02701c2992fa8d04aed7)
|
||||||
|
* Integration testing was moved to new `actix-test` crate. Namely these items from the `test`
|
||||||
|
module: `TestServer`, `TestServerConfig`, `start`, `start_with`, and `unused_addr`. [#2112]
|
||||||
|
|
||||||
[#2067]: https://github.com/actix/actix-web/pull/2067
|
[#2067]: https://github.com/actix/actix-web/pull/2067
|
||||||
[#2093]: https://github.com/actix/actix-web/pull/2093
|
[#2093]: https://github.com/actix/actix-web/pull/2093
|
||||||
[#2094]: https://github.com/actix/actix-web/pull/2094
|
[#2094]: https://github.com/actix/actix-web/pull/2094
|
||||||
[#2097]: https://github.com/actix/actix-web/pull/2097
|
[#2097]: https://github.com/actix/actix-web/pull/2097
|
||||||
|
[#2112]: https://github.com/actix/actix-web/pull/2112
|
||||||
|
|
||||||
|
|
||||||
## 4.0.0-beta.4 - 2021-03-09
|
## 4.0.0-beta.4 - 2021-03-09
|
||||||
|
28
Cargo.toml
28
Cargo.toml
@ -36,25 +36,26 @@ members = [
|
|||||||
"actix-web-actors",
|
"actix-web-actors",
|
||||||
"actix-web-codegen",
|
"actix-web-codegen",
|
||||||
"actix-http-test",
|
"actix-http-test",
|
||||||
|
"actix-test",
|
||||||
]
|
]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["compress", "cookies"]
|
default = ["compress", "cookies"]
|
||||||
|
|
||||||
# content-encoding support
|
# content-encoding support
|
||||||
compress = ["actix-http/compress", "awc/compress"]
|
compress = ["actix-http/compress"]
|
||||||
|
|
||||||
# support for cookies
|
# support for cookies
|
||||||
cookies = ["actix-http/cookies", "awc/cookies"]
|
cookies = ["actix-http/cookies"]
|
||||||
|
|
||||||
# secure cookies feature
|
# secure cookies feature
|
||||||
secure-cookies = ["actix-http/secure-cookies"]
|
secure-cookies = ["actix-http/secure-cookies"]
|
||||||
|
|
||||||
# openssl
|
# openssl
|
||||||
openssl = ["tls-openssl", "actix-tls/accept", "actix-tls/openssl", "awc/openssl"]
|
openssl = ["actix-http/openssl", "actix-tls/accept", "actix-tls/openssl"]
|
||||||
|
|
||||||
# rustls
|
# rustls
|
||||||
rustls = ["tls-rustls", "actix-tls/accept", "actix-tls/rustls", "awc/rustls"]
|
rustls = ["actix-http/rustls", "actix-tls/accept", "actix-tls/rustls"]
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "basic"
|
name = "basic"
|
||||||
@ -72,10 +73,6 @@ required-features = ["compress", "cookies"]
|
|||||||
name = "on_connect"
|
name = "on_connect"
|
||||||
required-features = []
|
required-features = []
|
||||||
|
|
||||||
[[example]]
|
|
||||||
name = "client"
|
|
||||||
required-features = ["rustls"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
actix-codec = "0.4.0-beta.1"
|
actix-codec = "0.4.0-beta.1"
|
||||||
actix-macros = "0.2.0"
|
actix-macros = "0.2.0"
|
||||||
@ -88,7 +85,6 @@ actix-tls = { version = "3.0.0-beta.5", default-features = false, optional = tru
|
|||||||
|
|
||||||
actix-web-codegen = "0.5.0-beta.2"
|
actix-web-codegen = "0.5.0-beta.2"
|
||||||
actix-http = "3.0.0-beta.4"
|
actix-http = "3.0.0-beta.4"
|
||||||
awc = { version = "3.0.0-beta.3", default-features = false }
|
|
||||||
|
|
||||||
ahash = "0.7"
|
ahash = "0.7"
|
||||||
bytes = "1"
|
bytes = "1"
|
||||||
@ -109,11 +105,12 @@ serde_urlencoded = "0.7"
|
|||||||
smallvec = "1.6"
|
smallvec = "1.6"
|
||||||
socket2 = "0.4.0"
|
socket2 = "0.4.0"
|
||||||
time = { version = "0.2.23", default-features = false, features = ["std"] }
|
time = { version = "0.2.23", default-features = false, features = ["std"] }
|
||||||
tls-openssl = { package = "openssl", version = "0.10.9", optional = true }
|
|
||||||
tls-rustls = { package = "rustls", version = "0.19.0", optional = true }
|
|
||||||
url = "2.1"
|
url = "2.1"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
|
actix-test = { version = "0.0.1", features = ["openssl", "rustls"] }
|
||||||
|
awc = { version = "3.0.0-beta.3", features = ["openssl"] }
|
||||||
|
|
||||||
brotli2 = "0.3.2"
|
brotli2 = "0.3.2"
|
||||||
criterion = "0.3"
|
criterion = "0.3"
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
@ -121,6 +118,8 @@ flate2 = "1.0.13"
|
|||||||
rand = "0.8"
|
rand = "0.8"
|
||||||
rcgen = "0.8"
|
rcgen = "0.8"
|
||||||
serde_derive = "1.0"
|
serde_derive = "1.0"
|
||||||
|
tls-openssl = { package = "openssl", version = "0.10.9" }
|
||||||
|
tls-rustls = { package = "rustls", version = "0.19.0" }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
@ -128,13 +127,14 @@ opt-level = 3
|
|||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
|
|
||||||
[patch.crates-io]
|
[patch.crates-io]
|
||||||
actix-web = { path = "." }
|
actix-files = { path = "actix-files" }
|
||||||
actix-http = { path = "actix-http" }
|
actix-http = { path = "actix-http" }
|
||||||
actix-http-test = { path = "actix-http-test" }
|
actix-http-test = { path = "actix-http-test" }
|
||||||
|
actix-multipart = { path = "actix-multipart" }
|
||||||
|
actix-test = { path = "actix-test" }
|
||||||
|
actix-web = { path = "." }
|
||||||
actix-web-actors = { path = "actix-web-actors" }
|
actix-web-actors = { path = "actix-web-actors" }
|
||||||
actix-web-codegen = { path = "actix-web-codegen" }
|
actix-web-codegen = { path = "actix-web-codegen" }
|
||||||
actix-multipart = { path = "actix-multipart" }
|
|
||||||
actix-files = { path = "actix-files" }
|
|
||||||
awc = { path = "awc" }
|
awc = { path = "awc" }
|
||||||
|
|
||||||
[[bench]]
|
[[bench]]
|
||||||
|
@ -35,3 +35,4 @@ percent-encoding = "2.1"
|
|||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2.2"
|
actix-rt = "2.2"
|
||||||
actix-web = "4.0.0-beta.4"
|
actix-web = "4.0.0-beta.4"
|
||||||
|
actix-test = "0.0.1"
|
||||||
|
@ -413,7 +413,7 @@ mod tests {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_named_file_content_range_headers() {
|
async fn test_named_file_content_range_headers() {
|
||||||
let srv = test::start(|| App::new().service(Files::new("/", ".")));
|
let srv = actix_test::start(|| App::new().service(Files::new("/", ".")));
|
||||||
|
|
||||||
// Valid range header
|
// Valid range header
|
||||||
let response = srv
|
let response = srv
|
||||||
@ -438,7 +438,7 @@ mod tests {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_named_file_content_length_headers() {
|
async fn test_named_file_content_length_headers() {
|
||||||
let srv = test::start(|| App::new().service(Files::new("/", ".")));
|
let srv = actix_test::start(|| App::new().service(Files::new("/", ".")));
|
||||||
|
|
||||||
// Valid range header
|
// Valid range header
|
||||||
let response = srv
|
let response = srv
|
||||||
@ -477,7 +477,7 @@ mod tests {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_head_content_length_headers() {
|
async fn test_head_content_length_headers() {
|
||||||
let srv = test::start(|| App::new().service(Files::new("/", ".")));
|
let srv = actix_test::start(|| App::new().service(Files::new("/", ".")));
|
||||||
|
|
||||||
let response = srv.head("/tests/test.binary").send().await.unwrap();
|
let response = srv.head("/tests/test.binary").send().await.unwrap();
|
||||||
|
|
||||||
|
@ -117,16 +117,6 @@ pub async fn test_server_with_addr<F: ServiceFactory<TcpStream>>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get first available unused address
|
|
||||||
pub fn unused_addr() -> net::SocketAddr {
|
|
||||||
let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap();
|
|
||||||
let socket = Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP)).unwrap();
|
|
||||||
socket.bind(&addr.into()).unwrap();
|
|
||||||
socket.set_reuse_address(true).unwrap();
|
|
||||||
let tcp = net::TcpListener::from(socket);
|
|
||||||
tcp.local_addr().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test server controller
|
/// Test server controller
|
||||||
pub struct TestServer {
|
pub struct TestServer {
|
||||||
addr: net::SocketAddr,
|
addr: net::SocketAddr,
|
||||||
@ -279,3 +269,13 @@ impl Drop for TestServer {
|
|||||||
self.stop()
|
self.stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get a localhost socket address with random, unused port.
|
||||||
|
pub fn unused_addr() -> net::SocketAddr {
|
||||||
|
let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap();
|
||||||
|
let socket = Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP)).unwrap();
|
||||||
|
socket.bind(&addr.into()).unwrap();
|
||||||
|
socket.set_reuse_address(true).unwrap();
|
||||||
|
let tcp = net::TcpListener::from(socket);
|
||||||
|
tcp.local_addr().unwrap()
|
||||||
|
}
|
||||||
|
@ -48,7 +48,7 @@ actix-service = "2.0.0-beta.4"
|
|||||||
actix-codec = "0.4.0-beta.1"
|
actix-codec = "0.4.0-beta.1"
|
||||||
actix-utils = "3.0.0-beta.4"
|
actix-utils = "3.0.0-beta.4"
|
||||||
actix-rt = "2.2"
|
actix-rt = "2.2"
|
||||||
actix-tls = "3.0.0-beta.5"
|
actix-tls = { version = "3.0.0-beta.5", features = ["accept", "connect"] }
|
||||||
|
|
||||||
ahash = "0.7"
|
ahash = "0.7"
|
||||||
base64 = "0.13"
|
base64 = "0.13"
|
||||||
|
@ -153,11 +153,14 @@ where
|
|||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Response: Into<Response<B>> + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
<S::Service as Service<Request>>::Future: 'static,
|
<S::Service as Service<Request>>::Future: 'static,
|
||||||
|
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
|
|
||||||
X: ServiceFactory<Request, Config = (), Response = Request>,
|
X: ServiceFactory<Request, Config = (), Response = Request>,
|
||||||
X::Future: 'static,
|
X::Future: 'static,
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
X::InitError: fmt::Debug,
|
X::InitError: fmt::Debug,
|
||||||
|
|
||||||
U: ServiceFactory<
|
U: ServiceFactory<
|
||||||
(Request, Framed<TcpStream, h1::Codec>),
|
(Request, Framed<TcpStream, h1::Codec>),
|
||||||
Config = (),
|
Config = (),
|
||||||
@ -187,11 +190,12 @@ where
|
|||||||
|
|
||||||
#[cfg(feature = "openssl")]
|
#[cfg(feature = "openssl")]
|
||||||
mod openssl {
|
mod openssl {
|
||||||
use super::*;
|
|
||||||
use actix_service::ServiceFactoryExt;
|
use actix_service::ServiceFactoryExt;
|
||||||
use actix_tls::accept::openssl::{Acceptor, SslAcceptor, SslError, TlsStream};
|
use actix_tls::accept::openssl::{Acceptor, SslAcceptor, SslError, TlsStream};
|
||||||
use actix_tls::accept::TlsError;
|
use actix_tls::accept::TlsError;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
impl<S, B, X, U> HttpService<TlsStream<TcpStream>, S, B, X, U>
|
impl<S, B, X, U> HttpService<TlsStream<TcpStream>, S, B, X, U>
|
||||||
where
|
where
|
||||||
S: ServiceFactory<Request, Config = ()>,
|
S: ServiceFactory<Request, Config = ()>,
|
||||||
@ -200,11 +204,14 @@ mod openssl {
|
|||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Response: Into<Response<B>> + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
<S::Service as Service<Request>>::Future: 'static,
|
<S::Service as Service<Request>>::Future: 'static,
|
||||||
|
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
|
|
||||||
X: ServiceFactory<Request, Config = (), Response = Request>,
|
X: ServiceFactory<Request, Config = (), Response = Request>,
|
||||||
X::Future: 'static,
|
X::Future: 'static,
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
X::InitError: fmt::Debug,
|
X::InitError: fmt::Debug,
|
||||||
|
|
||||||
U: ServiceFactory<
|
U: ServiceFactory<
|
||||||
(Request, Framed<TlsStream<TcpStream>, h1::Codec>),
|
(Request, Framed<TlsStream<TcpStream>, h1::Codec>),
|
||||||
Config = (),
|
Config = (),
|
||||||
@ -266,11 +273,14 @@ mod rustls {
|
|||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Response: Into<Response<B>> + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
<S::Service as Service<Request>>::Future: 'static,
|
<S::Service as Service<Request>>::Future: 'static,
|
||||||
|
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
|
|
||||||
X: ServiceFactory<Request, Config = (), Response = Request>,
|
X: ServiceFactory<Request, Config = (), Response = Request>,
|
||||||
X::Future: 'static,
|
X::Future: 'static,
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
X::InitError: fmt::Debug,
|
X::InitError: fmt::Debug,
|
||||||
|
|
||||||
U: ServiceFactory<
|
U: ServiceFactory<
|
||||||
(Request, Framed<TlsStream<TcpStream>, h1::Codec>),
|
(Request, Framed<TlsStream<TcpStream>, h1::Codec>),
|
||||||
Config = (),
|
Config = (),
|
||||||
@ -280,7 +290,7 @@ mod rustls {
|
|||||||
U::Error: fmt::Display + Into<Error>,
|
U::Error: fmt::Display + Into<Error>,
|
||||||
U::InitError: fmt::Debug,
|
U::InitError: fmt::Debug,
|
||||||
{
|
{
|
||||||
/// Create openssl based service
|
/// Create rustls based service
|
||||||
pub fn rustls(
|
pub fn rustls(
|
||||||
self,
|
self,
|
||||||
mut config: ServerConfig,
|
mut config: ServerConfig,
|
||||||
@ -321,17 +331,21 @@ impl<T, S, B, X, U> ServiceFactory<(T, Protocol, Option<net::SocketAddr>)>
|
|||||||
for HttpService<T, S, B, X, U>
|
for HttpService<T, S, B, X, U>
|
||||||
where
|
where
|
||||||
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
T: AsyncRead + AsyncWrite + Unpin + 'static,
|
||||||
|
|
||||||
S: ServiceFactory<Request, Config = ()>,
|
S: ServiceFactory<Request, Config = ()>,
|
||||||
S::Future: 'static,
|
S::Future: 'static,
|
||||||
S::Error: Into<Error> + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Response: Into<Response<B>> + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
<S::Service as Service<Request>>::Future: 'static,
|
<S::Service as Service<Request>>::Future: 'static,
|
||||||
|
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
|
|
||||||
X: ServiceFactory<Request, Config = (), Response = Request>,
|
X: ServiceFactory<Request, Config = (), Response = Request>,
|
||||||
X::Future: 'static,
|
X::Future: 'static,
|
||||||
X::Error: Into<Error>,
|
X::Error: Into<Error>,
|
||||||
X::InitError: fmt::Debug,
|
X::InitError: fmt::Debug,
|
||||||
|
|
||||||
U: ServiceFactory<(Request, Framed<T, h1::Codec>), Config = (), Response = ()>,
|
U: ServiceFactory<(Request, Framed<T, h1::Codec>), Config = (), Response = ()>,
|
||||||
U::Future: 'static,
|
U::Future: 'static,
|
||||||
U::Error: fmt::Display + Into<Error>,
|
U::Error: fmt::Display + Into<Error>,
|
||||||
|
6
actix-test/CHANGES.md
Normal file
6
actix-test/CHANGES.md
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# Changes
|
||||||
|
|
||||||
|
## Unreleased - 2021-xx-xx
|
||||||
|
* Move integration testing structs from `actix-web`. [#???]
|
||||||
|
|
||||||
|
[#???]: https://github.com/actix/actix-web/pull/???
|
35
actix-test/Cargo.toml
Normal file
35
actix-test/Cargo.toml
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
[package]
|
||||||
|
name = "actix-test"
|
||||||
|
version = "0.0.1"
|
||||||
|
authors = ["Rob Ede <robjtede@icloud.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
description = "Integration testing tools for Actix Web applications"
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = []
|
||||||
|
|
||||||
|
# rustls
|
||||||
|
rustls = ["tls-rustls", "actix-http/rustls"]
|
||||||
|
|
||||||
|
# openssl
|
||||||
|
openssl = ["tls-openssl", "actix-http/openssl"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
actix-codec = "0.4.0-beta.1"
|
||||||
|
actix-http = { version = "3.0.0-beta.4", features = ["cookies"] }
|
||||||
|
actix-http-test = { version = "3.0.0-beta.3", features = [] }
|
||||||
|
actix-service = "2.0.0-beta.4"
|
||||||
|
actix-utils = "3.0.0-beta.2"
|
||||||
|
actix-web = { version = "4.0.0-beta.4", default-features = false, features = ["cookies"] }
|
||||||
|
actix-rt = "2.1"
|
||||||
|
awc = { version = "3.0.0-beta.3", default-features = false, features = ["cookies"] }
|
||||||
|
|
||||||
|
futures-core = { version = "0.3.7", default-features = false, features = ["std"] }
|
||||||
|
futures-util = { version = "0.3.7", default-features = false, features = [] }
|
||||||
|
log = "0.4"
|
||||||
|
serde = { version = "1", features = ["derive"] }
|
||||||
|
serde_json = "1"
|
||||||
|
serde_urlencoded = "0.7"
|
||||||
|
tls-openssl = { package = "openssl", version = "0.10.9", optional = true }
|
||||||
|
tls-rustls = { package = "rustls", version = "0.19.0", optional = true }
|
1
actix-test/LICENSE-APACHE
Symbolic link
1
actix-test/LICENSE-APACHE
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../LICENSE-APACHE
|
1
actix-test/LICENSE-MIT
Symbolic link
1
actix-test/LICENSE-MIT
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../LICENSE-MIT
|
472
actix-test/src/lib.rs
Normal file
472
actix-test/src/lib.rs
Normal file
@ -0,0 +1,472 @@
|
|||||||
|
//! Integration testing tools for Actix Web applications.
|
||||||
|
//!
|
||||||
|
//! The main integration testing tool is [`TestServer`]. It spawns a real HTTP server on an
|
||||||
|
//! unused port and provides methods that use a real HTTP client. Therefore, it is much closer to
|
||||||
|
//! real-world cases than using `init_service`, which skips HTTP encoding and decoding.
|
||||||
|
//!
|
||||||
|
//! # Examples
|
||||||
|
//! ```
|
||||||
|
//! use actix_web::{get, web, test, App, HttpResponse, Error, Responder};
|
||||||
|
//!
|
||||||
|
//! #[get("/")]
|
||||||
|
//! async fn my_handler() -> Result<impl Responder, Error> {
|
||||||
|
//! Ok(HttpResponse::Ok())
|
||||||
|
//! }
|
||||||
|
//!
|
||||||
|
//! #[actix_rt::test]
|
||||||
|
//! async fn test_example() {
|
||||||
|
//! let srv = actix_test::start(||
|
||||||
|
//! App::new().service(my_handler)
|
||||||
|
//! );
|
||||||
|
//!
|
||||||
|
//! let req = srv.get("/");
|
||||||
|
//! let res = req.send().await.unwrap();
|
||||||
|
//!
|
||||||
|
//! assert!(res.status().is_success());
|
||||||
|
//! }
|
||||||
|
//! ```
|
||||||
|
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
extern crate tls_openssl as openssl;
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
|
extern crate tls_rustls as rustls;
|
||||||
|
|
||||||
|
use std::{fmt, net, sync::mpsc, thread, time};
|
||||||
|
|
||||||
|
use actix_codec::{AsyncRead, AsyncWrite, Framed};
|
||||||
|
pub use actix_http::test::TestBuffer;
|
||||||
|
use actix_http::{
|
||||||
|
http::{HeaderMap, Method},
|
||||||
|
ws, HttpService, Request,
|
||||||
|
};
|
||||||
|
use actix_service::{map_config, IntoServiceFactory, ServiceFactory};
|
||||||
|
use actix_web::{
|
||||||
|
dev::{AppConfig, MessageBody, Server, Service},
|
||||||
|
rt, web, Error, HttpResponse,
|
||||||
|
};
|
||||||
|
use awc::{error::PayloadError, Client, ClientRequest, ClientResponse, Connector};
|
||||||
|
use futures_core::Stream;
|
||||||
|
|
||||||
|
pub use actix_http_test::unused_addr;
|
||||||
|
pub use actix_web::test::{
|
||||||
|
call_service, default_service, init_service, load_stream, ok_service, read_body,
|
||||||
|
read_body_json, read_response, read_response_json, TestRequest,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Start default [`TestServer`].
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// use actix_web::{get, web, test, App, HttpResponse, Error, Responder};
|
||||||
|
///
|
||||||
|
/// #[get("/")]
|
||||||
|
/// async fn my_handler() -> Result<impl Responder, Error> {
|
||||||
|
/// Ok(HttpResponse::Ok())
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// #[actix_rt::test]
|
||||||
|
/// async fn test_example() {
|
||||||
|
/// let srv = actix_test::start(||
|
||||||
|
/// App::new().service(my_handler)
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// let req = srv.get("/");
|
||||||
|
/// let res = req.send().await.unwrap();
|
||||||
|
///
|
||||||
|
/// assert!(res.status().is_success());
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn start<F, I, S, B>(factory: F) -> TestServer
|
||||||
|
where
|
||||||
|
F: Fn() -> I + Send + Clone + 'static,
|
||||||
|
I: IntoServiceFactory<S, Request>,
|
||||||
|
S: ServiceFactory<Request, Config = AppConfig> + 'static,
|
||||||
|
S::Error: Into<Error> + 'static,
|
||||||
|
S::InitError: fmt::Debug,
|
||||||
|
S::Response: Into<HttpResponse<B>> + 'static,
|
||||||
|
<S::Service as Service<Request>>::Future: 'static,
|
||||||
|
B: MessageBody + 'static,
|
||||||
|
{
|
||||||
|
start_with(TestServerConfig::default(), factory)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start test server with custom configuration
|
||||||
|
///
|
||||||
|
/// Check [`TestServerConfig`] docs for configuration options.
|
||||||
|
///
|
||||||
|
/// # Examples
|
||||||
|
/// ```
|
||||||
|
/// use actix_web::{get, web, test, App, HttpResponse, Error, Responder};
|
||||||
|
///
|
||||||
|
/// #[get("/")]
|
||||||
|
/// async fn my_handler() -> Result<impl Responder, Error> {
|
||||||
|
/// Ok(HttpResponse::Ok())
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// #[actix_rt::test]
|
||||||
|
/// async fn test_example() {
|
||||||
|
/// let srv = actix_test::start_with(actix_test::config().h1(), ||
|
||||||
|
/// App::new().service(my_handler)
|
||||||
|
/// );
|
||||||
|
///
|
||||||
|
/// let req = srv.get("/");
|
||||||
|
/// let res = req.send().await.unwrap();
|
||||||
|
///
|
||||||
|
/// assert!(res.status().is_success());
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
pub fn start_with<F, I, S, B>(cfg: TestServerConfig, factory: F) -> TestServer
|
||||||
|
where
|
||||||
|
F: Fn() -> I + Send + Clone + 'static,
|
||||||
|
I: IntoServiceFactory<S, Request>,
|
||||||
|
S: ServiceFactory<Request, Config = AppConfig> + 'static,
|
||||||
|
S::Error: Into<Error> + 'static,
|
||||||
|
S::InitError: fmt::Debug,
|
||||||
|
S::Response: Into<HttpResponse<B>> + 'static,
|
||||||
|
<S::Service as Service<Request>>::Future: 'static,
|
||||||
|
B: MessageBody + 'static,
|
||||||
|
{
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
|
||||||
|
let tls = match cfg.stream {
|
||||||
|
StreamType::Tcp => false,
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
StreamType::Openssl(_) => true,
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
|
StreamType::Rustls(_) => true,
|
||||||
|
};
|
||||||
|
|
||||||
|
// run server in separate thread
|
||||||
|
thread::spawn(move || {
|
||||||
|
let sys = rt::System::new();
|
||||||
|
let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap();
|
||||||
|
let local_addr = tcp.local_addr().unwrap();
|
||||||
|
let factory = factory.clone();
|
||||||
|
let srv_cfg = cfg.clone();
|
||||||
|
let timeout = cfg.client_timeout;
|
||||||
|
let builder = Server::build().workers(1).disable_signals();
|
||||||
|
|
||||||
|
let srv = match srv_cfg.stream {
|
||||||
|
StreamType::Tcp => match srv_cfg.tp {
|
||||||
|
HttpVer::Http1 => builder.listen("test", tcp, move || {
|
||||||
|
let app_cfg =
|
||||||
|
AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);
|
||||||
|
HttpService::build()
|
||||||
|
.client_timeout(timeout)
|
||||||
|
.h1(map_config(factory(), move |_| app_cfg.clone()))
|
||||||
|
.tcp()
|
||||||
|
}),
|
||||||
|
HttpVer::Http2 => builder.listen("test", tcp, move || {
|
||||||
|
let app_cfg =
|
||||||
|
AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);
|
||||||
|
HttpService::build()
|
||||||
|
.client_timeout(timeout)
|
||||||
|
.h2(map_config(factory(), move |_| app_cfg.clone()))
|
||||||
|
.tcp()
|
||||||
|
}),
|
||||||
|
HttpVer::Both => builder.listen("test", tcp, move || {
|
||||||
|
let app_cfg =
|
||||||
|
AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);
|
||||||
|
HttpService::build()
|
||||||
|
.client_timeout(timeout)
|
||||||
|
.finish(map_config(factory(), move |_| app_cfg.clone()))
|
||||||
|
.tcp()
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
StreamType::Openssl(acceptor) => match cfg.tp {
|
||||||
|
HttpVer::Http1 => builder.listen("test", tcp, move || {
|
||||||
|
let app_cfg =
|
||||||
|
AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);
|
||||||
|
HttpService::build()
|
||||||
|
.client_timeout(timeout)
|
||||||
|
.h1(map_config(factory(), move |_| app_cfg.clone()))
|
||||||
|
.openssl(acceptor.clone())
|
||||||
|
}),
|
||||||
|
HttpVer::Http2 => builder.listen("test", tcp, move || {
|
||||||
|
let app_cfg =
|
||||||
|
AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);
|
||||||
|
HttpService::build()
|
||||||
|
.client_timeout(timeout)
|
||||||
|
.h2(map_config(factory(), move |_| app_cfg.clone()))
|
||||||
|
.openssl(acceptor.clone())
|
||||||
|
}),
|
||||||
|
HttpVer::Both => builder.listen("test", tcp, move || {
|
||||||
|
let app_cfg =
|
||||||
|
AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);
|
||||||
|
HttpService::build()
|
||||||
|
.client_timeout(timeout)
|
||||||
|
.finish(map_config(factory(), move |_| app_cfg.clone()))
|
||||||
|
.openssl(acceptor.clone())
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
|
StreamType::Rustls(config) => match cfg.tp {
|
||||||
|
HttpVer::Http1 => builder.listen("test", tcp, move || {
|
||||||
|
let app_cfg =
|
||||||
|
AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);
|
||||||
|
HttpService::build()
|
||||||
|
.client_timeout(timeout)
|
||||||
|
.h1(map_config(factory(), move |_| app_cfg.clone()))
|
||||||
|
.rustls(config.clone())
|
||||||
|
}),
|
||||||
|
HttpVer::Http2 => builder.listen("test", tcp, move || {
|
||||||
|
let app_cfg =
|
||||||
|
AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);
|
||||||
|
HttpService::build()
|
||||||
|
.client_timeout(timeout)
|
||||||
|
.h2(map_config(factory(), move |_| app_cfg.clone()))
|
||||||
|
.rustls(config.clone())
|
||||||
|
}),
|
||||||
|
HttpVer::Both => builder.listen("test", tcp, move || {
|
||||||
|
let app_cfg =
|
||||||
|
AppConfig::__priv_test_new(false, local_addr.to_string(), local_addr);
|
||||||
|
HttpService::build()
|
||||||
|
.client_timeout(timeout)
|
||||||
|
.finish(map_config(factory(), move |_| app_cfg.clone()))
|
||||||
|
.rustls(config.clone())
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
sys.block_on(async {
|
||||||
|
let srv = srv.run();
|
||||||
|
tx.send((rt::System::current(), srv, local_addr)).unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
sys.run()
|
||||||
|
});
|
||||||
|
|
||||||
|
let (system, server, addr) = rx.recv().unwrap();
|
||||||
|
|
||||||
|
let client = {
|
||||||
|
let connector = {
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
{
|
||||||
|
use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
|
||||||
|
|
||||||
|
let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
|
||||||
|
builder.set_verify(SslVerifyMode::NONE);
|
||||||
|
let _ = builder
|
||||||
|
.set_alpn_protos(b"\x02h2\x08http/1.1")
|
||||||
|
.map_err(|e| log::error!("Can not set alpn protocol: {:?}", e));
|
||||||
|
Connector::new()
|
||||||
|
.conn_lifetime(time::Duration::from_secs(0))
|
||||||
|
.timeout(time::Duration::from_millis(30000))
|
||||||
|
.ssl(builder.build())
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "openssl"))]
|
||||||
|
{
|
||||||
|
Connector::new()
|
||||||
|
.conn_lifetime(time::Duration::from_secs(0))
|
||||||
|
.timeout(time::Duration::from_millis(30000))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Client::builder().connector(connector).finish()
|
||||||
|
};
|
||||||
|
|
||||||
|
TestServer {
|
||||||
|
addr,
|
||||||
|
client,
|
||||||
|
system,
|
||||||
|
tls,
|
||||||
|
server,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
enum HttpVer {
|
||||||
|
Http1,
|
||||||
|
Http2,
|
||||||
|
Both,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
enum StreamType {
|
||||||
|
Tcp,
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
Openssl(openssl::ssl::SslAcceptor),
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
|
Rustls(rustls::ServerConfig),
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create default test server config.
|
||||||
|
pub fn config() -> TestServerConfig {
|
||||||
|
TestServerConfig::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct TestServerConfig {
|
||||||
|
tp: HttpVer,
|
||||||
|
stream: StreamType,
|
||||||
|
client_timeout: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TestServerConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
TestServerConfig::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestServerConfig {
|
||||||
|
/// Create default server configuration
|
||||||
|
pub(crate) fn new() -> TestServerConfig {
|
||||||
|
TestServerConfig {
|
||||||
|
tp: HttpVer::Both,
|
||||||
|
stream: StreamType::Tcp,
|
||||||
|
client_timeout: 5000,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Accept HTTP/1.1 only.
|
||||||
|
pub fn h1(mut self) -> Self {
|
||||||
|
self.tp = HttpVer::Http1;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Accept HTTP/2 only.
|
||||||
|
pub fn h2(mut self) -> Self {
|
||||||
|
self.tp = HttpVer::Http2;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Accept secure connections via OpenSSL.
|
||||||
|
#[cfg(feature = "openssl")]
|
||||||
|
pub fn openssl(mut self, acceptor: openssl::ssl::SslAcceptor) -> Self {
|
||||||
|
self.stream = StreamType::Openssl(acceptor);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Accept secure connections via Rustls.
|
||||||
|
#[cfg(feature = "rustls")]
|
||||||
|
pub fn rustls(mut self, config: rustls::ServerConfig) -> Self {
|
||||||
|
self.stream = StreamType::Rustls(config);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set client timeout in milliseconds for first request.
|
||||||
|
pub fn client_timeout(mut self, val: u64) -> Self {
|
||||||
|
self.client_timeout = val;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A basic HTTP server controller that simplifies the process of writing integration tests for
|
||||||
|
/// Actix Web applications.
|
||||||
|
///
|
||||||
|
/// See [`start`] for usage example.
|
||||||
|
pub struct TestServer {
|
||||||
|
addr: net::SocketAddr,
|
||||||
|
client: awc::Client,
|
||||||
|
system: rt::System,
|
||||||
|
tls: bool,
|
||||||
|
server: Server,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TestServer {
|
||||||
|
/// Construct test server url
|
||||||
|
pub fn addr(&self) -> net::SocketAddr {
|
||||||
|
self.addr
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Construct test server url
|
||||||
|
pub fn url(&self, uri: &str) -> String {
|
||||||
|
let scheme = if self.tls { "https" } else { "http" };
|
||||||
|
|
||||||
|
if uri.starts_with('/') {
|
||||||
|
format!("{}://localhost:{}{}", scheme, self.addr.port(), uri)
|
||||||
|
} else {
|
||||||
|
format!("{}://localhost:{}/{}", scheme, self.addr.port(), uri)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create `GET` request.
|
||||||
|
pub fn get(&self, path: impl AsRef<str>) -> ClientRequest {
|
||||||
|
self.client.get(self.url(path.as_ref()).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create `POST` request.
|
||||||
|
pub fn post(&self, path: impl AsRef<str>) -> ClientRequest {
|
||||||
|
self.client.post(self.url(path.as_ref()).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create `HEAD` request.
|
||||||
|
pub fn head(&self, path: impl AsRef<str>) -> ClientRequest {
|
||||||
|
self.client.head(self.url(path.as_ref()).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create `PUT` request.
|
||||||
|
pub fn put(&self, path: impl AsRef<str>) -> ClientRequest {
|
||||||
|
self.client.put(self.url(path.as_ref()).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create `PATCH` request.
|
||||||
|
pub fn patch(&self, path: impl AsRef<str>) -> ClientRequest {
|
||||||
|
self.client.patch(self.url(path.as_ref()).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create `DELETE` request.
|
||||||
|
pub fn delete(&self, path: impl AsRef<str>) -> ClientRequest {
|
||||||
|
self.client.delete(self.url(path.as_ref()).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create `OPTIONS` request.
|
||||||
|
pub fn options(&self, path: impl AsRef<str>) -> ClientRequest {
|
||||||
|
self.client.options(self.url(path.as_ref()).as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connect request with given method and path.
|
||||||
|
pub fn request(&self, method: Method, path: impl AsRef<str>) -> ClientRequest {
|
||||||
|
self.client.request(method, path.as_ref())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn load_body<S>(
|
||||||
|
&mut self,
|
||||||
|
mut response: ClientResponse<S>,
|
||||||
|
) -> Result<web::Bytes, PayloadError>
|
||||||
|
where
|
||||||
|
S: Stream<Item = Result<web::Bytes, PayloadError>> + Unpin + 'static,
|
||||||
|
{
|
||||||
|
response.body().limit(10_485_760).await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connect to WebSocket server at a given path.
|
||||||
|
pub async fn ws_at(
|
||||||
|
&mut self,
|
||||||
|
path: &str,
|
||||||
|
) -> Result<Framed<impl AsyncRead + AsyncWrite, ws::Codec>, awc::error::WsClientError> {
|
||||||
|
let url = self.url(path);
|
||||||
|
let connect = self.client.ws(url).connect();
|
||||||
|
connect.await.map(|(_, framed)| framed)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Connect to a WebSocket server.
|
||||||
|
pub async fn ws(
|
||||||
|
&mut self,
|
||||||
|
) -> Result<Framed<impl AsyncRead + AsyncWrite, ws::Codec>, awc::error::WsClientError> {
|
||||||
|
self.ws_at("/").await
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get default HeaderMap of Client.
|
||||||
|
///
|
||||||
|
/// Returns Some(&mut HeaderMap) when Client object is unique
|
||||||
|
/// (No other clone of client exists at the same time).
|
||||||
|
pub fn client_headers(&mut self) -> Option<&mut HeaderMap> {
|
||||||
|
self.client.headers()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gracefully stop HTTP server.
|
||||||
|
pub async fn stop(self) {
|
||||||
|
self.server.stop(true).await;
|
||||||
|
self.system.stop();
|
||||||
|
rt::time::sleep(time::Duration::from_millis(100)).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for TestServer {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.system.stop()
|
||||||
|
}
|
||||||
|
}
|
@ -29,6 +29,8 @@ tokio = { version = "1", features = ["sync"] }
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2.2"
|
actix-rt = "2.2"
|
||||||
|
actix-test = "0.0.1"
|
||||||
|
|
||||||
awc = { version = "3.0.0-beta.3", default-features = false }
|
awc = { version = "3.0.0-beta.3", default-features = false }
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
futures-util = { version = "0.3.7", default-features = false }
|
futures-util = { version = "0.3.7", default-features = false }
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use actix::prelude::*;
|
use actix::prelude::*;
|
||||||
use actix_web::{
|
use actix_web::{
|
||||||
http::{header, StatusCode},
|
http::{header, StatusCode},
|
||||||
test, web, App, HttpRequest, HttpResponse,
|
web, App, HttpRequest, HttpResponse,
|
||||||
};
|
};
|
||||||
use actix_web_actors::*;
|
use actix_web_actors::*;
|
||||||
use bytes::Bytes;
|
use bytes::Bytes;
|
||||||
@ -27,7 +27,7 @@ impl StreamHandler<Result<ws::Message, ws::ProtocolError>> for Ws {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_simple() {
|
async fn test_simple() {
|
||||||
let mut srv = test::start(|| {
|
let mut srv = actix_test::start(|| {
|
||||||
App::new().service(web::resource("/").to(
|
App::new().service(web::resource("/").to(
|
||||||
|req: HttpRequest, stream: web::Payload| async move { ws::start(Ws, &req, stream) },
|
|req: HttpRequest, stream: web::Payload| async move { ws::start(Ws, &req, stream) },
|
||||||
))
|
))
|
||||||
@ -62,7 +62,7 @@ async fn test_simple() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_with_credentials() {
|
async fn test_with_credentials() {
|
||||||
let mut srv = test::start(|| {
|
let mut srv = actix_test::start(|| {
|
||||||
App::new().service(web::resource("/").to(
|
App::new().service(web::resource("/").to(
|
||||||
|req: HttpRequest, stream: web::Payload| async move {
|
|req: HttpRequest, stream: web::Payload| async move {
|
||||||
if req.headers().contains_key("Authorization") {
|
if req.headers().contains_key("Authorization") {
|
||||||
|
@ -20,8 +20,10 @@ proc-macro2 = "1"
|
|||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
actix-rt = "2.2"
|
actix-rt = "2.2"
|
||||||
actix-web = "4.0.0-beta.4"
|
actix-test = "0.0.1"
|
||||||
actix-utils = "3.0.0-beta.4"
|
actix-utils = "3.0.0-beta.4"
|
||||||
|
actix-web = "4.0.0-beta.4"
|
||||||
|
|
||||||
futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
futures-core = { version = "0.3.7", default-features = false, features = ["alloc"] }
|
||||||
trybuild = "1"
|
trybuild = "1"
|
||||||
rustversion = "1"
|
rustversion = "1"
|
||||||
|
@ -4,7 +4,7 @@ use std::task::{Context, Poll};
|
|||||||
use actix_utils::future;
|
use actix_utils::future;
|
||||||
use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
|
use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
|
||||||
use actix_web::http::header::{HeaderName, HeaderValue};
|
use actix_web::http::header::{HeaderName, HeaderValue};
|
||||||
use actix_web::{http, test, web::Path, App, Error, HttpResponse, Responder};
|
use actix_web::{http, web::Path, App, Error, HttpResponse, Responder};
|
||||||
use actix_web_codegen::{connect, delete, get, head, options, patch, post, put, route, trace};
|
use actix_web_codegen::{connect, delete, get, head, options, patch, post, put, route, trace};
|
||||||
use futures_core::future::LocalBoxFuture;
|
use futures_core::future::LocalBoxFuture;
|
||||||
|
|
||||||
@ -149,7 +149,7 @@ async fn get_wrap(_: Path<String>) -> impl Responder {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_params() {
|
async fn test_params() {
|
||||||
let srv = test::start(|| {
|
let srv = actix_test::start(|| {
|
||||||
App::new()
|
App::new()
|
||||||
.service(get_param_test)
|
.service(get_param_test)
|
||||||
.service(put_param_test)
|
.service(put_param_test)
|
||||||
@ -171,7 +171,7 @@ async fn test_params() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_body() {
|
async fn test_body() {
|
||||||
let srv = test::start(|| {
|
let srv = actix_test::start(|| {
|
||||||
App::new()
|
App::new()
|
||||||
.service(post_test)
|
.service(post_test)
|
||||||
.service(put_test)
|
.service(put_test)
|
||||||
@ -245,7 +245,7 @@ async fn test_body() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_auto_async() {
|
async fn test_auto_async() {
|
||||||
let srv = test::start(|| App::new().service(auto_async));
|
let srv = actix_test::start(|| App::new().service(auto_async));
|
||||||
|
|
||||||
let request = srv.request(http::Method::GET, srv.url("/test"));
|
let request = srv.request(http::Method::GET, srv.url("/test"));
|
||||||
let response = request.send().await.unwrap();
|
let response = request.send().await.unwrap();
|
||||||
@ -254,7 +254,7 @@ async fn test_auto_async() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_wrap() {
|
async fn test_wrap() {
|
||||||
let srv = test::start(|| App::new().service(get_wrap));
|
let srv = actix_test::start(|| App::new().service(get_wrap));
|
||||||
|
|
||||||
let request = srv.request(http::Method::GET, srv.url("/test/wrap"));
|
let request = srv.request(http::Method::GET, srv.url("/test/wrap"));
|
||||||
let response = request.send().await.unwrap();
|
let response = request.send().await.unwrap();
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use actix_web::{Responder, HttpResponse, App, test};
|
use actix_web::{Responder, HttpResponse, App};
|
||||||
use actix_web_codegen::*;
|
use actix_web_codegen::*;
|
||||||
|
|
||||||
/// Docstrings shouldn't break anything.
|
/// doc comments shouldn't break anything
|
||||||
#[get("/")]
|
#[get("/")]
|
||||||
async fn index() -> impl Responder {
|
async fn index() -> impl Responder {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
@ -9,7 +9,7 @@ async fn index() -> impl Responder {
|
|||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let srv = test::start(|| App::new().service(index));
|
let srv = actix_test::start(|| App::new().service(index));
|
||||||
|
|
||||||
let request = srv.get("/");
|
let request = srv.get("/");
|
||||||
let response = request.send().await.unwrap();
|
let response = request.send().await.unwrap();
|
||||||
|
@ -7,9 +7,9 @@ async fn index() -> String {
|
|||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
use actix_web::{App, test};
|
use actix_web::App;
|
||||||
|
|
||||||
let srv = test::start(|| App::new().service(index));
|
let srv = actix_test::start(|| App::new().service(index));
|
||||||
|
|
||||||
let request = srv.get("/");
|
let request = srv.get("/");
|
||||||
let response = request.send().await.unwrap();
|
let response = request.send().await.unwrap();
|
||||||
|
@ -5,7 +5,7 @@ error: HTTP method defined more than once: `GET`
|
|||||||
| ^^^^^
|
| ^^^^^
|
||||||
|
|
||||||
error[E0425]: cannot find value `index` in this scope
|
error[E0425]: cannot find value `index` in this scope
|
||||||
--> $DIR/route-duplicate-method-fail.rs:12:49
|
--> $DIR/route-duplicate-method-fail.rs:12:55
|
||||||
|
|
|
|
||||||
12 | let srv = test::start(|| App::new().service(index));
|
12 | let srv = actix_test::start(|| App::new().service(index));
|
||||||
| ^^^^^ not found in this scope
|
| ^^^^^ not found in this scope
|
||||||
|
@ -7,9 +7,9 @@ async fn index() -> String {
|
|||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
use actix_web::{App, test};
|
use actix_web::App;
|
||||||
|
|
||||||
let srv = test::start(|| App::new().service(index));
|
let srv = actix_test::start(|| App::new().service(index));
|
||||||
|
|
||||||
let request = srv.get("/");
|
let request = srv.get("/");
|
||||||
let response = request.send().await.unwrap();
|
let response = request.send().await.unwrap();
|
||||||
|
@ -7,7 +7,7 @@ error: The #[route(..)] macro requires at least one `method` attribute
|
|||||||
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
= note: this error originates in an attribute macro (in Nightly builds, run with -Z macro-backtrace for more info)
|
||||||
|
|
||||||
error[E0425]: cannot find value `index` in this scope
|
error[E0425]: cannot find value `index` in this scope
|
||||||
--> $DIR/route-missing-method-fail.rs:12:49
|
--> $DIR/route-missing-method-fail.rs:12:55
|
||||||
|
|
|
|
||||||
12 | let srv = test::start(|| App::new().service(index));
|
12 | let srv = actix_test::start(|| App::new().service(index));
|
||||||
| ^^^^^ not found in this scope
|
| ^^^^^ not found in this scope
|
||||||
|
@ -7,9 +7,9 @@ async fn index() -> String {
|
|||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
use actix_web::{App, test};
|
use actix_web::App;
|
||||||
|
|
||||||
let srv = test::start(|| App::new().service(index));
|
let srv = actix_test::start(|| App::new().service(index));
|
||||||
|
|
||||||
let request = srv.get("/");
|
let request = srv.get("/");
|
||||||
let response = request.send().await.unwrap();
|
let response = request.send().await.unwrap();
|
||||||
|
@ -7,9 +7,9 @@ async fn index() -> String {
|
|||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
use actix_web::{App, test};
|
use actix_web::App;
|
||||||
|
|
||||||
let srv = test::start(|| App::new().service(index));
|
let srv = actix_test::start(|| App::new().service(index));
|
||||||
|
|
||||||
let request = srv.get("/");
|
let request = srv.get("/");
|
||||||
let response = request.send().await.unwrap();
|
let response = request.send().await.unwrap();
|
||||||
|
@ -5,7 +5,7 @@ error: Unexpected HTTP method: `UNEXPECTED`
|
|||||||
| ^^^^^^^^^^^^
|
| ^^^^^^^^^^^^
|
||||||
|
|
||||||
error[E0425]: cannot find value `index` in this scope
|
error[E0425]: cannot find value `index` in this scope
|
||||||
--> $DIR/route-unexpected-method-fail.rs:12:49
|
--> $DIR/route-unexpected-method-fail.rs:12:55
|
||||||
|
|
|
|
||||||
12 | let srv = test::start(|| App::new().service(index));
|
12 | let srv = actix_test::start(|| App::new().service(index));
|
||||||
| ^^^^^ not found in this scope
|
| ^^^^^ not found in this scope
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use actix_web::{Responder, HttpResponse, App, test};
|
use actix_web::{Responder, HttpResponse, App};
|
||||||
use actix_web_codegen::*;
|
use actix_web_codegen::*;
|
||||||
|
|
||||||
#[get("/config")]
|
#[get("/config")]
|
||||||
@ -8,7 +8,7 @@ async fn config() -> impl Responder {
|
|||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
async fn main() {
|
async fn main() {
|
||||||
let srv = test::start(|| App::new().service(config));
|
let srv = actix_test::start(|| App::new().service(config));
|
||||||
|
|
||||||
let request = srv.get("/config");
|
let request = srv.get("/config");
|
||||||
let response = request.send().await.unwrap();
|
let response = request.send().await.unwrap();
|
||||||
|
@ -72,6 +72,7 @@ actix-http-test = { version = "3.0.0-beta.3", features = ["openssl"] }
|
|||||||
actix-utils = "3.0.0-beta.4"
|
actix-utils = "3.0.0-beta.4"
|
||||||
actix-server = "2.0.0-beta.3"
|
actix-server = "2.0.0-beta.3"
|
||||||
actix-tls = { version = "3.0.0-beta.5", features = ["openssl", "rustls"] }
|
actix-tls = { version = "3.0.0-beta.5", features = ["openssl", "rustls"] }
|
||||||
|
actix-test = { version = "0.0.1", features = ["openssl", "rustls"] }
|
||||||
|
|
||||||
brotli2 = "0.3.2"
|
brotli2 = "0.3.2"
|
||||||
env_logger = "0.8"
|
env_logger = "0.8"
|
||||||
@ -79,3 +80,7 @@ flate2 = "1.0.13"
|
|||||||
futures-util = { version = "0.3.7", default-features = false }
|
futures-util = { version = "0.3.7", default-features = false }
|
||||||
rcgen = "0.8"
|
rcgen = "0.8"
|
||||||
webpki = "0.21"
|
webpki = "0.21"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "client"
|
||||||
|
required-features = ["rustls"]
|
||||||
|
@ -283,10 +283,9 @@ fn rebuild_uri(res: &ClientResponse, org_uri: Uri) -> Result<Uri, SendRequestErr
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use actix_web::{test::start, web, App, Error, HttpResponse};
|
use actix_web::{web, App, Error, HttpResponse};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use crate::ClientBuilder;
|
use crate::ClientBuilder;
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
@ -296,7 +295,7 @@ mod tests {
|
|||||||
.wrap(Redirect::new().max_redirect_times(10))
|
.wrap(Redirect::new().max_redirect_times(10))
|
||||||
.finish();
|
.finish();
|
||||||
|
|
||||||
let srv = start(|| {
|
let srv = actix_test::start(|| {
|
||||||
App::new()
|
App::new()
|
||||||
.service(web::resource("/test").route(web::to(|| async {
|
.service(web::resource("/test").route(web::to(|| async {
|
||||||
Ok::<_, Error>(HttpResponse::BadRequest())
|
Ok::<_, Error>(HttpResponse::BadRequest())
|
||||||
@ -323,7 +322,7 @@ mod tests {
|
|||||||
.connector(crate::Connector::new())
|
.connector(crate::Connector::new())
|
||||||
.finish();
|
.finish();
|
||||||
|
|
||||||
let srv = start(|| {
|
let srv = actix_test::start(|| {
|
||||||
App::new()
|
App::new()
|
||||||
.service(web::resource("/").route(web::to(|| async {
|
.service(web::resource("/").route(web::to(|| async {
|
||||||
Ok::<_, Error>(
|
Ok::<_, Error>(
|
||||||
|
@ -24,7 +24,7 @@ use actix_web::{
|
|||||||
dev::{AppConfig, BodyEncoding},
|
dev::{AppConfig, BodyEncoding},
|
||||||
http::{header, Cookie},
|
http::{header, Cookie},
|
||||||
middleware::Compress,
|
middleware::Compress,
|
||||||
test, web, App, Error, HttpMessage, HttpRequest, HttpResponse,
|
web, App, Error, HttpMessage, HttpRequest, HttpResponse,
|
||||||
};
|
};
|
||||||
use awc::error::{JsonPayloadError, PayloadError, SendRequestError};
|
use awc::error::{JsonPayloadError, PayloadError, SendRequestError};
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_simple() {
|
async fn test_simple() {
|
||||||
let srv = test::start(|| {
|
let srv = actix_test::start(|| {
|
||||||
App::new().service(web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))))
|
App::new().service(web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))))
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -78,7 +78,7 @@ async fn test_simple() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_json() {
|
async fn test_json() {
|
||||||
let srv = test::start(|| {
|
let srv = actix_test::start(|| {
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::resource("/").route(web::to(|_: web::Json<String>| HttpResponse::Ok())),
|
web::resource("/").route(web::to(|_: web::Json<String>| HttpResponse::Ok())),
|
||||||
)
|
)
|
||||||
@ -94,7 +94,7 @@ async fn test_json() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_form() {
|
async fn test_form() {
|
||||||
let srv = test::start(|| {
|
let srv = actix_test::start(|| {
|
||||||
App::new().service(web::resource("/").route(web::to(
|
App::new().service(web::resource("/").route(web::to(
|
||||||
|_: web::Form<HashMap<String, String>>| HttpResponse::Ok(),
|
|_: web::Form<HashMap<String, String>>| HttpResponse::Ok(),
|
||||||
)))
|
)))
|
||||||
@ -113,7 +113,7 @@ async fn test_form() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_timeout() {
|
async fn test_timeout() {
|
||||||
let srv = test::start(|| {
|
let srv = actix_test::start(|| {
|
||||||
App::new().service(web::resource("/").route(web::to(|| async {
|
App::new().service(web::resource("/").route(web::to(|| async {
|
||||||
actix_rt::time::sleep(Duration::from_millis(200)).await;
|
actix_rt::time::sleep(Duration::from_millis(200)).await;
|
||||||
Ok::<_, Error>(HttpResponse::Ok().body(STR))
|
Ok::<_, Error>(HttpResponse::Ok().body(STR))
|
||||||
@ -138,7 +138,7 @@ async fn test_timeout() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_timeout_override() {
|
async fn test_timeout_override() {
|
||||||
let srv = test::start(|| {
|
let srv = actix_test::start(|| {
|
||||||
App::new().service(web::resource("/").route(web::to(|| async {
|
App::new().service(web::resource("/").route(web::to(|| async {
|
||||||
actix_rt::time::sleep(Duration::from_millis(200)).await;
|
actix_rt::time::sleep(Duration::from_millis(200)).await;
|
||||||
Ok::<_, Error>(HttpResponse::Ok().body(STR))
|
Ok::<_, Error>(HttpResponse::Ok().body(STR))
|
||||||
@ -162,7 +162,7 @@ async fn test_timeout_override() {
|
|||||||
async fn test_response_timeout() {
|
async fn test_response_timeout() {
|
||||||
use futures_util::stream::{once, StreamExt as _};
|
use futures_util::stream::{once, StreamExt as _};
|
||||||
|
|
||||||
let srv = test::start(|| {
|
let srv = actix_test::start(|| {
|
||||||
App::new().service(web::resource("/").route(web::to(|| async {
|
App::new().service(web::resource("/").route(web::to(|| async {
|
||||||
Ok::<_, Error>(
|
Ok::<_, Error>(
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
@ -444,7 +444,7 @@ async fn test_connection_wait_queue_force_close() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_with_query_parameter() {
|
async fn test_with_query_parameter() {
|
||||||
let srv = test::start(|| {
|
let srv = actix_test::start(|| {
|
||||||
App::new().service(web::resource("/").to(|req: HttpRequest| {
|
App::new().service(web::resource("/").to(|req: HttpRequest| {
|
||||||
if req.query_string().contains("qp") {
|
if req.query_string().contains("qp") {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
@ -464,7 +464,7 @@ async fn test_with_query_parameter() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_no_decompress() {
|
async fn test_no_decompress() {
|
||||||
let srv = test::start(|| {
|
let srv = actix_test::start(|| {
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(Compress::default())
|
.wrap(Compress::default())
|
||||||
.service(web::resource("/").route(web::to(|| {
|
.service(web::resource("/").route(web::to(|| {
|
||||||
@ -508,7 +508,7 @@ async fn test_no_decompress() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_client_gzip_encoding() {
|
async fn test_client_gzip_encoding() {
|
||||||
let srv = test::start(|| {
|
let srv = actix_test::start(|| {
|
||||||
App::new().service(web::resource("/").route(web::to(|| {
|
App::new().service(web::resource("/").route(web::to(|| {
|
||||||
let mut e = GzEncoder::new(Vec::new(), Compression::default());
|
let mut e = GzEncoder::new(Vec::new(), Compression::default());
|
||||||
e.write_all(STR.as_ref()).unwrap();
|
e.write_all(STR.as_ref()).unwrap();
|
||||||
@ -531,7 +531,7 @@ async fn test_client_gzip_encoding() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_client_gzip_encoding_large() {
|
async fn test_client_gzip_encoding_large() {
|
||||||
let srv = test::start(|| {
|
let srv = actix_test::start(|| {
|
||||||
App::new().service(web::resource("/").route(web::to(|| {
|
App::new().service(web::resource("/").route(web::to(|| {
|
||||||
let mut e = GzEncoder::new(Vec::new(), Compression::default());
|
let mut e = GzEncoder::new(Vec::new(), Compression::default());
|
||||||
e.write_all(STR.repeat(10).as_ref()).unwrap();
|
e.write_all(STR.repeat(10).as_ref()).unwrap();
|
||||||
@ -560,7 +560,7 @@ async fn test_client_gzip_encoding_large_random() {
|
|||||||
.map(char::from)
|
.map(char::from)
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
|
|
||||||
let srv = test::start(|| {
|
let srv = actix_test::start(|| {
|
||||||
App::new().service(web::resource("/").route(web::to(|data: Bytes| {
|
App::new().service(web::resource("/").route(web::to(|data: Bytes| {
|
||||||
let mut e = GzEncoder::new(Vec::new(), Compression::default());
|
let mut e = GzEncoder::new(Vec::new(), Compression::default());
|
||||||
e.write_all(&data).unwrap();
|
e.write_all(&data).unwrap();
|
||||||
@ -582,7 +582,7 @@ async fn test_client_gzip_encoding_large_random() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_client_brotli_encoding() {
|
async fn test_client_brotli_encoding() {
|
||||||
let srv = test::start(|| {
|
let srv = actix_test::start(|| {
|
||||||
App::new().service(web::resource("/").route(web::to(|data: Bytes| {
|
App::new().service(web::resource("/").route(web::to(|data: Bytes| {
|
||||||
let mut e = BrotliEncoder::new(Vec::new(), 5);
|
let mut e = BrotliEncoder::new(Vec::new(), 5);
|
||||||
e.write_all(&data).unwrap();
|
e.write_all(&data).unwrap();
|
||||||
@ -610,7 +610,7 @@ async fn test_client_brotli_encoding_large_random() {
|
|||||||
.map(char::from)
|
.map(char::from)
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
|
|
||||||
let srv = test::start(|| {
|
let srv = actix_test::start(|| {
|
||||||
App::new().service(web::resource("/").route(web::to(|data: Bytes| {
|
App::new().service(web::resource("/").route(web::to(|data: Bytes| {
|
||||||
let mut e = BrotliEncoder::new(Vec::new(), 5);
|
let mut e = BrotliEncoder::new(Vec::new(), 5);
|
||||||
e.write_all(&data).unwrap();
|
e.write_all(&data).unwrap();
|
||||||
@ -633,7 +633,7 @@ async fn test_client_brotli_encoding_large_random() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_client_deflate_encoding() {
|
async fn test_client_deflate_encoding() {
|
||||||
let srv = test::start(|| {
|
let srv = actix_test::start(|| {
|
||||||
App::new().default_service(web::to(|body: Bytes| {
|
App::new().default_service(web::to(|body: Bytes| {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.encoding(http::ContentEncoding::Br)
|
.encoding(http::ContentEncoding::Br)
|
||||||
@ -658,7 +658,7 @@ async fn test_client_deflate_encoding_large_random() {
|
|||||||
.take(70_000)
|
.take(70_000)
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
|
|
||||||
let srv = test::start(|| {
|
let srv = actix_test::start(|| {
|
||||||
App::new().default_service(web::to(|body: Bytes| {
|
App::new().default_service(web::to(|body: Bytes| {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.encoding(http::ContentEncoding::Br)
|
.encoding(http::ContentEncoding::Br)
|
||||||
@ -677,7 +677,7 @@ async fn test_client_deflate_encoding_large_random() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_client_streaming_explicit() {
|
async fn test_client_streaming_explicit() {
|
||||||
let srv = test::start(|| {
|
let srv = actix_test::start(|| {
|
||||||
App::new().default_service(web::to(|body: web::Payload| {
|
App::new().default_service(web::to(|body: web::Payload| {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.encoding(http::ContentEncoding::Identity)
|
.encoding(http::ContentEncoding::Identity)
|
||||||
@ -698,7 +698,7 @@ async fn test_client_streaming_explicit() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_body_streaming_implicit() {
|
async fn test_body_streaming_implicit() {
|
||||||
let srv = test::start(|| {
|
let srv = actix_test::start(|| {
|
||||||
App::new().default_service(web::to(|| {
|
App::new().default_service(web::to(|| {
|
||||||
let body = stream::once(async {
|
let body = stream::once(async {
|
||||||
Ok::<_, actix_http::Error>(Bytes::from_static(STR.as_bytes()))
|
Ok::<_, actix_http::Error>(Bytes::from_static(STR.as_bytes()))
|
||||||
@ -734,7 +734,7 @@ async fn test_client_cookie_handling() {
|
|||||||
let cookie1b = cookie1.clone();
|
let cookie1b = cookie1.clone();
|
||||||
let cookie2b = cookie2.clone();
|
let cookie2b = cookie2.clone();
|
||||||
|
|
||||||
let srv = test::start(move || {
|
let srv = actix_test::start(move || {
|
||||||
let cookie1 = cookie1b.clone();
|
let cookie1 = cookie1b.clone();
|
||||||
let cookie2 = cookie2b.clone();
|
let cookie2 = cookie2b.clone();
|
||||||
|
|
||||||
@ -790,8 +790,7 @@ async fn test_client_cookie_handling() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn client_unread_response() {
|
async fn client_unread_response() {
|
||||||
let addr = test::unused_addr();
|
let addr = actix_test::unused_addr();
|
||||||
|
|
||||||
let lst = std::net::TcpListener::bind(addr).unwrap();
|
let lst = std::net::TcpListener::bind(addr).unwrap();
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
@ -820,7 +819,7 @@ async fn client_unread_response() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn client_basic_auth() {
|
async fn client_basic_auth() {
|
||||||
let srv = test::start(|| {
|
let srv = actix_test::start(|| {
|
||||||
App::new().route(
|
App::new().route(
|
||||||
"/",
|
"/",
|
||||||
web::to(|req: HttpRequest| {
|
web::to(|req: HttpRequest| {
|
||||||
@ -848,7 +847,7 @@ async fn client_basic_auth() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn client_bearer_auth() {
|
async fn client_bearer_auth() {
|
||||||
let srv = test::start(|| {
|
let srv = actix_test::start(|| {
|
||||||
App::new().route(
|
App::new().route(
|
||||||
"/",
|
"/",
|
||||||
web::to(|req: HttpRequest| {
|
web::to(|req: HttpRequest| {
|
||||||
@ -878,7 +877,7 @@ async fn client_bearer_auth() {
|
|||||||
async fn test_local_address() {
|
async fn test_local_address() {
|
||||||
let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
|
let ip = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
|
||||||
|
|
||||||
let srv = test::start(move || {
|
let srv = actix_test::start(move || {
|
||||||
App::new().service(web::resource("/").route(web::to(
|
App::new().service(web::resource("/").route(web::to(
|
||||||
move |req: HttpRequest| async move {
|
move |req: HttpRequest| async move {
|
||||||
assert_eq!(req.peer_addr().unwrap().ip(), ip);
|
assert_eq!(req.peer_addr().unwrap().ip(), ip);
|
||||||
|
@ -32,7 +32,7 @@ fn bench_async_burst(c: &mut Criterion) {
|
|||||||
let rt = actix_rt::System::new();
|
let rt = actix_rt::System::new();
|
||||||
|
|
||||||
let srv = rt.block_on(async {
|
let srv = rt.block_on(async {
|
||||||
test::start(|| {
|
actix_test::start(|| {
|
||||||
App::new()
|
App::new()
|
||||||
.service(web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))))
|
.service(web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))))
|
||||||
})
|
})
|
||||||
|
@ -11,6 +11,5 @@ coverage:
|
|||||||
|
|
||||||
ignore: # ignore code coverage on following paths
|
ignore: # ignore code coverage on following paths
|
||||||
- "**/tests"
|
- "**/tests"
|
||||||
- "test-server"
|
|
||||||
- "**/benches"
|
- "**/benches"
|
||||||
- "**/examples"
|
- "**/examples"
|
||||||
|
@ -103,8 +103,8 @@ impl AppService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Application connection config
|
/// Application connection config.
|
||||||
#[derive(Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AppConfig {
|
pub struct AppConfig {
|
||||||
secure: bool,
|
secure: bool,
|
||||||
host: String,
|
host: String,
|
||||||
@ -112,10 +112,15 @@ pub struct AppConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl AppConfig {
|
impl AppConfig {
|
||||||
pub(crate) fn new(secure: bool, addr: SocketAddr, host: String) -> Self {
|
pub(crate) fn new(secure: bool, host: String, addr: SocketAddr) -> Self {
|
||||||
AppConfig { secure, host, addr }
|
AppConfig { secure, host, addr }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn __priv_test_new(secure: bool, host: String, addr: SocketAddr) -> Self {
|
||||||
|
AppConfig::new(secure, host, addr)
|
||||||
|
}
|
||||||
|
|
||||||
/// Server host name.
|
/// Server host name.
|
||||||
///
|
///
|
||||||
/// Host name is used by application router as a hostname for url generation.
|
/// Host name is used by application router as a hostname for url generation.
|
||||||
@ -142,8 +147,8 @@ impl Default for AppConfig {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
AppConfig::new(
|
AppConfig::new(
|
||||||
false,
|
false,
|
||||||
"127.0.0.1:8080".parse().unwrap(),
|
|
||||||
"localhost:8080".to_owned(),
|
"localhost:8080".to_owned(),
|
||||||
|
"127.0.0.1:8080".parse().unwrap(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
55
src/data.rs
55
src/data.rs
@ -148,13 +148,13 @@ impl<T: ?Sized + 'static> DataFactory for Data<T> {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::dev::Service;
|
use crate::{
|
||||||
use crate::http::StatusCode;
|
dev::Service,
|
||||||
use crate::test::{self, init_service, TestRequest};
|
http::StatusCode,
|
||||||
use crate::{web, App, HttpResponse};
|
test::{init_service, TestRequest},
|
||||||
|
web, App, HttpResponse,
|
||||||
|
};
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_data_extractor() {
|
async fn test_data_extractor() {
|
||||||
@ -270,49 +270,6 @@ mod tests {
|
|||||||
assert_eq!(resp.status(), StatusCode::OK);
|
assert_eq!(resp.status(), StatusCode::OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
|
||||||
async fn test_data_drop() {
|
|
||||||
struct TestData(Arc<AtomicUsize>);
|
|
||||||
|
|
||||||
impl TestData {
|
|
||||||
fn new(inner: Arc<AtomicUsize>) -> Self {
|
|
||||||
let _ = inner.fetch_add(1, Ordering::SeqCst);
|
|
||||||
Self(inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for TestData {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
let inner = self.0.clone();
|
|
||||||
let _ = inner.fetch_add(1, Ordering::SeqCst);
|
|
||||||
Self(inner)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for TestData {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
let _ = self.0.fetch_sub(1, Ordering::SeqCst);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let num = Arc::new(AtomicUsize::new(0));
|
|
||||||
let data = TestData::new(num.clone());
|
|
||||||
assert_eq!(num.load(Ordering::SeqCst), 1);
|
|
||||||
|
|
||||||
let srv = test::start(move || {
|
|
||||||
let data = data.clone();
|
|
||||||
|
|
||||||
App::new()
|
|
||||||
.data(data)
|
|
||||||
.service(web::resource("/").to(|_data: Data<TestData>| async { "ok" }))
|
|
||||||
});
|
|
||||||
|
|
||||||
assert!(srv.get("/").send().await.unwrap().status().is_success());
|
|
||||||
srv.stop().await;
|
|
||||||
|
|
||||||
assert_eq!(num.load(Ordering::SeqCst), 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_data_from_arc() {
|
async fn test_data_from_arc() {
|
||||||
let data_new = Data::new(String::from("test-123"));
|
let data_new = Data::new(String::from("test-123"));
|
||||||
|
@ -71,11 +71,6 @@
|
|||||||
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
#![doc(html_logo_url = "https://actix.rs/img/logo.png")]
|
||||||
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
#![doc(html_favicon_url = "https://actix.rs/favicon.ico")]
|
||||||
|
|
||||||
#[cfg(feature = "openssl")]
|
|
||||||
extern crate tls_openssl as openssl;
|
|
||||||
#[cfg(feature = "rustls")]
|
|
||||||
extern crate tls_rustls as rustls;
|
|
||||||
|
|
||||||
mod app;
|
mod app;
|
||||||
mod app_service;
|
mod app_service;
|
||||||
mod config;
|
mod config;
|
||||||
|
@ -137,7 +137,7 @@ mod tests {
|
|||||||
use crate::{web, App, HttpResponse};
|
use crate::{web, App, HttpResponse};
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
#[cfg(feature = "cookies")]
|
#[cfg(all(feature = "cookies", feature = "compress"))]
|
||||||
async fn test_scope_middleware() {
|
async fn test_scope_middleware() {
|
||||||
use crate::middleware::Compress;
|
use crate::middleware::Compress;
|
||||||
|
|
||||||
@ -160,7 +160,7 @@ mod tests {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
#[cfg(feature = "cookies")]
|
#[cfg(all(feature = "cookies", feature = "compress"))]
|
||||||
async fn test_resource_scope_middleware() {
|
async fn test_resource_scope_middleware() {
|
||||||
use crate::middleware::Compress;
|
use crate::middleware::Compress;
|
||||||
|
|
||||||
|
@ -52,6 +52,18 @@ impl HttpRequest {
|
|||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn __priv_test_new(
|
||||||
|
path: Path<Url>,
|
||||||
|
head: Message<RequestHead>,
|
||||||
|
rmap: Rc<ResourceMap>,
|
||||||
|
config: AppConfig,
|
||||||
|
app_data: Rc<Extensions>,
|
||||||
|
) -> HttpRequest {
|
||||||
|
let app_state = AppInitServiceState::new(rmap, config);
|
||||||
|
Self::new(path, head, app_state, app_data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HttpRequest {
|
impl HttpRequest {
|
||||||
|
@ -71,12 +71,15 @@ impl<F, I, S, B> HttpServer<F, I, S, B>
|
|||||||
where
|
where
|
||||||
F: Fn() -> I + Send + Clone + 'static,
|
F: Fn() -> I + Send + Clone + 'static,
|
||||||
I: IntoServiceFactory<S, Request>,
|
I: IntoServiceFactory<S, Request>,
|
||||||
|
|
||||||
S: ServiceFactory<Request, Config = AppConfig> + 'static,
|
S: ServiceFactory<Request, Config = AppConfig> + 'static,
|
||||||
|
// S::Future: 'static,
|
||||||
S::Error: Into<Error> + 'static,
|
S::Error: Into<Error> + 'static,
|
||||||
S::InitError: fmt::Debug,
|
S::InitError: fmt::Debug,
|
||||||
S::Response: Into<Response<B>> + 'static,
|
S::Response: Into<Response<B>> + 'static,
|
||||||
<S::Service as Service<Request>>::Future: 'static,
|
<S::Service as Service<Request>>::Future: 'static,
|
||||||
S::Service: 'static,
|
S::Service: 'static,
|
||||||
|
// S::Service: 'static,
|
||||||
B: MessageBody + 'static,
|
B: MessageBody + 'static,
|
||||||
{
|
{
|
||||||
/// Create new HTTP server with application factory
|
/// Create new HTTP server with application factory
|
||||||
@ -288,7 +291,7 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
svc.finish(map_config(factory(), move |_| {
|
svc.finish(map_config(factory(), move |_| {
|
||||||
AppConfig::new(false, addr, host.clone())
|
AppConfig::new(false, host.clone(), addr)
|
||||||
}))
|
}))
|
||||||
.tcp()
|
.tcp()
|
||||||
})?;
|
})?;
|
||||||
@ -343,10 +346,11 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
svc.finish(map_config(factory(), move |_| {
|
svc.finish(map_config(factory(), move |_| {
|
||||||
AppConfig::new(true, addr, host.clone())
|
AppConfig::new(true, host.clone(), addr)
|
||||||
}))
|
}))
|
||||||
.openssl(acceptor.clone())
|
.openssl(acceptor.clone())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,10 +400,11 @@ where
|
|||||||
};
|
};
|
||||||
|
|
||||||
svc.finish(map_config(factory(), move |_| {
|
svc.finish(map_config(factory(), move |_| {
|
||||||
AppConfig::new(true, addr, host.clone())
|
AppConfig::new(true, host.clone(), addr)
|
||||||
}))
|
}))
|
||||||
.rustls(config.clone())
|
.rustls(config.clone())
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
Ok(self)
|
Ok(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -502,8 +507,8 @@ where
|
|||||||
let c = cfg.lock().unwrap();
|
let c = cfg.lock().unwrap();
|
||||||
let config = AppConfig::new(
|
let config = AppConfig::new(
|
||||||
false,
|
false,
|
||||||
socket_addr,
|
|
||||||
c.host.clone().unwrap_or_else(|| format!("{}", socket_addr)),
|
c.host.clone().unwrap_or_else(|| format!("{}", socket_addr)),
|
||||||
|
socket_addr,
|
||||||
);
|
);
|
||||||
|
|
||||||
pipeline_factory(|io: UnixStream| async { Ok((io, Protocol::Http1, None)) })
|
pipeline_factory(|io: UnixStream| async { Ok((io, Protocol::Http1, None)) })
|
||||||
@ -552,8 +557,8 @@ where
|
|||||||
let c = cfg.lock().unwrap();
|
let c = cfg.lock().unwrap();
|
||||||
let config = AppConfig::new(
|
let config = AppConfig::new(
|
||||||
false,
|
false,
|
||||||
socket_addr,
|
|
||||||
c.host.clone().unwrap_or_else(|| format!("{}", socket_addr)),
|
c.host.clone().unwrap_or_else(|| format!("{}", socket_addr)),
|
||||||
|
socket_addr,
|
||||||
);
|
);
|
||||||
pipeline_factory(|io: UnixStream| async { Ok((io, Protocol::Http1, None)) })
|
pipeline_factory(|io: UnixStream| async { Ok((io, Protocol::Http1, None)) })
|
||||||
.and_then(
|
.and_then(
|
||||||
|
@ -69,6 +69,12 @@ impl ServiceRequest {
|
|||||||
Self { req, payload }
|
Self { req, payload }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Construct service request.
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub fn __priv_test_new(req: HttpRequest, payload: Payload) -> Self {
|
||||||
|
Self::new(req, payload)
|
||||||
|
}
|
||||||
|
|
||||||
/// Deconstruct request into parts
|
/// Deconstruct request into parts
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn into_parts(self) -> (HttpRequest, Payload) {
|
pub fn into_parts(self) -> (HttpRequest, Payload) {
|
||||||
|
481
src/test.rs
481
src/test.rs
@ -1,47 +1,41 @@
|
|||||||
//! Various helpers for Actix applications to use during testing.
|
//! Various helpers for Actix applications to use during testing.
|
||||||
|
|
||||||
use std::net::SocketAddr;
|
use std::{net::SocketAddr, rc::Rc};
|
||||||
use std::rc::Rc;
|
|
||||||
use std::sync::mpsc;
|
|
||||||
use std::{fmt, net, thread, time};
|
|
||||||
|
|
||||||
use actix_codec::{AsyncRead, AsyncWrite, Framed};
|
|
||||||
#[cfg(feature = "cookies")]
|
#[cfg(feature = "cookies")]
|
||||||
use actix_http::cookie::Cookie;
|
use actix_http::cookie::Cookie;
|
||||||
use actix_http::http::{HeaderMap, Method, StatusCode, Uri, Version};
|
pub use actix_http::test::TestBuffer;
|
||||||
use actix_http::test::TestRequest as HttpTestRequest;
|
use actix_http::{
|
||||||
use actix_http::{ws, Extensions, HttpService, Request};
|
http::{header::IntoHeaderPair, Method, StatusCode, Uri, Version},
|
||||||
|
test::TestRequest as HttpTestRequest,
|
||||||
|
Extensions, Request,
|
||||||
|
};
|
||||||
use actix_router::{Path, ResourceDef, Url};
|
use actix_router::{Path, ResourceDef, Url};
|
||||||
use actix_rt::{time::sleep, System};
|
use actix_service::{IntoService, IntoServiceFactory, Service, ServiceFactory};
|
||||||
use actix_service::{map_config, IntoService, IntoServiceFactory, Service, ServiceFactory};
|
|
||||||
use actix_utils::future::ok;
|
use actix_utils::future::ok;
|
||||||
use awc::error::PayloadError;
|
|
||||||
use awc::{Client, ClientRequest, ClientResponse, Connector};
|
|
||||||
use bytes::{Bytes, BytesMut};
|
|
||||||
use futures_core::Stream;
|
use futures_core::Stream;
|
||||||
use futures_util::StreamExt as _;
|
use futures_util::StreamExt as _;
|
||||||
use serde::de::DeserializeOwned;
|
use serde::{de::DeserializeOwned, Serialize};
|
||||||
use serde::Serialize;
|
|
||||||
use socket2::{Domain, Protocol, Socket, Type};
|
|
||||||
|
|
||||||
pub use actix_http::test::TestBuffer;
|
use crate::{
|
||||||
|
app_service::AppInitServiceState,
|
||||||
|
config::AppConfig,
|
||||||
|
data::Data,
|
||||||
|
dev::{Body, MessageBody, Payload},
|
||||||
|
http::header::ContentType,
|
||||||
|
rmap::ResourceMap,
|
||||||
|
service::{ServiceRequest, ServiceResponse},
|
||||||
|
web::{Bytes, BytesMut},
|
||||||
|
Error, HttpRequest, HttpResponse,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::app_service::AppInitServiceState;
|
/// Create service that always responds with `HttpResponse::Ok()` and no body.
|
||||||
use crate::config::AppConfig;
|
|
||||||
use crate::data::Data;
|
|
||||||
use crate::dev::{Body, MessageBody, Payload, Server};
|
|
||||||
use crate::http::header::{ContentType, IntoHeaderPair};
|
|
||||||
use crate::rmap::ResourceMap;
|
|
||||||
use crate::service::{ServiceRequest, ServiceResponse};
|
|
||||||
use crate::{Error, HttpRequest, HttpResponse};
|
|
||||||
|
|
||||||
/// Create service that always responds with `HttpResponse::Ok()`
|
|
||||||
pub fn ok_service(
|
pub fn ok_service(
|
||||||
) -> impl Service<ServiceRequest, Response = ServiceResponse<Body>, Error = Error> {
|
) -> impl Service<ServiceRequest, Response = ServiceResponse<Body>, Error = Error> {
|
||||||
default_service(StatusCode::OK)
|
default_service(StatusCode::OK)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create service that responds with response with specified status code
|
/// Create service that always responds with given status code and no body.
|
||||||
pub fn default_service(
|
pub fn default_service(
|
||||||
status_code: StatusCode,
|
status_code: StatusCode,
|
||||||
) -> impl Service<ServiceRequest, Response = ServiceResponse<Body>, Error = Error> {
|
) -> impl Service<ServiceRequest, Response = ServiceResponse<Body>, Error = Error> {
|
||||||
@ -51,8 +45,7 @@ pub fn default_service(
|
|||||||
.into_service()
|
.into_service()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This method accepts application builder instance, and constructs
|
/// Initialize service from application builder instance.
|
||||||
/// service.
|
|
||||||
///
|
///
|
||||||
/// ```
|
/// ```
|
||||||
/// use actix_service::Service;
|
/// use actix_service::Service;
|
||||||
@ -83,10 +76,10 @@ where
|
|||||||
{
|
{
|
||||||
try_init_service(app)
|
try_init_service(app)
|
||||||
.await
|
.await
|
||||||
.expect("service initilization failed")
|
.expect("service initialization failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Fallible version of init_service that allows testing data factory errors.
|
/// Fallible version of [`init_service`] that allows testing initialization errors.
|
||||||
pub(crate) async fn try_init_service<R, S, B, E>(
|
pub(crate) async fn try_init_service<R, S, B, E>(
|
||||||
app: R,
|
app: R,
|
||||||
) -> Result<impl Service<Request, Response = ServiceResponse<B>, Error = E>, S::InitError>
|
) -> Result<impl Service<Request, Response = ServiceResponse<B>, Error = E>, S::InitError>
|
||||||
@ -166,9 +159,11 @@ where
|
|||||||
|
|
||||||
let mut body = resp.take_body();
|
let mut body = resp.take_body();
|
||||||
let mut bytes = BytesMut::new();
|
let mut bytes = BytesMut::new();
|
||||||
|
|
||||||
while let Some(item) = body.next().await {
|
while let Some(item) = body.next().await {
|
||||||
bytes.extend_from_slice(&item.unwrap());
|
bytes.extend_from_slice(&item.unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes.freeze()
|
bytes.freeze()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -573,430 +568,12 @@ impl TestRequest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Start test server with default configuration
|
|
||||||
///
|
|
||||||
/// Test server is very simple server that simplify process of writing
|
|
||||||
/// integration tests cases for actix web applications.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use actix_web::{web, test, App, HttpResponse, Error};
|
|
||||||
///
|
|
||||||
/// async fn my_handler() -> Result<HttpResponse, Error> {
|
|
||||||
/// Ok(HttpResponse::Ok().into())
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// #[actix_rt::test]
|
|
||||||
/// async fn test_example() {
|
|
||||||
/// let srv = test::start(
|
|
||||||
/// || App::new().service(
|
|
||||||
/// web::resource("/").to(my_handler))
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// let req = srv.get("/");
|
|
||||||
/// let response = req.send().await.unwrap();
|
|
||||||
/// assert!(response.status().is_success());
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub fn start<F, I, S, B>(factory: F) -> TestServer
|
|
||||||
where
|
|
||||||
F: Fn() -> I + Send + Clone + 'static,
|
|
||||||
I: IntoServiceFactory<S, Request>,
|
|
||||||
S: ServiceFactory<Request, Config = AppConfig> + 'static,
|
|
||||||
S::Error: Into<Error> + 'static,
|
|
||||||
S::InitError: fmt::Debug,
|
|
||||||
S::Response: Into<HttpResponse<B>> + 'static,
|
|
||||||
<S::Service as Service<Request>>::Future: 'static,
|
|
||||||
B: MessageBody + 'static,
|
|
||||||
{
|
|
||||||
start_with(TestServerConfig::default(), factory)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Start test server with custom configuration
|
|
||||||
///
|
|
||||||
/// Test server could be configured in different ways, for details check
|
|
||||||
/// `TestServerConfig` docs.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use actix_web::{web, test, App, HttpResponse, Error};
|
|
||||||
///
|
|
||||||
/// async fn my_handler() -> Result<HttpResponse, Error> {
|
|
||||||
/// Ok(HttpResponse::Ok().into())
|
|
||||||
/// }
|
|
||||||
///
|
|
||||||
/// #[actix_rt::test]
|
|
||||||
/// async fn test_example() {
|
|
||||||
/// let srv = test::start_with(test::config().h1(), ||
|
|
||||||
/// App::new().service(web::resource("/").to(my_handler))
|
|
||||||
/// );
|
|
||||||
///
|
|
||||||
/// let req = srv.get("/");
|
|
||||||
/// let response = req.send().await.unwrap();
|
|
||||||
/// assert!(response.status().is_success());
|
|
||||||
/// }
|
|
||||||
/// ```
|
|
||||||
pub fn start_with<F, I, S, B>(cfg: TestServerConfig, factory: F) -> TestServer
|
|
||||||
where
|
|
||||||
F: Fn() -> I + Send + Clone + 'static,
|
|
||||||
I: IntoServiceFactory<S, Request>,
|
|
||||||
S: ServiceFactory<Request, Config = AppConfig> + 'static,
|
|
||||||
S::Error: Into<Error> + 'static,
|
|
||||||
S::InitError: fmt::Debug,
|
|
||||||
S::Response: Into<HttpResponse<B>> + 'static,
|
|
||||||
<S::Service as Service<Request>>::Future: 'static,
|
|
||||||
B: MessageBody + 'static,
|
|
||||||
{
|
|
||||||
let (tx, rx) = mpsc::channel();
|
|
||||||
|
|
||||||
let ssl = match cfg.stream {
|
|
||||||
StreamType::Tcp => false,
|
|
||||||
#[cfg(feature = "openssl")]
|
|
||||||
StreamType::Openssl(_) => true,
|
|
||||||
#[cfg(feature = "rustls")]
|
|
||||||
StreamType::Rustls(_) => true,
|
|
||||||
};
|
|
||||||
|
|
||||||
// run server in separate thread
|
|
||||||
thread::spawn(move || {
|
|
||||||
let sys = System::new();
|
|
||||||
let tcp = net::TcpListener::bind("127.0.0.1:0").unwrap();
|
|
||||||
let local_addr = tcp.local_addr().unwrap();
|
|
||||||
let factory = factory.clone();
|
|
||||||
let cfg = cfg.clone();
|
|
||||||
let ctimeout = cfg.client_timeout;
|
|
||||||
let builder = Server::build().workers(1).disable_signals();
|
|
||||||
|
|
||||||
let srv = match cfg.stream {
|
|
||||||
StreamType::Tcp => match cfg.tp {
|
|
||||||
HttpVer::Http1 => builder.listen("test", tcp, move || {
|
|
||||||
let cfg = AppConfig::new(false, local_addr, format!("{}", local_addr));
|
|
||||||
HttpService::build()
|
|
||||||
.client_timeout(ctimeout)
|
|
||||||
.h1(map_config(factory(), move |_| cfg.clone()))
|
|
||||||
.tcp()
|
|
||||||
}),
|
|
||||||
HttpVer::Http2 => builder.listen("test", tcp, move || {
|
|
||||||
let cfg = AppConfig::new(false, local_addr, format!("{}", local_addr));
|
|
||||||
HttpService::build()
|
|
||||||
.client_timeout(ctimeout)
|
|
||||||
.h2(map_config(factory(), move |_| cfg.clone()))
|
|
||||||
.tcp()
|
|
||||||
}),
|
|
||||||
HttpVer::Both => builder.listen("test", tcp, move || {
|
|
||||||
let cfg = AppConfig::new(false, local_addr, format!("{}", local_addr));
|
|
||||||
HttpService::build()
|
|
||||||
.client_timeout(ctimeout)
|
|
||||||
.finish(map_config(factory(), move |_| cfg.clone()))
|
|
||||||
.tcp()
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
#[cfg(feature = "openssl")]
|
|
||||||
StreamType::Openssl(acceptor) => match cfg.tp {
|
|
||||||
HttpVer::Http1 => builder.listen("test", tcp, move || {
|
|
||||||
let cfg = AppConfig::new(true, local_addr, format!("{}", local_addr));
|
|
||||||
HttpService::build()
|
|
||||||
.client_timeout(ctimeout)
|
|
||||||
.h1(map_config(factory(), move |_| cfg.clone()))
|
|
||||||
.openssl(acceptor.clone())
|
|
||||||
}),
|
|
||||||
HttpVer::Http2 => builder.listen("test", tcp, move || {
|
|
||||||
let cfg = AppConfig::new(true, local_addr, format!("{}", local_addr));
|
|
||||||
HttpService::build()
|
|
||||||
.client_timeout(ctimeout)
|
|
||||||
.h2(map_config(factory(), move |_| cfg.clone()))
|
|
||||||
.openssl(acceptor.clone())
|
|
||||||
}),
|
|
||||||
HttpVer::Both => builder.listen("test", tcp, move || {
|
|
||||||
let cfg = AppConfig::new(true, local_addr, format!("{}", local_addr));
|
|
||||||
HttpService::build()
|
|
||||||
.client_timeout(ctimeout)
|
|
||||||
.finish(map_config(factory(), move |_| cfg.clone()))
|
|
||||||
.openssl(acceptor.clone())
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
#[cfg(feature = "rustls")]
|
|
||||||
StreamType::Rustls(config) => match cfg.tp {
|
|
||||||
HttpVer::Http1 => builder.listen("test", tcp, move || {
|
|
||||||
let cfg = AppConfig::new(true, local_addr, format!("{}", local_addr));
|
|
||||||
HttpService::build()
|
|
||||||
.client_timeout(ctimeout)
|
|
||||||
.h1(map_config(factory(), move |_| cfg.clone()))
|
|
||||||
.rustls(config.clone())
|
|
||||||
}),
|
|
||||||
HttpVer::Http2 => builder.listen("test", tcp, move || {
|
|
||||||
let cfg = AppConfig::new(true, local_addr, format!("{}", local_addr));
|
|
||||||
HttpService::build()
|
|
||||||
.client_timeout(ctimeout)
|
|
||||||
.h2(map_config(factory(), move |_| cfg.clone()))
|
|
||||||
.rustls(config.clone())
|
|
||||||
}),
|
|
||||||
HttpVer::Both => builder.listen("test", tcp, move || {
|
|
||||||
let cfg = AppConfig::new(true, local_addr, format!("{}", local_addr));
|
|
||||||
HttpService::build()
|
|
||||||
.client_timeout(ctimeout)
|
|
||||||
.finish(map_config(factory(), move |_| cfg.clone()))
|
|
||||||
.rustls(config.clone())
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
sys.block_on(async {
|
|
||||||
let srv = srv.run();
|
|
||||||
tx.send((System::current(), srv, local_addr)).unwrap();
|
|
||||||
});
|
|
||||||
|
|
||||||
sys.run()
|
|
||||||
});
|
|
||||||
|
|
||||||
let (system, server, addr) = rx.recv().unwrap();
|
|
||||||
|
|
||||||
let client = {
|
|
||||||
let connector = {
|
|
||||||
#[cfg(feature = "openssl")]
|
|
||||||
{
|
|
||||||
use openssl::ssl::{SslConnector, SslMethod, SslVerifyMode};
|
|
||||||
|
|
||||||
let mut builder = SslConnector::builder(SslMethod::tls()).unwrap();
|
|
||||||
builder.set_verify(SslVerifyMode::NONE);
|
|
||||||
let _ = builder
|
|
||||||
.set_alpn_protos(b"\x02h2\x08http/1.1")
|
|
||||||
.map_err(|e| log::error!("Can not set alpn protocol: {:?}", e));
|
|
||||||
Connector::new()
|
|
||||||
.conn_lifetime(time::Duration::from_secs(0))
|
|
||||||
.timeout(time::Duration::from_millis(30000))
|
|
||||||
.ssl(builder.build())
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "openssl"))]
|
|
||||||
{
|
|
||||||
Connector::new()
|
|
||||||
.conn_lifetime(time::Duration::from_secs(0))
|
|
||||||
.timeout(time::Duration::from_millis(30000))
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Client::builder().connector(connector).finish()
|
|
||||||
};
|
|
||||||
|
|
||||||
TestServer {
|
|
||||||
addr,
|
|
||||||
client,
|
|
||||||
system,
|
|
||||||
ssl,
|
|
||||||
server,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct TestServerConfig {
|
|
||||||
tp: HttpVer,
|
|
||||||
stream: StreamType,
|
|
||||||
client_timeout: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
enum HttpVer {
|
|
||||||
Http1,
|
|
||||||
Http2,
|
|
||||||
Both,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
enum StreamType {
|
|
||||||
Tcp,
|
|
||||||
#[cfg(feature = "openssl")]
|
|
||||||
Openssl(openssl::ssl::SslAcceptor),
|
|
||||||
#[cfg(feature = "rustls")]
|
|
||||||
Rustls(rustls::ServerConfig),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for TestServerConfig {
|
|
||||||
fn default() -> Self {
|
|
||||||
TestServerConfig::new()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create default test server config
|
|
||||||
pub fn config() -> TestServerConfig {
|
|
||||||
TestServerConfig::new()
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TestServerConfig {
|
|
||||||
/// Create default server configuration
|
|
||||||
pub(crate) fn new() -> TestServerConfig {
|
|
||||||
TestServerConfig {
|
|
||||||
tp: HttpVer::Both,
|
|
||||||
stream: StreamType::Tcp,
|
|
||||||
client_timeout: 5000,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Start HTTP/1.1 server only
|
|
||||||
pub fn h1(mut self) -> Self {
|
|
||||||
self.tp = HttpVer::Http1;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Start HTTP/2 server only
|
|
||||||
pub fn h2(mut self) -> Self {
|
|
||||||
self.tp = HttpVer::Http2;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Start openssl server
|
|
||||||
#[cfg(feature = "openssl")]
|
|
||||||
pub fn openssl(mut self, acceptor: openssl::ssl::SslAcceptor) -> Self {
|
|
||||||
self.stream = StreamType::Openssl(acceptor);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Start rustls server
|
|
||||||
#[cfg(feature = "rustls")]
|
|
||||||
pub fn rustls(mut self, config: rustls::ServerConfig) -> Self {
|
|
||||||
self.stream = StreamType::Rustls(config);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set server client timeout in milliseconds for first request.
|
|
||||||
pub fn client_timeout(mut self, val: u64) -> Self {
|
|
||||||
self.client_timeout = val;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get first available unused address
|
|
||||||
pub fn unused_addr() -> net::SocketAddr {
|
|
||||||
let addr: net::SocketAddr = "127.0.0.1:0".parse().unwrap();
|
|
||||||
let socket = Socket::new(Domain::IPV4, Type::STREAM, Some(Protocol::TCP)).unwrap();
|
|
||||||
socket.bind(&addr.into()).unwrap();
|
|
||||||
socket.set_reuse_address(true).unwrap();
|
|
||||||
let tcp = net::TcpListener::from(socket);
|
|
||||||
tcp.local_addr().unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Test server controller
|
|
||||||
pub struct TestServer {
|
|
||||||
addr: net::SocketAddr,
|
|
||||||
client: awc::Client,
|
|
||||||
system: actix_rt::System,
|
|
||||||
ssl: bool,
|
|
||||||
server: Server,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TestServer {
|
|
||||||
/// Construct test server url
|
|
||||||
pub fn addr(&self) -> net::SocketAddr {
|
|
||||||
self.addr
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Construct test server url
|
|
||||||
pub fn url(&self, uri: &str) -> String {
|
|
||||||
let scheme = if self.ssl { "https" } else { "http" };
|
|
||||||
|
|
||||||
if uri.starts_with('/') {
|
|
||||||
format!("{}://localhost:{}{}", scheme, self.addr.port(), uri)
|
|
||||||
} else {
|
|
||||||
format!("{}://localhost:{}/{}", scheme, self.addr.port(), uri)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create `GET` request
|
|
||||||
pub fn get<S: AsRef<str>>(&self, path: S) -> ClientRequest {
|
|
||||||
self.client.get(self.url(path.as_ref()).as_str())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create `POST` request
|
|
||||||
pub fn post<S: AsRef<str>>(&self, path: S) -> ClientRequest {
|
|
||||||
self.client.post(self.url(path.as_ref()).as_str())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create `HEAD` request
|
|
||||||
pub fn head<S: AsRef<str>>(&self, path: S) -> ClientRequest {
|
|
||||||
self.client.head(self.url(path.as_ref()).as_str())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create `PUT` request
|
|
||||||
pub fn put<S: AsRef<str>>(&self, path: S) -> ClientRequest {
|
|
||||||
self.client.put(self.url(path.as_ref()).as_str())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create `PATCH` request
|
|
||||||
pub fn patch<S: AsRef<str>>(&self, path: S) -> ClientRequest {
|
|
||||||
self.client.patch(self.url(path.as_ref()).as_str())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create `DELETE` request
|
|
||||||
pub fn delete<S: AsRef<str>>(&self, path: S) -> ClientRequest {
|
|
||||||
self.client.delete(self.url(path.as_ref()).as_str())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create `OPTIONS` request
|
|
||||||
pub fn options<S: AsRef<str>>(&self, path: S) -> ClientRequest {
|
|
||||||
self.client.options(self.url(path.as_ref()).as_str())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Connect to test HTTP server
|
|
||||||
pub fn request<S: AsRef<str>>(&self, method: Method, path: S) -> ClientRequest {
|
|
||||||
self.client.request(method, path.as_ref())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn load_body<S>(
|
|
||||||
&mut self,
|
|
||||||
mut response: ClientResponse<S>,
|
|
||||||
) -> Result<Bytes, PayloadError>
|
|
||||||
where
|
|
||||||
S: Stream<Item = Result<Bytes, PayloadError>> + Unpin + 'static,
|
|
||||||
{
|
|
||||||
response.body().limit(10_485_760).await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Connect to WebSocket server at a given path.
|
|
||||||
pub async fn ws_at(
|
|
||||||
&mut self,
|
|
||||||
path: &str,
|
|
||||||
) -> Result<Framed<impl AsyncRead + AsyncWrite, ws::Codec>, awc::error::WsClientError> {
|
|
||||||
let url = self.url(path);
|
|
||||||
let connect = self.client.ws(url).connect();
|
|
||||||
connect.await.map(|(_, framed)| framed)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Connect to a WebSocket server.
|
|
||||||
pub async fn ws(
|
|
||||||
&mut self,
|
|
||||||
) -> Result<Framed<impl AsyncRead + AsyncWrite, ws::Codec>, awc::error::WsClientError> {
|
|
||||||
self.ws_at("/").await
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get default HeaderMap of Client.
|
|
||||||
///
|
|
||||||
/// Returns Some(&mut HeaderMap) when Client object is unique
|
|
||||||
/// (No other clone of client exists at the same time).
|
|
||||||
pub fn client_headers(&mut self) -> Option<&mut HeaderMap> {
|
|
||||||
self.client.headers()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gracefully stop HTTP server
|
|
||||||
pub async fn stop(self) {
|
|
||||||
self.server.stop(true).await;
|
|
||||||
self.system.stop();
|
|
||||||
sleep(time::Duration::from_millis(100)).await;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Drop for TestServer {
|
|
||||||
fn drop(&mut self) {
|
|
||||||
self.system.stop()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
use actix_http::HttpMessage;
|
use actix_http::HttpMessage;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::time::SystemTime;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{http::header, web, App, HttpResponse, Responder};
|
use crate::{http::header, web, App, HttpResponse, Responder};
|
||||||
|
@ -3,14 +3,14 @@ extern crate tls_openssl as openssl;
|
|||||||
|
|
||||||
#[cfg(any(unix, feature = "openssl"))]
|
#[cfg(any(unix, feature = "openssl"))]
|
||||||
use {
|
use {
|
||||||
actix_web::{test, web, App, HttpResponse, HttpServer},
|
actix_web::{web, App, HttpResponse, HttpServer},
|
||||||
std::{sync::mpsc, thread, time::Duration},
|
std::{sync::mpsc, thread, time::Duration},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_start() {
|
async fn test_start() {
|
||||||
let addr = test::unused_addr();
|
let addr = actix_test::unused_addr();
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
@ -93,7 +93,7 @@ fn ssl_acceptor() -> openssl::ssl::SslAcceptorBuilder {
|
|||||||
async fn test_start_ssl() {
|
async fn test_start_ssl() {
|
||||||
use actix_web::HttpRequest;
|
use actix_web::HttpRequest;
|
||||||
|
|
||||||
let addr = test::unused_addr();
|
let addr = actix_test::unused_addr();
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
|
|
||||||
thread::spawn(move || {
|
thread::spawn(move || {
|
||||||
|
@ -31,7 +31,7 @@ use rand::{distributions::Alphanumeric, Rng};
|
|||||||
|
|
||||||
use actix_web::dev::BodyEncoding;
|
use actix_web::dev::BodyEncoding;
|
||||||
use actix_web::middleware::{Compress, NormalizePath, TrailingSlash};
|
use actix_web::middleware::{Compress, NormalizePath, TrailingSlash};
|
||||||
use actix_web::{dev, test, web, App, Error, HttpResponse};
|
use actix_web::{dev, web, App, Error, HttpResponse};
|
||||||
|
|
||||||
const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
|
const STR: &str = "Hello World Hello World Hello World Hello World Hello World \
|
||||||
Hello World Hello World Hello World Hello World Hello World \
|
Hello World Hello World Hello World Hello World Hello World \
|
||||||
@ -115,7 +115,7 @@ impl futures_core::stream::Stream for TestBody {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_body() {
|
async fn test_body() {
|
||||||
let srv = test::start(|| {
|
let srv = actix_test::start(|| {
|
||||||
App::new().service(web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))))
|
App::new().service(web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))))
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -129,7 +129,7 @@ async fn test_body() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_body_gzip() {
|
async fn test_body_gzip() {
|
||||||
let srv = test::start_with(test::config().h1(), || {
|
let srv = actix_test::start_with(actix_test::config().h1(), || {
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(Compress::new(ContentEncoding::Gzip))
|
.wrap(Compress::new(ContentEncoding::Gzip))
|
||||||
.service(web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))))
|
.service(web::resource("/").route(web::to(|| HttpResponse::Ok().body(STR))))
|
||||||
@ -156,7 +156,7 @@ async fn test_body_gzip() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_body_gzip2() {
|
async fn test_body_gzip2() {
|
||||||
let srv = test::start_with(test::config().h1(), || {
|
let srv = actix_test::start_with(actix_test::config().h1(), || {
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(Compress::new(ContentEncoding::Gzip))
|
.wrap(Compress::new(ContentEncoding::Gzip))
|
||||||
.service(web::resource("/").route(web::to(|| {
|
.service(web::resource("/").route(web::to(|| {
|
||||||
@ -185,7 +185,7 @@ async fn test_body_gzip2() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_body_encoding_override() {
|
async fn test_body_encoding_override() {
|
||||||
let srv = test::start_with(test::config().h1(), || {
|
let srv = actix_test::start_with(actix_test::config().h1(), || {
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(Compress::new(ContentEncoding::Gzip))
|
.wrap(Compress::new(ContentEncoding::Gzip))
|
||||||
.service(web::resource("/").route(web::to(|| {
|
.service(web::resource("/").route(web::to(|| {
|
||||||
@ -248,7 +248,7 @@ async fn test_body_gzip_large() {
|
|||||||
let data = STR.repeat(10);
|
let data = STR.repeat(10);
|
||||||
let srv_data = data.clone();
|
let srv_data = data.clone();
|
||||||
|
|
||||||
let srv = test::start_with(test::config().h1(), move || {
|
let srv = actix_test::start_with(actix_test::config().h1(), move || {
|
||||||
let data = srv_data.clone();
|
let data = srv_data.clone();
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(Compress::new(ContentEncoding::Gzip))
|
.wrap(Compress::new(ContentEncoding::Gzip))
|
||||||
@ -286,7 +286,7 @@ async fn test_body_gzip_large_random() {
|
|||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
let srv_data = data.clone();
|
let srv_data = data.clone();
|
||||||
|
|
||||||
let srv = test::start_with(test::config().h1(), move || {
|
let srv = actix_test::start_with(actix_test::config().h1(), move || {
|
||||||
let data = srv_data.clone();
|
let data = srv_data.clone();
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(Compress::new(ContentEncoding::Gzip))
|
.wrap(Compress::new(ContentEncoding::Gzip))
|
||||||
@ -318,7 +318,7 @@ async fn test_body_gzip_large_random() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_body_chunked_implicit() {
|
async fn test_body_chunked_implicit() {
|
||||||
let srv = test::start_with(test::config().h1(), || {
|
let srv = actix_test::start_with(actix_test::config().h1(), || {
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(Compress::new(ContentEncoding::Gzip))
|
.wrap(Compress::new(ContentEncoding::Gzip))
|
||||||
.service(web::resource("/").route(web::get().to(move || {
|
.service(web::resource("/").route(web::get().to(move || {
|
||||||
@ -352,7 +352,7 @@ async fn test_body_chunked_implicit() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_body_br_streaming() {
|
async fn test_body_br_streaming() {
|
||||||
let srv = test::start_with(test::config().h1(), || {
|
let srv = actix_test::start_with(actix_test::config().h1(), || {
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(Compress::new(ContentEncoding::Br))
|
.wrap(Compress::new(ContentEncoding::Br))
|
||||||
.service(web::resource("/").route(web::to(move || {
|
.service(web::resource("/").route(web::to(move || {
|
||||||
@ -384,7 +384,7 @@ async fn test_body_br_streaming() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_head_binary() {
|
async fn test_head_binary() {
|
||||||
let srv = test::start_with(test::config().h1(), || {
|
let srv = actix_test::start_with(actix_test::config().h1(), || {
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::resource("/").route(web::head().to(move || HttpResponse::Ok().body(STR))),
|
web::resource("/").route(web::head().to(move || HttpResponse::Ok().body(STR))),
|
||||||
)
|
)
|
||||||
@ -405,7 +405,7 @@ async fn test_head_binary() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_no_chunking() {
|
async fn test_no_chunking() {
|
||||||
let srv = test::start_with(test::config().h1(), || {
|
let srv = actix_test::start_with(actix_test::config().h1(), || {
|
||||||
App::new().service(web::resource("/").route(web::to(move || {
|
App::new().service(web::resource("/").route(web::to(move || {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.no_chunking(STR.len() as u64)
|
.no_chunking(STR.len() as u64)
|
||||||
@ -424,7 +424,7 @@ async fn test_no_chunking() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_body_deflate() {
|
async fn test_body_deflate() {
|
||||||
let srv = test::start_with(test::config().h1(), || {
|
let srv = actix_test::start_with(actix_test::config().h1(), || {
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(Compress::new(ContentEncoding::Deflate))
|
.wrap(Compress::new(ContentEncoding::Deflate))
|
||||||
.service(web::resource("/").route(web::to(move || HttpResponse::Ok().body(STR))))
|
.service(web::resource("/").route(web::to(move || HttpResponse::Ok().body(STR))))
|
||||||
@ -451,7 +451,7 @@ async fn test_body_deflate() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_body_brotli() {
|
async fn test_body_brotli() {
|
||||||
let srv = test::start_with(test::config().h1(), || {
|
let srv = actix_test::start_with(actix_test::config().h1(), || {
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(Compress::new(ContentEncoding::Br))
|
.wrap(Compress::new(ContentEncoding::Br))
|
||||||
.service(web::resource("/").route(web::to(move || HttpResponse::Ok().body(STR))))
|
.service(web::resource("/").route(web::to(move || HttpResponse::Ok().body(STR))))
|
||||||
@ -479,7 +479,7 @@ async fn test_body_brotli() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_encoding() {
|
async fn test_encoding() {
|
||||||
let srv = test::start_with(test::config().h1(), || {
|
let srv = actix_test::start_with(actix_test::config().h1(), || {
|
||||||
App::new().wrap(Compress::default()).service(
|
App::new().wrap(Compress::default()).service(
|
||||||
web::resource("/").route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))),
|
web::resource("/").route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))),
|
||||||
)
|
)
|
||||||
@ -504,7 +504,7 @@ async fn test_encoding() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_gzip_encoding() {
|
async fn test_gzip_encoding() {
|
||||||
let srv = test::start_with(test::config().h1(), || {
|
let srv = actix_test::start_with(actix_test::config().h1(), || {
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::resource("/").route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))),
|
web::resource("/").route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))),
|
||||||
)
|
)
|
||||||
@ -530,7 +530,7 @@ async fn test_gzip_encoding() {
|
|||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_gzip_encoding_large() {
|
async fn test_gzip_encoding_large() {
|
||||||
let data = STR.repeat(10);
|
let data = STR.repeat(10);
|
||||||
let srv = test::start_with(test::config().h1(), || {
|
let srv = actix_test::start_with(actix_test::config().h1(), || {
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::resource("/").route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))),
|
web::resource("/").route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))),
|
||||||
)
|
)
|
||||||
@ -561,7 +561,7 @@ async fn test_reading_gzip_encoding_large_random() {
|
|||||||
.map(char::from)
|
.map(char::from)
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
|
|
||||||
let srv = test::start_with(test::config().h1(), || {
|
let srv = actix_test::start_with(actix_test::config().h1(), || {
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::resource("/").route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))),
|
web::resource("/").route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))),
|
||||||
)
|
)
|
||||||
@ -587,7 +587,7 @@ async fn test_reading_gzip_encoding_large_random() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_reading_deflate_encoding() {
|
async fn test_reading_deflate_encoding() {
|
||||||
let srv = test::start_with(test::config().h1(), || {
|
let srv = actix_test::start_with(actix_test::config().h1(), || {
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::resource("/").route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))),
|
web::resource("/").route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))),
|
||||||
)
|
)
|
||||||
@ -613,7 +613,7 @@ async fn test_reading_deflate_encoding() {
|
|||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_reading_deflate_encoding_large() {
|
async fn test_reading_deflate_encoding_large() {
|
||||||
let data = STR.repeat(10);
|
let data = STR.repeat(10);
|
||||||
let srv = test::start_with(test::config().h1(), || {
|
let srv = actix_test::start_with(actix_test::config().h1(), || {
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::resource("/").route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))),
|
web::resource("/").route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))),
|
||||||
)
|
)
|
||||||
@ -644,7 +644,7 @@ async fn test_reading_deflate_encoding_large_random() {
|
|||||||
.map(char::from)
|
.map(char::from)
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
|
|
||||||
let srv = test::start_with(test::config().h1(), || {
|
let srv = actix_test::start_with(actix_test::config().h1(), || {
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::resource("/").route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))),
|
web::resource("/").route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))),
|
||||||
)
|
)
|
||||||
@ -670,7 +670,7 @@ async fn test_reading_deflate_encoding_large_random() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_brotli_encoding() {
|
async fn test_brotli_encoding() {
|
||||||
let srv = test::start_with(test::config().h1(), || {
|
let srv = actix_test::start_with(actix_test::config().h1(), || {
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::resource("/").route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))),
|
web::resource("/").route(web::to(move |body: Bytes| HttpResponse::Ok().body(body))),
|
||||||
)
|
)
|
||||||
@ -701,7 +701,7 @@ async fn test_brotli_encoding_large() {
|
|||||||
.map(char::from)
|
.map(char::from)
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
|
|
||||||
let srv = test::start_with(test::config().h1(), || {
|
let srv = actix_test::start_with(actix_test::config().h1(), || {
|
||||||
App::new().service(
|
App::new().service(
|
||||||
web::resource("/")
|
web::resource("/")
|
||||||
.app_data(web::PayloadConfig::new(320_000))
|
.app_data(web::PayloadConfig::new(320_000))
|
||||||
@ -732,7 +732,8 @@ async fn test_brotli_encoding_large() {
|
|||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_brotli_encoding_large_openssl() {
|
async fn test_brotli_encoding_large_openssl() {
|
||||||
let data = STR.repeat(10);
|
let data = STR.repeat(10);
|
||||||
let srv = test::start_with(test::config().openssl(openssl_config()), move || {
|
let srv =
|
||||||
|
actix_test::start_with(actix_test::config().openssl(openssl_config()), move || {
|
||||||
App::new().service(web::resource("/").route(web::to(|bytes: Bytes| {
|
App::new().service(web::resource("/").route(web::to(|bytes: Bytes| {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.encoding(actix_web::http::ContentEncoding::Identity)
|
.encoding(actix_web::http::ContentEncoding::Identity)
|
||||||
@ -794,7 +795,7 @@ mod plus_rustls {
|
|||||||
.map(char::from)
|
.map(char::from)
|
||||||
.collect::<String>();
|
.collect::<String>();
|
||||||
|
|
||||||
let srv = test::start_with(test::config().rustls(rustls_config()), || {
|
let srv = actix_test::start_with(actix_test::config().rustls(rustls_config()), || {
|
||||||
App::new().service(web::resource("/").route(web::to(|bytes: Bytes| {
|
App::new().service(web::resource("/").route(web::to(|bytes: Bytes| {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.encoding(actix_web::http::ContentEncoding::Identity)
|
.encoding(actix_web::http::ContentEncoding::Identity)
|
||||||
@ -827,7 +828,7 @@ mod plus_rustls {
|
|||||||
async fn test_server_cookies() {
|
async fn test_server_cookies() {
|
||||||
use actix_web::{http, HttpMessage};
|
use actix_web::{http, HttpMessage};
|
||||||
|
|
||||||
let srv = test::start(|| {
|
let srv = actix_test::start(|| {
|
||||||
App::new().default_service(web::to(|| {
|
App::new().default_service(web::to(|| {
|
||||||
HttpResponse::Ok()
|
HttpResponse::Ok()
|
||||||
.cookie(
|
.cookie(
|
||||||
@ -880,7 +881,7 @@ async fn test_server_cookies() {
|
|||||||
async fn test_slow_request() {
|
async fn test_slow_request() {
|
||||||
use std::net;
|
use std::net;
|
||||||
|
|
||||||
let srv = test::start_with(test::config().client_timeout(200), || {
|
let srv = actix_test::start_with(actix_test::config().client_timeout(200), || {
|
||||||
App::new().service(web::resource("/").route(web::to(HttpResponse::Ok)))
|
App::new().service(web::resource("/").route(web::to(HttpResponse::Ok)))
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -898,7 +899,7 @@ async fn test_slow_request() {
|
|||||||
|
|
||||||
#[actix_rt::test]
|
#[actix_rt::test]
|
||||||
async fn test_normalize() {
|
async fn test_normalize() {
|
||||||
let srv = test::start_with(test::config().h1(), || {
|
let srv = actix_test::start_with(actix_test::config().h1(), || {
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(NormalizePath::new(TrailingSlash::Trim))
|
.wrap(NormalizePath::new(TrailingSlash::Trim))
|
||||||
.service(web::resource("/one").route(web::to(|| HttpResponse::Ok().finish())))
|
.service(web::resource("/one").route(web::to(|| HttpResponse::Ok().finish())))
|
||||||
@ -907,3 +908,51 @@ async fn test_normalize() {
|
|||||||
let response = srv.get("/one/").send().await.unwrap();
|
let response = srv.get("/one/").send().await.unwrap();
|
||||||
assert!(response.status().is_success());
|
assert!(response.status().is_success());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[actix_rt::test]
|
||||||
|
async fn test_data_drop() {
|
||||||
|
use std::sync::{
|
||||||
|
atomic::{AtomicUsize, Ordering},
|
||||||
|
Arc,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TestData(Arc<AtomicUsize>);
|
||||||
|
|
||||||
|
impl TestData {
|
||||||
|
fn new(inner: Arc<AtomicUsize>) -> Self {
|
||||||
|
let _ = inner.fetch_add(1, Ordering::SeqCst);
|
||||||
|
Self(inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for TestData {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
let inner = self.0.clone();
|
||||||
|
let _ = inner.fetch_add(1, Ordering::SeqCst);
|
||||||
|
Self(inner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for TestData {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.0.fetch_sub(1, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let num = Arc::new(AtomicUsize::new(0));
|
||||||
|
let data = TestData::new(num.clone());
|
||||||
|
assert_eq!(num.load(Ordering::SeqCst), 1);
|
||||||
|
|
||||||
|
let srv = actix_test::start(move || {
|
||||||
|
let data = data.clone();
|
||||||
|
|
||||||
|
App::new()
|
||||||
|
.data(data)
|
||||||
|
.service(web::resource("/").to(|_data: web::Data<TestData>| async { "ok" }))
|
||||||
|
});
|
||||||
|
|
||||||
|
assert!(srv.get("/").send().await.unwrap().status().is_success());
|
||||||
|
srv.stop().await;
|
||||||
|
|
||||||
|
assert_eq!(num.load(Ordering::SeqCst), 0);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user