1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-24 00:21:08 +01:00

add TestServiceRequest builder

This commit is contained in:
Nikolay Kim 2019-03-02 16:24:14 -08:00
parent 9394a4e2a5
commit e4198a037a
6 changed files with 229 additions and 259 deletions

View File

@ -44,13 +44,11 @@ flate2-rust = ["flate2/rust_backend"]
[dependencies]
actix-codec = "0.1.0"
#actix-service = "0.2.1"
#actix-server = "0.2.1"
#actix-utils = "0.2.1"
actix-utils = { git = "https://github.com/actix/actix-net.git" }
actix-service = "0.3.0"
actix-utils = "0.3.0"
actix-http = { git = "https://github.com/actix/actix-http.git" }
actix-router = { git = "https://github.com/actix/actix-net.git" }
actix-service = { git = "https://github.com/actix/actix-net.git" }
bytes = "0.4"
derive_more = "0.14"
@ -73,8 +71,7 @@ flate2 = { version="^1.0.2", optional = true, default-features = false }
[dev-dependencies]
actix-rt = "0.1.0"
#actix-server = { version="0.2", features=["ssl"] }
actix-server = { git = "https://github.com/actix/actix-net.git", features=["ssl"] }
actix-server = { version="0.3.0", features=["ssl"] }
actix-http = { git = "https://github.com/actix/actix-http.git", features=["ssl"] }
actix-http-test = { git = "https://github.com/actix/actix-http.git", features=["ssl"] }
rand = "0.6"

View File

