mirror of
https://github.com/actix/actix-web.git
synced 2024-12-21 07:36:43 +00:00
add FramedRequest builder for testing
This commit is contained in:
parent
67c34a5937
commit
5cfba5ff16
6 changed files with 240 additions and 32 deletions
|
@ -4,6 +4,7 @@ mod request;
|
||||||
mod route;
|
mod route;
|
||||||
mod service;
|
mod service;
|
||||||
mod state;
|
mod state;
|
||||||
|
pub mod test;
|
||||||
|
|
||||||
// re-export for convinience
|
// re-export for convinience
|
||||||
pub use actix_http::{http, Error, HttpMessage, Response, ResponseError};
|
pub use actix_http::{http, Error, HttpMessage, Response, ResponseError};
|
||||||
|
|
|
@ -123,6 +123,7 @@ impl<Io, S> FramedRequest<Io, S> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use actix_http::http::{HeaderName, HeaderValue, HttpTryFrom};
|
||||||
use actix_http::test::{TestBuffer, TestRequest};
|
use actix_http::test::{TestBuffer, TestRequest};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -136,7 +137,7 @@ mod tests {
|
||||||
.finish();
|
.finish();
|
||||||
let path = Path::new(Url::new(req.uri().clone()));
|
let path = Path::new(Url::new(req.uri().clone()));
|
||||||
|
|
||||||
let freq = FramedRequest::new(req, framed, path, State::new(10u8));
|
let mut freq = FramedRequest::new(req, framed, path, State::new(10u8));
|
||||||
assert_eq!(*freq.state(), 10);
|
assert_eq!(*freq.state(), 10);
|
||||||
assert_eq!(freq.version(), Version::HTTP_11);
|
assert_eq!(freq.version(), Version::HTTP_11);
|
||||||
assert_eq!(freq.method(), Method::GET);
|
assert_eq!(freq.method(), Method::GET);
|
||||||
|
@ -151,6 +152,15 @@ mod tests {
|
||||||
"test"
|
"test"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
freq.head_mut().headers.insert(
|
||||||
|
HeaderName::try_from("x-hdr").unwrap(),
|
||||||
|
HeaderValue::from_static("test"),
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
freq.headers().get("x-hdr").unwrap().to_str().unwrap(),
|
||||||
|
"test"
|
||||||
|
);
|
||||||
|
|
||||||
freq.extensions_mut().insert(100usize);
|
freq.extensions_mut().insert(100usize);
|
||||||
assert_eq!(*freq.extensions().get::<usize>().unwrap(), 100usize);
|
assert_eq!(*freq.extensions().get::<usize>().unwrap(), 100usize);
|
||||||
|
|
||||||
|
|
133
actix-framed/src/test.rs
Normal file
133
actix-framed/src/test.rs
Normal file
|
@ -0,0 +1,133 @@
|
||||||
|
//! Various helpers for Actix applications to use during testing.
|
||||||
|
use actix_codec::Framed;
|
||||||
|
use actix_http::h1::Codec;
|
||||||
|
use actix_http::http::header::{Header, HeaderName, IntoHeaderValue};
|
||||||
|
use actix_http::http::{HttpTryFrom, Method, Uri, Version};
|
||||||
|
use actix_http::test::{TestBuffer, TestRequest as HttpTestRequest};
|
||||||
|
use actix_router::{Path, Url};
|
||||||
|
|
||||||
|
use crate::{FramedRequest, State};
|
||||||
|
|
||||||
|
/// Test `Request` builder.
|
||||||
|
pub struct TestRequest {
|
||||||
|
req: HttpTestRequest,
|
||||||
|
path: Path<Url>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for TestRequest {
|
||||||
|
fn default() -> TestRequest {
|
||||||
|
TestRequest {
|
||||||
|
req: HttpTestRequest::default(),
|
||||||
|
path: Path::new(Url::new(Uri::default())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::wrong_self_convention)]
|
||||||
|
impl TestRequest {
|
||||||
|
/// Create TestRequest and set request uri
|
||||||
|
pub fn with_uri(path: &str) -> TestRequest {
|
||||||
|
Self::get().uri(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create TestRequest and set header
|
||||||
|
pub fn with_hdr<H: Header>(hdr: H) -> TestRequest {
|
||||||
|
Self::default().set(hdr)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create TestRequest and set header
|
||||||
|
pub fn with_header<K, V>(key: K, value: V) -> TestRequest
|
||||||
|
where
|
||||||
|
HeaderName: HttpTryFrom<K>,
|
||||||
|
V: IntoHeaderValue,
|
||||||
|
{
|
||||||
|
Self::default().header(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create TestRequest and set method to `Method::GET`
|
||||||
|
pub fn get() -> TestRequest {
|
||||||
|
Self::default().method(Method::GET)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create TestRequest and set method to `Method::POST`
|
||||||
|
pub fn post() -> TestRequest {
|
||||||
|
Self::default().method(Method::POST)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set HTTP version of this request
|
||||||
|
pub fn version(mut self, ver: Version) -> Self {
|
||||||
|
self.req.version(ver);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set HTTP method of this request
|
||||||
|
pub fn method(mut self, meth: Method) -> Self {
|
||||||
|
self.req.method(meth);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set HTTP Uri of this request
|
||||||
|
pub fn uri(mut self, path: &str) -> Self {
|
||||||
|
self.req.uri(path);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a header
|
||||||
|
pub fn set<H: Header>(mut self, hdr: H) -> Self {
|
||||||
|
self.req.set(hdr);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set a header
|
||||||
|
pub fn header<K, V>(mut self, key: K, value: V) -> Self
|
||||||
|
where
|
||||||
|
HeaderName: HttpTryFrom<K>,
|
||||||
|
V: IntoHeaderValue,
|
||||||
|
{
|
||||||
|
self.req.header(key, value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set request path pattern parameter
|
||||||
|
pub fn param(mut self, name: &'static str, value: &'static str) -> Self {
|
||||||
|
self.path.add_static(name, value);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Complete request creation and generate `Request` instance
|
||||||
|
pub fn finish(self) -> FramedRequest<TestBuffer, ()> {
|
||||||
|
self.finish_with_state(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Complete request creation and generate `Request` instance
|
||||||
|
pub fn finish_with_state<S>(mut self, state: S) -> FramedRequest<TestBuffer, S> {
|
||||||
|
let req = self.req.finish();
|
||||||
|
self.path.get_mut().update(req.uri());
|
||||||
|
let framed = Framed::new(TestBuffer::empty(), Codec::default());
|
||||||
|
FramedRequest::new(req, framed, self.path, State::new(state))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
let req = TestRequest::with_uri("/index.html")
|
||||||
|
.header("x-test", "test")
|
||||||
|
.param("test", "123")
|
||||||
|
.finish();
|
||||||
|
|
||||||
|
assert_eq!(*req.state(), ());
|
||||||
|
assert_eq!(req.version(), Version::HTTP_11);
|
||||||
|
assert_eq!(req.method(), Method::GET);
|
||||||
|
assert_eq!(req.path(), "/index.html");
|
||||||
|
assert_eq!(req.query_string(), "");
|
||||||
|
assert_eq!(
|
||||||
|
req.headers().get("x-test").unwrap().to_str().unwrap(),
|
||||||
|
"test"
|
||||||
|
);
|
||||||
|
assert_eq!(&req.match_info()["test"], "123");
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +1,13 @@
|
||||||
use actix_codec::{AsyncRead, AsyncWrite};
|
use actix_codec::{AsyncRead, AsyncWrite};
|
||||||
use actix_http::{body, ws, Error, HttpService, Response};
|
use actix_http::{body, ws, Error, HttpService, Response};
|
||||||
use actix_http_test::TestServer;
|
use actix_http_test::TestServer;
|
||||||
|
use actix_service::{IntoNewService, NewService};
|
||||||
use actix_utils::framed::FramedTransport;
|
use actix_utils::framed::FramedTransport;
|
||||||
use bytes::{Bytes, BytesMut};
|
use bytes::{Bytes, BytesMut};
|
||||||
use futures::future::{self, ok};
|
use futures::future::{self, ok};
|
||||||
use futures::{Future, Sink, Stream};
|
use futures::{Future, Sink, Stream};
|
||||||
|
|
||||||
use actix_framed::{FramedApp, FramedRequest, FramedRoute};
|
use actix_framed::{FramedApp, FramedRequest, FramedRoute, SendError, VerifyWebSockets};
|
||||||
|
|
||||||
fn ws_service<T: AsyncRead + AsyncWrite>(
|
fn ws_service<T: AsyncRead + AsyncWrite>(
|
||||||
req: FramedRequest<T>,
|
req: FramedRequest<T>,
|
||||||
|
@ -81,3 +82,55 @@ fn test_simple() {
|
||||||
Some(ws::Frame::Close(Some(ws::CloseCode::Normal.into())))
|
Some(ws::Frame::Close(Some(ws::CloseCode::Normal.into())))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_service() {
|
||||||
|
let mut srv = TestServer::new(|| {
|
||||||
|
actix_http::h1::OneRequest::new().map_err(|_| ()).and_then(
|
||||||
|
VerifyWebSockets::default()
|
||||||
|
.then(SendError::default())
|
||||||
|
.map_err(|_| ())
|
||||||
|
.and_then(
|
||||||
|
FramedApp::new()
|
||||||
|
.service(FramedRoute::get("/index.html").to(ws_service))
|
||||||
|
.into_new_service()
|
||||||
|
.map_err(|_| ()),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
assert!(srv.ws_at("/test").is_err());
|
||||||
|
|
||||||
|
// client service
|
||||||
|
let framed = srv.ws_at("/index.html").unwrap();
|
||||||
|
let framed = srv
|
||||||
|
.block_on(framed.send(ws::Message::Text("text".to_string())))
|
||||||
|
.unwrap();
|
||||||
|
let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap();
|
||||||
|
assert_eq!(item, Some(ws::Frame::Text(Some(BytesMut::from("text")))));
|
||||||
|
|
||||||
|
let framed = srv
|
||||||
|
.block_on(framed.send(ws::Message::Binary("text".into())))
|
||||||
|
.unwrap();
|
||||||
|
let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
item,
|
||||||
|
Some(ws::Frame::Binary(Some(Bytes::from_static(b"text").into())))
|
||||||
|
);
|
||||||
|
|
||||||
|
let framed = srv
|
||||||
|
.block_on(framed.send(ws::Message::Ping("text".into())))
|
||||||
|
.unwrap();
|
||||||
|
let (item, framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap();
|
||||||
|
assert_eq!(item, Some(ws::Frame::Pong("text".to_string().into())));
|
||||||
|
|
||||||
|
let framed = srv
|
||||||
|
.block_on(framed.send(ws::Message::Close(Some(ws::CloseCode::Normal.into()))))
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let (item, _framed) = srv.block_on(framed.into_future()).map_err(|_| ()).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
item,
|
||||||
|
Some(ws::Frame::Close(Some(ws::CloseCode::Normal.into())))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
35
src/test.rs
35
src/test.rs
|
@ -201,22 +201,12 @@ impl Default for TestRequest {
|
||||||
impl TestRequest {
|
impl TestRequest {
|
||||||
/// Create TestRequest and set request uri
|
/// Create TestRequest and set request uri
|
||||||
pub fn with_uri(path: &str) -> TestRequest {
|
pub fn with_uri(path: &str) -> TestRequest {
|
||||||
TestRequest {
|
TestRequest::default().uri(path)
|
||||||
req: HttpTestRequest::default().uri(path).take(),
|
|
||||||
rmap: ResourceMap::new(ResourceDef::new("")),
|
|
||||||
config: AppConfigInner::default(),
|
|
||||||
route_data: Extensions::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create TestRequest and set header
|
/// Create TestRequest and set header
|
||||||
pub fn with_hdr<H: Header>(hdr: H) -> TestRequest {
|
pub fn with_hdr<H: Header>(hdr: H) -> TestRequest {
|
||||||
TestRequest {
|
TestRequest::default().set(hdr)
|
||||||
req: HttpTestRequest::default().set(hdr).take(),
|
|
||||||
config: AppConfigInner::default(),
|
|
||||||
rmap: ResourceMap::new(ResourceDef::new("")),
|
|
||||||
route_data: Extensions::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create TestRequest and set header
|
/// Create TestRequest and set header
|
||||||
|
@ -225,32 +215,17 @@ impl TestRequest {
|
||||||
HeaderName: HttpTryFrom<K>,
|
HeaderName: HttpTryFrom<K>,
|
||||||
V: IntoHeaderValue,
|
V: IntoHeaderValue,
|
||||||
{
|
{
|
||||||
TestRequest {
|
TestRequest::default().header(key, value)
|
||||||
req: HttpTestRequest::default().header(key, value).take(),
|
|
||||||
config: AppConfigInner::default(),
|
|
||||||
rmap: ResourceMap::new(ResourceDef::new("")),
|
|
||||||
route_data: Extensions::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create TestRequest and set method to `Method::GET`
|
/// Create TestRequest and set method to `Method::GET`
|
||||||
pub fn get() -> TestRequest {
|
pub fn get() -> TestRequest {
|
||||||
TestRequest {
|
TestRequest::default().method(Method::GET)
|
||||||
req: HttpTestRequest::default().method(Method::GET).take(),
|
|
||||||
config: AppConfigInner::default(),
|
|
||||||
rmap: ResourceMap::new(ResourceDef::new("")),
|
|
||||||
route_data: Extensions::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Create TestRequest and set method to `Method::POST`
|
/// Create TestRequest and set method to `Method::POST`
|
||||||
pub fn post() -> TestRequest {
|
pub fn post() -> TestRequest {
|
||||||
TestRequest {
|
TestRequest::default().method(Method::POST)
|
||||||
req: HttpTestRequest::default().method(Method::POST).take(),
|
|
||||||
config: AppConfigInner::default(),
|
|
||||||
rmap: ResourceMap::new(ResourceDef::new("")),
|
|
||||||
route_data: Extensions::new(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set HTTP version of this request
|
/// Set HTTP version of this request
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
//! Various helpers for Actix applications to use during testing.
|
//! Various helpers for Actix applications to use during testing.
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::{net, thread, time};
|
use std::{net, thread, time};
|
||||||
|
|
||||||
|
@ -12,6 +13,41 @@ use futures::{Future, Stream};
|
||||||
use http::Method;
|
use http::Method;
|
||||||
use net2::TcpBuilder;
|
use net2::TcpBuilder;
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
static RT: RefCell<Runtime> = {
|
||||||
|
RefCell::new(Runtime::new().unwrap())
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runs the provided future, blocking the current thread until the future
|
||||||
|
/// completes.
|
||||||
|
///
|
||||||
|
/// This function can be used to synchronously block the current thread
|
||||||
|
/// until the provided `future` has resolved either successfully or with an
|
||||||
|
/// error. The result of the future is then returned from this function
|
||||||
|
/// call.
|
||||||
|
///
|
||||||
|
/// Note that this function is intended to be used only for testing purpose.
|
||||||
|
/// This function panics on nested call.
|
||||||
|
pub fn block_on<F>(f: F) -> Result<F::Item, F::Error>
|
||||||
|
where
|
||||||
|
F: Future,
|
||||||
|
{
|
||||||
|
RT.with(move |rt| rt.borrow_mut().block_on(f))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runs the provided function, with runtime enabled.
|
||||||
|
///
|
||||||
|
/// Note that this function is intended to be used only for testing purpose.
|
||||||
|
/// This function panics on nested call.
|
||||||
|
pub fn run_on<F, R>(f: F) -> R
|
||||||
|
where
|
||||||
|
F: Fn() -> R,
|
||||||
|
{
|
||||||
|
RT.with(move |rt| rt.borrow_mut().block_on(lazy(|| Ok::<_, ()>(f()))))
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
/// The `TestServer` type.
|
/// The `TestServer` type.
|
||||||
///
|
///
|
||||||
/// `TestServer` is very simple test server that simplify process of writing
|
/// `TestServer` is very simple test server that simplify process of writing
|
||||||
|
|
Loading…
Reference in a new issue