1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224
use std::{
fmt::Debug,
net::IpAddr,
ops::{Deref, DerefMut},
};
use trillium::{Conn, Handler, HeaderName, HeaderValues, Method};
use trillium_http::{Conn as HttpConn, Synthetic};
type SyntheticConn = HttpConn<Synthetic>;
/**
A wrapper around a [`trillium::Conn`] for testing
Stability note: this may be replaced by an extension trait at some point.
*/
#[derive(Debug)]
pub struct TestConn(Conn);
impl TestConn {
/**
constructs a new TestConn with the provided method, path, and body.
```
use trillium_testing::{prelude::*, TestConn};
let mut conn = TestConn::build("get", "/", "body");
assert_eq!(conn.method(), Method::Get);
assert_eq!(conn.path(), "/");
assert_eq!(conn.take_request_body_string(), "body");
```
*/
pub fn build<M>(method: M, path: impl Into<String>, body: impl Into<Synthetic>) -> Self
where
M: TryInto<Method>,
<M as TryInto<Method>>::Error: Debug,
{
Self(HttpConn::new_synthetic(method.try_into().unwrap(), path.into(), body).into())
}
/**
chainable constructor to append a request header to the TestConn
```
use trillium_testing::TestConn;
let conn = TestConn::build("get", "/", "body")
.with_request_header("some-header", "value");
assert_eq!(conn.request_headers().get_str("some-header"), Some("value"));
```
*/
pub fn with_request_header(
self,
header_name: impl Into<HeaderName<'static>>,
header_value: impl Into<HeaderValues>,
) -> Self {
let mut inner: SyntheticConn = self.into();
inner
.request_headers_mut()
.append(header_name, header_value);
Self(inner.into())
}
/**
chainable constructor to replace the request body. this is useful
when chaining with a [`trillium_testing::methods`](crate::methods)
builder, as they do not provide a way to specify the body.
```
use trillium_testing::{methods::post, TestConn};
let mut conn = post("/").with_request_body("some body");
assert_eq!(conn.take_request_body_string(), "some body");
let mut conn = TestConn::build("post", "/", "some body")
.with_request_body("once told me");
assert_eq!(conn.take_request_body_string(), "once told me");
```
*/
pub fn with_request_body(self, body: impl Into<Synthetic>) -> Self {
let mut inner: SyntheticConn = self.into();
inner.replace_body(body);
Self(inner.into())
}
/// sets the peer ip for this test conn
pub fn with_peer_ip(mut self, ip: IpAddr) -> Self {
self.inner_mut().set_peer_ip(Some(ip));
self
}
/// set the test conn to be secure
pub fn secure(mut self) -> Self {
self.inner_mut().set_secure(true);
self
}
/// set state on the test conn
pub fn with_state<S>(mut self, state: S) -> Self
where
S: Send + Sync + 'static,
{
self.0.insert_state(state);
self
}
/**
blocks on running this conn against a handler and finalizes
response headers. also aliased as [`TestConn::on`]
```
use trillium_testing::prelude::*;
async fn handler(conn: Conn) -> Conn {
conn.ok("hello trillium")
}
let conn = get("/").run(&handler);
assert_ok!(conn, "hello trillium", "content-length" => "14");
```
*/
pub fn run(self, handler: &impl Handler) -> Self {
crate::block_on(self.run_async(handler))
}
/**
runs this conn against a handler and finalizes
response headers.
```
use trillium_testing::prelude::*;
async fn handler(conn: Conn) -> Conn {
conn.ok("hello trillium")
}
block_on(async move {
let conn = get("/").run_async(&handler).await;
assert_ok!(conn, "hello trillium", "content-length" => "14");
});
```
*/
pub async fn run_async(self, handler: &impl Handler) -> Self {
let conn = handler.run(self.into()).await;
let mut conn = handler.before_send(conn).await;
conn.inner_mut().finalize_headers();
Self(conn)
}
/**
blocks on running this conn against a handler and finalizes
response headers. also aliased as [`TestConn::run`].
```
use trillium_testing::prelude::*;
async fn handler(conn: Conn) -> Conn {
conn.ok("hello trillium")
}
let conn = get("/").on(&handler);
assert_ok!(conn, "hello trillium", "content-length" => "14");
```
*/
pub fn on(self, handler: &impl Handler) -> Self {
self.run(handler)
}
/**
Reads the response body to string and returns it, if set. This is
used internally to [`assert_body`] which is the preferred
interface
*/
pub fn take_response_body_string(&mut self) -> Option<String> {
if let Some(body) = self.take_response_body() {
String::from_utf8(
futures_lite::future::block_on(body.into_bytes())
.unwrap()
.to_vec(),
)
.ok()
} else {
None
}
}
/**
Reads the request body to string and returns it
*/
pub fn take_request_body_string(&mut self) -> String {
futures_lite::future::block_on(async {
self.request_body().await.read_string().await.unwrap()
})
}
}
impl From<Conn> for TestConn {
fn from(conn: Conn) -> Self {
Self(conn)
}
}
impl From<TestConn> for Conn {
fn from(tc: TestConn) -> Self {
tc.0
}
}
impl From<TestConn> for SyntheticConn {
fn from(tc: TestConn) -> Self {
tc.0.into_inner()
}
}
impl Deref for TestConn {
type Target = Conn;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for TestConn {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}