@ -2,7 +2,7 @@ use std::cell::RefCell;
use std::marker::PhantomData;
use std::rc::Rc;
use actix_http::body::MessageBody;
use actix_http::body::{Body, MessageBody};
use actix_http::{Extensions, PayloadStream, Request, Response};
use actix_router::{Path, ResourceDef, ResourceInfo, Router, Url};
use actix_service::boxed::{self, BoxedNewService, BoxedService};
@ -130,7 +130,7 @@ where
/// });
/// }
/// ```
pub fn resource<F, U, B>(self, path: &str, f: F) -> AppRouter<T, P, B, AppEntry<P>>
pub fn resource<F, U>(self, path: &str, f: F) -> AppRouter<T, P, Body, AppEntry<P>>
where
F: FnOnce(Resource<P>) -> Resource<P, U>,
U: NewService<
@ -159,16 +159,16 @@ where
}
/// Register a middleware.
pub fn middleware<M, B, F>(
pub fn middleware<M, F>(
self,
mw: F,
) -> AppRouter<
T,
P,
B,
Body,
impl NewService<
Request = ServiceRequest<P>,
Response = ServiceResponse<B>,
Response = ServiceResponse,
Error = (),
InitError = (),
>,
@ -177,11 +177,10 @@ where
M: NewTransform<
AppService<P>,
Request = ServiceRequest<P>,
Response = ServiceResponse<B>,
Response = ServiceResponse,
Error = (),
InitError = (),
>,
B: MessageBody,
F: IntoNewTransform<M, AppService<P>>,
{
let fref = Rc::new(RefCell::new(None));

View File

@ -16,14 +16,13 @@ pub trait Filter {
/// Return filter that matches if any of supplied filters.
///
/// ```rust,ignore
/// # extern crate actix_web;
/// use actix_web2::{filter, App, HttpResponse};
/// use actix_web::{filter, App, HttpResponse};
///
/// fn main() {
/// App::new().resource("/index.html", |r| {
/// r.route()
/// .filter(pred::Any(pred::Get()).or(pred::Post()))
/// .f(|r| HttpResponse::MethodNotAllowed())
/// .to(|| HttpResponse::MethodNotAllowed())
/// });
/// }
/// ```
@ -55,18 +54,18 @@ impl Filter for AnyFilter {
/// Return filter that matches if all of supplied filters match.
///
/// ```rust,ignore
/// ```rust
/// # extern crate actix_web;
/// use actix_web::{pred, App, HttpResponse};
/// use actix_web::{filter, App, HttpResponse};
///
/// fn main() {
/// App::new().resource("/index.html", |r| {
/// r.route()
/// .filter(
/// pred::All(pred::Get())
/// .and(pred::Header("content-type", "text/plain")),
/// r.route(
/// |r| r.filter(
/// filter::All(filter::Get())
/// .and(filter::Header("content-type", "text/plain")),
/// )
/// .f(|_| HttpResponse::MethodNotAllowed())
/// .to(|| HttpResponse::MethodNotAllowed()))
/// });
/// }
/// ```
@ -191,137 +190,146 @@ impl Filter for HeaderFilter {
}
}
/// Return predicate that matches if request contains specified Host name.
///
/// ```rust,ignore
/// # extern crate actix_web;
/// use actix_web::{pred, App, HttpResponse};
///
/// fn main() {
/// App::new().resource("/index.html", |r| {
/// r.route()
/// .filter(pred::Host("www.rust-lang.org"))
/// .f(|_| HttpResponse::MethodNotAllowed())
/// });
/// }
/// ```
pub fn Host<H: AsRef<str>>(host: H) -> HostFilter {
HostFilter(host.as_ref().to_string(), None)
}
// /// Return predicate that matches if request contains specified Host name.
// ///
// /// ```rust,ignore
// /// # extern crate actix_web;
// /// use actix_web::{pred, App, HttpResponse};
// ///
// /// fn main() {
// /// App::new().resource("/index.html", |r| {
// /// r.route()
// /// .filter(pred::Host("www.rust-lang.org"))
// /// .f(|_| HttpResponse::MethodNotAllowed())
// /// });
// /// }
// /// ```
// pub fn Host<H: AsRef<str>>(host: H) -> HostFilter {
// HostFilter(host.as_ref().to_string(), None)
// }
#[doc(hidden)]
pub struct HostFilter(String, Option<String>);
// #[doc(hidden)]
// pub struct HostFilter(String, Option<String>);
impl HostFilter {
/// Set reuest scheme to match
pub fn scheme<H: AsRef<str>>(&mut self, scheme: H) {
self.1 = Some(scheme.as_ref().to_string())
}
}
impl Filter for HostFilter {
fn check(&self, _req: &HttpRequest) -> bool {
// let info = req.connection_info();
// if let Some(ref scheme) = self.1 {
// self.0 == info.host() && scheme == info.scheme()
// } else {
// self.0 == info.host()
// }
false
}
}
// #[cfg(test)]
// mod tests {
// use actix_http::http::{header, Method};
// use actix_http::test::TestRequest;
// use super::*;
// #[test]
// fn test_header() {
// let req = TestRequest::with_header(
// header::TRANSFER_ENCODING,
// header::HeaderValue::from_static("chunked"),
// )
// .finish();
// let pred = Header("transfer-encoding", "chunked");
// assert!(pred.check(&req, req.state()));
// let pred = Header("transfer-encoding", "other");
// assert!(!pred.check(&req, req.state()));
// let pred = Header("content-type", "other");
// assert!(!pred.check(&req, req.state()));
// }
// #[test]
// fn test_host() {
// let req = TestRequest::default()
// .header(
// header::HOST,
// header::HeaderValue::from_static("www.rust-lang.org"),
// )
// .finish();
// let pred = Host("www.rust-lang.org");
// assert!(pred.check(&req, req.state()));
// let pred = Host("localhost");
// assert!(!pred.check(&req, req.state()));
// }
// #[test]
// fn test_methods() {
// let req = TestRequest::default().finish();
// let req2 = TestRequest::default().method(Method::POST).finish();
// assert!(Get().check(&req, req.state()));
// assert!(!Get().check(&req2, req2.state()));
// assert!(Post().check(&req2, req2.state()));
// assert!(!Post().check(&req, req.state()));
// let r = TestRequest::default().method(Method::PUT).finish();
// assert!(Put().check(&r, r.state()));
// assert!(!Put().check(&req, req.state()));
// let r = TestRequest::default().method(Method::DELETE).finish();
// assert!(Delete().check(&r, r.state()));
// assert!(!Delete().check(&req, req.state()));
// let r = TestRequest::default().method(Method::HEAD).finish();
// assert!(Head().check(&r, r.state()));
// assert!(!Head().check(&req, req.state()));
// let r = TestRequest::default().method(Method::OPTIONS).finish();
// assert!(Options().check(&r, r.state()));
// assert!(!Options().check(&req, req.state()));
// let r = TestRequest::default().method(Method::CONNECT).finish();
// assert!(Connect().check(&r, r.state()));
// assert!(!Connect().check(&req, req.state()));
// let r = TestRequest::default().method(Method::PATCH).finish();
// assert!(Patch().check(&r, r.state()));
// assert!(!Patch().check(&req, req.state()));
// let r = TestRequest::default().method(Method::TRACE).finish();
// assert!(Trace().check(&r, r.state()));
// assert!(!Trace().check(&req, req.state()));
// }
// #[test]
// fn test_preds() {
// let r = TestRequest::default().method(Method::TRACE).finish();
// assert!(Not(Get()).check(&r, r.state()));
// assert!(!Not(Trace()).check(&r, r.state()));
// assert!(All(Trace()).and(Trace()).check(&r, r.state()));
// assert!(!All(Get()).and(Trace()).check(&r, r.state()));
// assert!(Any(Get()).or(Trace()).check(&r, r.state()));
// assert!(!Any(Get()).or(Get()).check(&r, r.state()));
// impl HostFilter {
// /// Set reuest scheme to match
// pub fn scheme<H: AsRef<str>>(&mut self, scheme: H) {
// self.1 = Some(scheme.as_ref().to_string())
// }
// }
// impl Filter for HostFilter {
// fn check(&self, _req: &HttpRequest) -> bool {
// // let info = req.connection_info();
// // if let Some(ref scheme) = self.1 {
// // self.0 == info.host() && scheme == info.scheme()
// // } else {
// // self.0 == info.host()
// // }
// false
// }
// }
#[cfg(test)]
mod tests {
use crate::test::TestServiceRequest;
use actix_http::http::{header, Method};
use super::*;
#[test]
fn test_header() {
let req = TestServiceRequest::with_header(header::TRANSFER_ENCODING, "chunked")
.finish()
.into_request();
let pred = Header("transfer-encoding", "chunked");
assert!(pred.check(&req));
let pred = Header("transfer-encoding", "other");
assert!(!pred.check(&req));
let pred = Header("content-type", "other");
assert!(!pred.check(&req));
}
// #[test]
// fn test_host() {
// let req = TestServiceRequest::default()
// .header(
// header::HOST,
// header::HeaderValue::from_static("www.rust-lang.org"),
// )
// .request();
// let pred = Host("www.rust-lang.org");
// assert!(pred.check(&req));
// let pred = Host("localhost");
// assert!(!pred.check(&req));
// }
#[test]
fn test_methods() {
let req = TestServiceRequest::default().finish().into_request();
let req2 = TestServiceRequest::default()
.method(Method::POST)
.finish()
.into_request();
assert!(Get().check(&req));
assert!(!Get().check(&req2));
assert!(Post().check(&req2));
assert!(!Post().check(&req));
let r = TestServiceRequest::default().method(Method::PUT).finish();
assert!(Put().check(&r,));
assert!(!Put().check(&req,));
let r = TestServiceRequest::default()
.method(Method::DELETE)
.finish();
assert!(Delete().check(&r,));
assert!(!Delete().check(&req,));
let r = TestServiceRequest::default().method(Method::HEAD).finish();
assert!(Head().check(&r,));
assert!(!Head().check(&req,));
let r = TestServiceRequest::default()
.method(Method::OPTIONS)
.finish();
assert!(Options().check(&r,));
assert!(!Options().check(&req,));
let r = TestServiceRequest::default()
.method(Method::CONNECT)
.finish();
assert!(Connect().check(&r,));
assert!(!Connect().check(&req,));
let r = TestServiceRequest::default().method(Method::PATCH).finish();
assert!(Patch().check(&r,));
assert!(!Patch().check(&req,));
let r = TestServiceRequest::default().method(Method::TRACE).finish();
assert!(Trace().check(&r,));
assert!(!Trace().check(&req,));
}
#[test]
fn test_preds() {
let r = TestServiceRequest::default()
.method(Method::TRACE)
.request();
assert!(Not(Get()).check(&r,));
assert!(!Not(Trace()).check(&r,));
assert!(All(Trace()).and(Trace()).check(&r,));
assert!(!All(Get()).and(Trace()).check(&r,));
assert!(Any(Get()).or(Trace()).check(&r,));
assert!(!Any(Get()).or(Get()).check(&r,));
}
}

View File

@ -13,6 +13,7 @@ mod responder;
mod route;
mod service;
mod state;
pub mod test;
// re-export for convenience
pub use actix_http::Response as HttpResponse;

View File

@ -180,7 +180,7 @@ impl<P: 'static> RouteBuilder<P> {
/// # .finish();
/// # }
/// ```
pub fn filter<F: Filter + 'static>(&mut self, f: F) -> &mut Self {
pub fn filter<F: Filter + 'static>(mut self, f: F) -> Self {
self.filters.push(Box::new(f));
self
}

View File

@ -1,18 +1,15 @@
//! Various helpers for Actix applications to use during testing.
use std::str::FromStr;
use std::ops::{Deref, DerefMut};
use std::rc::Rc;
use bytes::Bytes;
use futures::IntoFuture;
use tokio_current_thread::Runtime;
use actix_http::dev::Payload;
use actix_http::http::header::{Header, HeaderName, IntoHeaderValue};
use actix_http::http::{HeaderMap, HttpTryFrom, Method, Uri, Version};
use actix_http::Request as HttpRequest;
use actix_http::http::{HttpTryFrom, Method, Version};
use actix_http::test::TestRequest;
use actix_http::{Extensions, PayloadStream};
use actix_router::{Path, Url};
use bytes::Bytes;
use crate::app::State;
use crate::request::Request;
use crate::request::HttpRequest;
use crate::service::ServiceRequest;
/// Test `Request` builder
@ -42,90 +39,71 @@ use crate::service::ServiceRequest;
/// assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
/// }
/// ```
pub struct TestRequest<S> {
state: S,
version: Version,
method: Method,
uri: Uri,
headers: HeaderMap,
params: Path<Url>,
payload: Option<Payload>,
pub struct TestServiceRequest {
req: TestRequest,
extensions: Extensions,
}
impl Default for TestRequest<()> {
fn default() -> TestRequest<()> {
TestRequest {
state: (),
method: Method::GET,
uri: Uri::from_str("/").unwrap(),
version: Version::HTTP_11,
headers: HeaderMap::new(),
params: Path::new(Url::default()),
payload: None,
impl Default for TestServiceRequest {
fn default() -> TestServiceRequest {
TestServiceRequest {
req: TestRequest::default(),
extensions: Extensions::new(),
}
}
}
impl TestRequest<()> {
impl TestServiceRequest {
/// Create TestRequest and set request uri
pub fn with_uri(path: &str) -> TestRequest<()> {
TestRequest::default().uri(path)
pub fn with_uri(path: &str) -> TestServiceRequest {
TestServiceRequest {
req: TestRequest::default().uri(path).take(),
extensions: Extensions::new(),
}
}
/// Create TestRequest and set header
pub fn with_hdr<H: Header>(hdr: H) -> TestRequest<()> {
TestRequest::default().set(hdr)
pub fn with_hdr<H: Header>(hdr: H) -> TestServiceRequest {
TestServiceRequest {
req: TestRequest::default().set(hdr).take(),
extensions: Extensions::new(),
}
}
/// Create TestRequest and set header
pub fn with_header<K, V>(key: K, value: V) -> TestRequest<()>
pub fn with_header<K, V>(key: K, value: V) -> TestServiceRequest
where
HeaderName: HttpTryFrom<K>,
V: IntoHeaderValue,
{
TestRequest::default().header(key, value)
}
}
impl<S: 'static> TestRequest<S> {
/// Start HttpRequest build process with application state
pub fn with_state(state: S) -> TestRequest<S> {
TestRequest {
state,
method: Method::GET,
uri: Uri::from_str("/").unwrap(),
version: Version::HTTP_11,
headers: HeaderMap::new(),
params: Path::new(Url::default()),
payload: None,
TestServiceRequest {
req: TestRequest::default().header(key, value).take(),
extensions: Extensions::new(),
}
}
/// Set HTTP version of this request
pub fn version(mut self, ver: Version) -> Self {
self.version = ver;
self.req.version(ver);
self
}
/// Set HTTP method of this request
pub fn method(mut self, meth: Method) -> Self {
self.method = meth;
self.req.method(meth);
self
}
/// Set HTTP Uri of this request
pub fn uri(mut self, path: &str) -> Self {
self.uri = Uri::from_str(path).unwrap();
self.req.uri(path);
self
}
/// Set a header
pub fn set<H: Header>(mut self, hdr: H) -> Self {
if let Ok(value) = hdr.try_into() {
self.headers.append(H::name(), value);
return self;
}
panic!("Can not set header");
self.req.set(hdr);
self
}
/// Set a header
@ -134,63 +112,50 @@ impl<S: 'static> TestRequest<S> {
HeaderName: HttpTryFrom<K>,
V: IntoHeaderValue,
{
if let Ok(key) = HeaderName::try_from(key) {
if let Ok(value) = value.try_into() {
self.headers.append(key, value);
return self;
}
}
panic!("Can not create header");
}
/// Set request path pattern parameter
pub fn param(mut self, name: &'static str, value: &'static str) -> Self {
self.params.add_static(name, value);
self.req.header(key, value);
self
}
/// Set request payload
pub fn set_payload<B: Into<Bytes>>(mut self, data: B) -> Self {
let mut payload = Payload::empty();
payload.unread_data(data.into());
self.payload = Some(payload);
self.req.set_payload(data);
self
}
/// Complete request creation and generate `HttpRequest` instance
pub fn finish(self) -> ServiceRequest<S> {
let TestRequest {
state,
method,
uri,
version,
headers,
mut params,
payload,
} = self;
/// Complete request creation and generate `ServiceRequest` instance
pub fn finish(mut self) -> ServiceRequest<PayloadStream> {
let req = self.req.finish();
params.get_mut().update(&uri);
let mut req = HttpRequest::new();
{
let inner = req.inner_mut();
inner.head.uri = uri;
inner.head.method = method;
inner.head.version = version;
inner.head.headers = headers;
*inner.payload.borrow_mut() = payload;
}
Request::new(State::new(state), req, params)
ServiceRequest::new(
Path::new(Url::new(req.uri().clone())),
req,
Rc::new(self.extensions),
)
}
/// This method generates `HttpRequest` instance and executes handler
pub fn run_async<F, R, I, E>(self, f: F) -> Result<I, E>
where
F: FnOnce(&Request<S>) -> R,
R: IntoFuture<Item = I, Error = E>,
{
let mut rt = Runtime::new().unwrap();
rt.block_on(f(&self.finish()).into_future())
/// Complete request creation and generate `HttpRequest` instance
pub fn request(mut self) -> HttpRequest {
let req = self.req.finish();
ServiceRequest::new(
Path::new(Url::new(req.uri().clone())),
req,
Rc::new(self.extensions),
)
.into_request()
}
}
impl Deref for TestServiceRequest {
type Target = TestRequest;
fn deref(&self) -> &TestRequest {
&self.req
}
}
impl DerefMut for TestServiceRequest {
fn deref_mut(&mut self) -> &mut TestRequest {
&mut self.req
}
}