use std::str::FromStr;
use bytes::Bytes;
use http::{header, Method, Uri, Version};
use crate::{
header::{HeaderMap, TryIntoHeaderPair},
payload::Payload,
Request,
};
pub struct TestRequest(Option<Inner>);
struct Inner {
version: Version,
method: Method,
uri: Uri,
headers: HeaderMap,
payload: Option<Payload>,
}
impl Default for TestRequest {
fn default() -> TestRequest {
TestRequest(Some(Inner {
method: Method::GET,
uri: Uri::from_str("/").unwrap(),
version: Version::HTTP_11,
headers: HeaderMap::new(),
payload: None,
}))
}
}
impl TestRequest {
pub fn with_uri(path: &str) -> TestRequest {
TestRequest::default().uri(path).take()
}
pub fn version(&mut self, ver: Version) -> &mut Self {
parts(&mut self.0).version = ver;
self
}
pub fn method(&mut self, meth: Method) -> &mut Self {
parts(&mut self.0).method = meth;
self
}
pub fn uri(&mut self, path: &str) -> &mut Self {
parts(&mut self.0).uri = Uri::from_str(path).unwrap();
self
}
pub fn insert_header(&mut self, header: impl TryIntoHeaderPair) -> &mut Self {
match header.try_into_pair() {
Ok((key, value)) => {
parts(&mut self.0).headers.insert(key, value);
}
Err(err) => {
panic!("Error inserting test header: {}.", err.into());
}
}
self
}
pub fn append_header(&mut self, header: impl TryIntoHeaderPair) -> &mut Self {
match header.try_into_pair() {
Ok((key, value)) => {
parts(&mut self.0).headers.append(key, value);
}
Err(err) => {
panic!("Error inserting test header: {}.", err.into());
}
}
self
}
pub fn set_payload(&mut self, data: impl Into<Bytes>) -> &mut Self {
let mut payload = crate::h1::Payload::empty();
let bytes = data.into();
self.insert_header((header::CONTENT_LENGTH, bytes.len()));
payload.unread_data(bytes);
parts(&mut self.0).payload = Some(payload.into());
self
}
pub fn take(&mut self) -> TestRequest {
TestRequest(self.0.take())
}
pub fn finish(&mut self) -> Request {
let inner = self.0.take().expect("cannot reuse test request builder");
let mut req = if let Some(pl) = inner.payload {
Request::with_payload(pl)
} else {
Request::with_payload(crate::h1::Payload::empty().into())
};
let head = req.head_mut();
head.uri = inner.uri;
head.method = inner.method;
head.version = inner.version;
head.headers = inner.headers;
req
}
}
#[inline]
fn parts(parts: &mut Option<Inner>) -> &mut Inner {
parts.as_mut().expect("cannot reuse test request builder")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn request_builder() {
let mut request = TestRequest::with_uri("/initial");
request
.version(Version::HTTP_10)
.method(Method::POST)
.uri("/final")
.insert_header(("x-test", "one"))
.append_header(("x-test", "two"));
let request = request.finish();
assert_eq!(request.head().uri, Uri::from_static("/final"));
assert_eq!(request.head().method, Method::POST);
assert_eq!(request.head().version, Version::HTTP_10);
assert_eq!(request.head().headers.get_all("x-test").count(), 2);
let mut request = TestRequest::default();
request.set_payload("body");
let request = request.finish();
assert_eq!(
request.head().headers.get(header::CONTENT_LENGTH).unwrap(),
"4"
);
let mut request = TestRequest::default();
let request = request.take();
let mut request = request;
assert_eq!(request.finish().head().uri, Uri::from_static("/"));
}
#[test]
#[should_panic(expected = "Error inserting test header")]
fn insert_header_rejects_invalid_header() {
TestRequest::default().insert_header(("invalid name", "value"));
}
#[test]
#[should_panic(expected = "Error inserting test header")]
fn append_header_rejects_invalid_header() {
TestRequest::default().append_header(("invalid name", "value"));
}
}