diff --git a/index.d.ts b/index.d.ts index e67d396..d1dbf4f 100644 --- a/index.d.ts +++ b/index.d.ts @@ -257,15 +257,19 @@ export declare class TestServer { process(request: TestRequest): Promise } export declare class TestRequest { - constructor(props: { method?: string, uri: string, headers?: { [key: string]: string }, body?: any }) + constructor(props: { method?: string, uri: string, headers?: { [key: string]: string }, body?: any, cookies?: Cookie[] }) get method(): string set method(method: string) get uri(): string set uri(uri: string) - insertHeader(key: string, value: string): void - appendHeader(key: string, value: string): void + get headers(): Headers + set headers(headers: Headers) + insertHeader(key: string, value: string): this + appendHeader(key: string, value: string): this get body(): Buffer set body(body: Buffer) + get cookies(): Cookies + set cookies(cookies: Cookies) } export declare class TestResponse { get status(): number @@ -273,11 +277,8 @@ export declare class TestResponse { body(): Buffer bodyAsString(): string bodyAsJson(): any - containsHeader(name: string): boolean - headerValue(name: string): string | null - headerValues(name: string): Array - headerKeys(): string[] - headersLength(): number + get headers(): Headers + get cookies(): Cookies } export class TeoError extends Error { constructor(message: string, code?: number, errors?: { [key: string]: string } | null) diff --git a/src/cookies/cookies.rs b/src/cookies/cookies.rs index aaef7ce..575e9ed 100644 --- a/src/cookies/cookies.rs +++ b/src/cookies/cookies.rs @@ -2,6 +2,7 @@ use napi::{Env, JsFunction, JsUnknown, Result}; use teo::prelude::cookies::Cookies as OriginalCookies; use super::cookie::Cookie; +#[derive(Clone)] #[napi] pub struct Cookies { original: OriginalCookies diff --git a/src/headers/headers.rs b/src/headers/headers.rs index 05d14a8..e118e86 100644 --- a/src/headers/headers.rs +++ b/src/headers/headers.rs @@ -1,6 +1,7 @@ use teo::prelude::headers::Headers as OriginalHeaders; use napi::Result; +#[derive(Clone)] #[napi] pub struct Headers { original: OriginalHeaders diff --git a/src/object/mod.rs b/src/object/mod.rs index 5e1d829..cc2d4f1 100644 --- a/src/object/mod.rs +++ b/src/object/mod.rs @@ -50,7 +50,7 @@ pub fn js_any_to_teo_value(any: JsUnknown, env: Env) -> Result { let len = object.get_array_length()?; let mut result: Vec = vec![]; for n in 0..len { - let item: JsUnknown = object.get_element(n).unwrap(); + let item: JsUnknown = object.get_element(n)?; result.push(js_any_to_teo_value(item, env)?); } Value::Array(result) diff --git a/src/test/test_request.rs b/src/test/test_request.rs index ee73aa0..fb8f728 100644 --- a/src/test/test_request.rs +++ b/src/test/test_request.rs @@ -1,20 +1,22 @@ use std::str::FromStr; use hyper::{header::{HeaderName, HeaderValue}, HeaderMap, Method}; -use napi::{bindgen_prelude::Buffer, Env, JsBuffer, JsFunction, JsObject, JsString, JsUnknown, Result, ValueType}; +use napi::{bindgen_prelude::{Buffer, FromNapiRef}, Env, JsBuffer, JsFunction, JsObject, JsString, JsUnknown, NapiRaw, Result, ValueType}; use http_body_util::Full; use bytes::Bytes; +use crate::{cookies::{Cookie, Cookies}, headers::headers::Headers}; #[napi] pub struct TestRequest { method: Method, uri: String, - headers: HeaderMap, + headers: Headers, body: Bytes, + cookies: Cookies, } #[napi] impl TestRequest { - #[napi(constructor, ts_args_type = "props: { method?: string, uri: string, headers?: { [key: string]: string }, body?: any }")] + #[napi(constructor, ts_args_type = "props: { method?: string, uri: string, headers?: { [key: string]: string }, body?: any, cookies?: Cookie[] }")] pub fn new(props: JsObject, env: Env) -> Result { let method: Option = props.get_named_property("method")?; let method = match method { @@ -42,6 +44,8 @@ impl TestRequest { }); } } + let headers = teo::prelude::headers::Headers::from(headers); + let headers = Headers::from(headers); let body: Option = props.get_named_property("body")?; let body = match body { Some(body) => { @@ -70,11 +74,23 @@ impl TestRequest { }, None => Bytes::new(), }; + let mut cookies = vec![]; + let cookies_object: Option = props.get_named_property("cookies")?; + if let Some(cookies_object) = cookies_object { + let len = cookies_object.get_array_length()?; + for n in 0..len { + let item_object: JsUnknown = cookies_object.get_element(n)?; + let cookie: &Cookie = unsafe { Cookie::from_napi_ref(env.raw(), item_object.raw())? }; + cookies.push(cookie); + } + } + let cookies = Cookies::new(Some(cookies)); Ok(Self { method, uri, headers, body, + cookies, }) } @@ -104,28 +120,26 @@ impl TestRequest { self.uri = uri; } + #[napi(getter)] + pub fn headers(&self) -> Headers { + self.headers.clone() + } + + #[napi(setter)] + pub fn set_headers(&mut self, headers: &Headers) { + self.headers = headers.clone(); + } + #[napi] - pub fn insert_header(&mut self, key: String, value: String) -> Result<()> { - self.headers.insert(match HeaderName::try_from(key) { - Ok(value) => value, - Err(_) => return Err(teo_result::Error::internal_server_error_message("cannot parse header name").into()), - }, match HeaderValue::from_str(value.as_str()) { - Ok(value) => value, - Err(_) => return Err(teo_result::Error::internal_server_error_message("cannot parse header value").into()), - }); - Ok(()) + pub fn insert_header(&mut self, key: String, value: String) -> Result<&Self> { + self.headers.set(key, value)?; + Ok(self) } #[napi] - pub fn append_header(&mut self, key: String, value: String) -> Result<()> { - self.headers.append(match HeaderName::try_from(key) { - Ok(value) => value, - Err(_) => return Err(teo_result::Error::internal_server_error_message("cannot parse header name").into()), - }, match HeaderValue::from_str(value.as_str()) { - Ok(value) => value, - Err(_) => return Err(teo_result::Error::internal_server_error_message("cannot parse header value").into()), - }); - Ok(()) + pub fn append_header(&mut self, key: String, value: String) -> Result<&Self> { + self.headers.append(key, value)?; + Ok(self) } #[napi(getter)] @@ -139,13 +153,25 @@ impl TestRequest { self.body = Bytes::copy_from_slice(&body_vec); } + #[napi(getter)] + pub fn cookies(&self) -> Cookies { + self.cookies.clone() + } + + #[napi(setter)] + pub fn set_cookies(&mut self, cookies: &Cookies) { + self.cookies = cookies.clone(); + } + pub(crate) fn to_hyper_request(&self) -> hyper::Request> { - let mut request = hyper::Request::builder() + let request = hyper::Request::builder() .method(self.method.clone()) .uri(self.uri.clone()); - for (key, value) in self.headers.iter() { - request = request.header(key.clone(), value.clone()); + let mut request = request.body(Full::new(self.body.clone())).unwrap(); + self.headers.original().extend_to(request.headers_mut()); + for cookie in self.cookies.original().iter() { + request.headers_mut().append("Cookie", HeaderValue::try_from(cookie.encoded()).unwrap()); } - request.body(Full::new(self.body.clone())).unwrap() + request } } diff --git a/src/test/test_response.rs b/src/test/test_response.rs index 80d68f1..bb296c9 100644 --- a/src/test/test_response.rs +++ b/src/test/test_response.rs @@ -1,41 +1,44 @@ use napi::{bindgen_prelude::Buffer, Env, JsFunction, JsGlobal, JsObject, JsUnknown, Result}; +use teo::server::test_response::TestResponse as OriginalTestResponse; +use crate::{cookies::Cookies, headers::headers::Headers}; #[napi] pub struct TestResponse { - teo_test_response: teo::server::test_response::TestResponse, + original: OriginalTestResponse, +} + +impl From for TestResponse { + fn from(original: OriginalTestResponse) -> Self { + Self { original } + } } #[napi] impl TestResponse { - pub(super) fn new(teo_test_response: teo::server::test_response::TestResponse) -> Self { - Self { - teo_test_response, - } - } #[napi(getter)] pub fn status(&self) -> u16 { - self.teo_test_response.status().as_u16() + self.original.status().as_u16() } #[napi(getter)] pub fn version(&self) -> String { - format!("{:?}", self.teo_test_response.version()) + format!("{:?}", self.original.version()) } #[napi] pub fn body(&self) -> Buffer { - Buffer::from(Vec::::from(self.teo_test_response.body().clone())) + Buffer::from(Vec::::from(self.original.body().clone())) } #[napi] pub fn body_as_string(&self) -> String { - self.teo_test_response.body_as_string() + self.original.body_as_string() } #[napi(ts_return_type = "any")] pub fn body_as_json(&self, env: Env) -> Result { - let string = self.teo_test_response.body_as_string(); + let string = self.original.body_as_string(); let js_string = env.create_string(&string)?; let global: JsGlobal = env.get_global()?; let json: JsObject = global.get_named_property("JSON")?; @@ -44,50 +47,13 @@ impl TestResponse { Ok(json_result) } - #[napi] - pub fn contains_header(&self, name: String) -> bool { - self.teo_test_response.headers().contains_key(name.as_str()) - } - - #[napi] - pub fn header_value(&self, name: String) -> Result> { - let header_value = self.teo_test_response.headers().get(name.as_str()); - match header_value { - None => Ok(None), - Some(header_value) => { - let header_value = header_value.to_str().map_err(|_| { - teo_result::Error::internal_server_error_message(format!("cannot read request header value: {}", name)) - })?; - Ok(Some(header_value)) - } - } - } - - #[napi] - pub fn header_values(&self, name: String) -> Result> { - let header_values = self.teo_test_response.headers().get_all(name.as_str()); - let mut result = Vec::new(); - for header_value in header_values { - let header_value = header_value.to_str().map_err(|_| { - teo_result::Error::internal_server_error_message(format!("cannot read request header value: {}", name)) - })?; - result.push(header_value); - } - Ok(result) - } - - #[napi(js_name = "headerKeys", ts_return_type = "string[]")] - pub fn header_keys(&self) -> Vec<&str> { - let header_map = self.teo_test_response.headers(); - let mut result = vec![]; - header_map.keys().for_each(|k| { - result.push(k.as_str()); - }); - result + #[napi(getter)] + pub fn headers(&self) -> Headers { + Headers::from(self.original.headers().clone()) } - #[napi] - pub fn headers_length(&self) -> i64 { - self.teo_test_response.headers().len() as i64 + #[napi(getter)] + pub fn cookies(&self) -> Cookies { + Cookies::from(self.original.cookies().clone()) } } \ No newline at end of file diff --git a/src/test/test_server.rs b/src/test/test_server.rs index 408fe18..decd91c 100644 --- a/src/test/test_server.rs +++ b/src/test/test_server.rs @@ -37,6 +37,6 @@ impl TestServer { pub async fn process(&self, request: &TestRequest) -> Result { let hyper_request = request.to_hyper_request(); let response = self.server.process_test_request_with_hyper_request(hyper_request).await?; - Ok(TestResponse::new(response)) + Ok(TestResponse::from(response)) } } \ No newline at end of file