1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-27 17:52:56 +01:00

add FramedRequest builder for testing

This commit is contained in:
Nikolay Kim 2019-04-12 11:15:58 -07:00
parent 67c34a5937
commit 5cfba5ff16
6 changed files with 240 additions and 32 deletions

View File

@ -4,6 +4,7 @@ mod request;
mod route;
mod service;
mod state;
pub mod test;
// re-export for convinience
pub use actix_http::{http, Error, HttpMessage, Response, ResponseError};

View File

@ -123,6 +123,7 @@ impl<Io, S> FramedRequest<Io, S> {
#[cfg(test)]
mod tests {
use actix_http::http::{HeaderName, HeaderValue, HttpTryFrom};
use actix_http::test::{TestBuffer, TestRequest};
use super::*;
@ -136,7 +137,7 @@ mod tests {
.finish();
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.version(), Version::HTTP_11);
assert_eq!(freq.method(), Method::GET);
@ -151,6 +152,15 @@ mod tests {
"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);
assert_eq!(*freq.extensions().get::<usize>().unwrap(), 100usize);

133
actix-framed/src/test.rs Normal file
View 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");
}
}

View File

@ -1,12 +1,13 @@
use actix_codec::{AsyncRead, AsyncWrite};
use actix_http::{body, ws, Error, HttpService, Response};
use actix_http_test::TestServer;
use actix_service::{IntoNewService, NewService};
use actix_utils::framed::FramedTransport;
use bytes::{Bytes, BytesMut};
use futures::future::{self, ok};
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>(
req: FramedRequest<T>,
@ -81,3 +82,55 @@ fn test_simple() {
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())))
);
}

View File

@ -201,22 +201,12 @@ impl Default for TestRequest {
impl TestRequest {
/// Create TestRequest and set request uri
pub fn with_uri(path: &str) -> TestRequest {
TestRequest {
req: HttpTestRequest::default().uri(path).take(),
rmap: ResourceMap::new(ResourceDef::new("")),
config: AppConfigInner::default(),
route_data: Extensions::new(),
}
TestRequest::default().uri(path)
}
/// Create TestRequest and set header
pub fn with_hdr<H: Header>(hdr: H) -> TestRequest {
TestRequest {
req: HttpTestRequest::default().set(hdr).take(),
config: AppConfigInner::default(),
rmap: ResourceMap::new(ResourceDef::new("")),
route_data: Extensions::new(),
}
TestRequest::default().set(hdr)
}
/// Create TestRequest and set header
@ -225,32 +215,17 @@ impl TestRequest {
HeaderName: HttpTryFrom<K>,
V: IntoHeaderValue,
{
TestRequest {
req: HttpTestRequest::default().header(key, value).take(),
config: AppConfigInner::default(),
rmap: ResourceMap::new(ResourceDef::new("")),
route_data: Extensions::new(),
}
TestRequest::default().header(key, value)
}
/// Create TestRequest and set method to `Method::GET`
pub fn get() -> TestRequest {
TestRequest {
req: HttpTestRequest::default().method(Method::GET).take(),
config: AppConfigInner::default(),
rmap: ResourceMap::new(ResourceDef::new("")),
route_data: Extensions::new(),
}
TestRequest::default().method(Method::GET)
}
/// Create TestRequest and set method to `Method::POST`
pub fn post() -> TestRequest {
TestRequest {
req: HttpTestRequest::default().method(Method::POST).take(),
config: AppConfigInner::default(),
rmap: ResourceMap::new(ResourceDef::new("")),
route_data: Extensions::new(),
}
TestRequest::default().method(Method::POST)
}
/// Set HTTP version of this request

View File

@ -1,4 +1,5 @@
//! Various helpers for Actix applications to use during testing.
use std::cell::RefCell;
use std::sync::mpsc;
use std::{net, thread, time};
@ -12,6 +13,41 @@ use futures::{Future, Stream};
use http::Method;
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.
///
/// `TestServer` is very simple test server that simplify process of writing