1use http::header::{CONTENT_TYPE, COOKIE};
6pub use tablo_core::protocol::{
7 FilterOption, Row, RowActions, SESSION_COOKIE, body_bytes, body_string, cookie_header,
8 field_error, filter_options, form_body, input_value, multipart_body, response_cookies,
9 row_actions, rows, session_cookie_value, set_cookie_header,
10};
11use topcoat::router::{Body, Router};
12
13#[derive(Clone)]
15pub struct TestClient<'a> {
16 router: &'a Router,
17 cookies: Vec<(String, String)>,
18 tenant: Option<uuid::Uuid>,
19}
20
21impl<'a> TestClient<'a> {
22 pub fn new(router: &'a Router) -> Self {
23 Self {
24 router,
25 cookies: Vec::new(),
26 tenant: None,
27 }
28 }
29
30 pub fn cookie(&self, name: &str, value: &str) -> Self {
33 let mut client = self.clone();
34 match client.cookies.iter_mut().find(|(kept, _)| kept == name) {
35 Some(existing) => existing.1 = value.to_string(),
36 None => client.cookies.push((name.to_string(), value.to_string())),
37 }
38 client
39 }
40
41 pub fn cookies(&self, cookies: &[(String, String)]) -> Self {
44 let mut client = self.clone();
45 for (name, value) in cookies {
46 match client.cookies.iter_mut().find(|(kept, _)| kept == name) {
47 Some(existing) => existing.1 = value.clone(),
48 None => client.cookies.push((name.clone(), value.clone())),
49 }
50 }
51 client
52 }
53
54 pub fn csrf(&self, token: &str) -> Self {
56 self.cookie(tablo_core::csrf::COOKIE_NAME, token)
57 }
58
59 pub fn tenant(&self, tenant: uuid::Uuid) -> Self {
62 let mut client = self.clone();
63 client.tenant = Some(tenant);
64 client
65 }
66
67 pub async fn get(&self, uri: &str) -> http::Response<Body> {
68 self.router
69 .handle(self.request(http::Method::GET, uri))
70 .await
71 }
72
73 pub async fn post_form(&self, uri: &str, body: String) -> http::Response<Body> {
74 let mut request = self.request(http::Method::POST, uri);
75 request.headers_mut().insert(
76 CONTENT_TYPE,
77 http::HeaderValue::from_static("application/x-www-form-urlencoded"),
78 );
79 *request.body_mut() = Body::from(body);
80 self.router.handle(request).await
81 }
82
83 pub async fn post_multipart(
84 &self,
85 uri: &str,
86 boundary: &str,
87 body: String,
88 ) -> http::Response<Body> {
89 let mut request = self.request(http::Method::POST, uri);
90 request.headers_mut().insert(
91 CONTENT_TYPE,
92 format!("multipart/form-data; boundary={boundary}")
93 .parse()
94 .expect("multipart content type"),
95 );
96 *request.body_mut() = Body::from(body);
97 self.router.handle(request).await
98 }
99
100 pub async fn post_json(&self, uri: &str, body: String, identity: &str) -> http::Response<Body> {
102 let mut request = self.request(http::Method::POST, uri);
103 request.headers_mut().insert(
104 CONTENT_TYPE,
105 http::HeaderValue::from_static("application/json"),
106 );
107 request.headers_mut().insert(
108 topcoat::router::request::IDENTITY_HEADER,
109 http::HeaderValue::from_str(identity).expect("identity header"),
110 );
111 *request.body_mut() = Body::from(body);
112 self.router.handle(request).await
113 }
114
115 fn request(&self, method: http::Method, uri: &str) -> http::Request<Body> {
116 let mut builder = http::Request::builder().method(method).uri(uri);
117 if let Some(jar) = cookie_header(
118 self.cookies
119 .iter()
120 .map(|(name, value)| (name.as_str(), value.as_str())),
121 ) {
122 builder = builder.header(COOKIE, jar);
123 }
124 let (mut parts, body) = builder.body(Body::empty()).unwrap().into_parts();
125 if let Some(tenant) = self.tenant {
126 parts.extensions.insert(tablo_core::Tenant(tenant));
127 }
128 http::Request::from_parts(parts, body)
129 }
130}