From 19e65cab3a7b2dbf07af9e288cb4192d332860b1 Mon Sep 17 00:00:00 2001 From: Lorenzo Felletti Date: Tue, 10 Dec 2024 20:52:35 +0000 Subject: [PATCH] test: write test for _request macro --- src/http/macros.rs | 276 +++++++++++++++++++++++++++++++++------------ 1 file changed, 203 insertions(+), 73 deletions(-) diff --git a/src/http/macros.rs b/src/http/macros.rs index 8049b0c..36e4774 100644 --- a/src/http/macros.rs +++ b/src/http/macros.rs @@ -4,30 +4,31 @@ /// /// # Examples /// ## Example GET request -/// ```no_run +/// ``` /// # extern crate alloc; /// # use psp_net::request; /// # use alloc::string::String; /// let req = request! { /// "www.example.com" get "/", -/// "User-Agent" => "Mozilla/5.0", +/// "User-Agent" => "Mozilla/5.0"; /// }; /// ``` /// /// ## Example POST request -/// ```no_run +/// ``` /// # extern crate alloc; /// # use psp_net::request; /// # use alloc::string::String; /// # let body = "test".to_string(); /// let req = request! { /// "www.example.com" post "/users/create" ContentType::ApplicationJson, -/// body body +/// body body, +/// "User-Agent" => (format!("Mozilla/5.0 ({})", "test")); /// }; /// ``` /// /// ## Example With HTTP 1.0 -/// ```no_run +/// ``` /// # extern crate alloc; /// # use psp_net::request; /// # use alloc::string::String; @@ -38,7 +39,7 @@ /// ``` /// /// ## Example With Formatted Header -/// ```no_run +/// ``` /// # extern crate alloc; /// # use psp_net::request; /// # use alloc::string::String; @@ -46,20 +47,21 @@ /// "www.example.com" get "/", /// /// enclose the header value in parentheses if it is not /// /// a string, or more specifically a single token tree (tt). -/// "User-Agent" => (format!("Mozilla/5.0 ({})", "test")), +/// "User-Agent" => (format!("Mozilla/5.0 ({})", "test")); /// }; /// ``` #[macro_export] macro_rules! request { + // get, no authorization ( $host:tt get $path:tt $(; $http_version:expr)?, - $($header:expr => $value:expr,)* + $($header:expr => $value:expr;)* ) => { { - use alloc::string::{String, ToString}; + use alloc::string::ToString; use alloc::vec::Vec; use alloc::vec as a_vec; - use psp_net::some_or_none; + let http_ver = $crate::some_or_none!($($http_version)?).unwrap_or($crate::http::HttpVersion::V1_1); $crate::_request! { http_version http_ver, @@ -67,38 +69,87 @@ macro_rules! request { path $path, method $crate::http::Method::Get, auth $crate::http::types::Authorization::None, - $($header => $value,)* + body Vec::new(), + $($header => $value;)* } } }; + // get, with authorization ( $host:tt get $path:tt $(; $http_version:expr)?, - $(authorization $auth:expr,)? - $($header:expr => $value:expr,)* + authorization $auth:expr, + $($header:expr => $value:expr;)* ) => { { - use alloc::string::{String, ToString}; + use alloc::string::ToString; use alloc::vec::Vec; use alloc::vec as a_vec; - use psp_net::some_or_none; - let auth = some_or_none!($($auth)?).unwrap_or($crate::http::types::Authorization::None); - $crate::http::Request { - method: $crate::http::Method::Get, - path: $path.to_string(), - headers: a_vec![("Host".to_string(), $host.to_string()), $(($header.to_string(), $value.to_string()),)*], - authorization: auth, - content_type: None, - body: Vec::new(), - http_version: $crate::some_or_none!($($http_version)?).unwrap_or($crate::http::HttpVersion::V1_1), + + let http_ver = $crate::some_or_none!($($http_version)?).unwrap_or($crate::http::HttpVersion::V1_1); + $crate::_request! { + http_version http_ver, + host $host, + path $path, + method $crate::http::Method::Get, + auth $auth, + body Vec::new(), + $($header => $value;)* + } + } + }; + + // post, no authorization + ( + $host:tt post $path:tt $($content_type:expr)? $(; $http_version:expr)?, + body $body:expr, + $($header:expr => $value:expr;)* + ) => { + { + let http_ver = $crate::some_or_none!($($http_version)?).unwrap_or($crate::http::HttpVersion::V1_1); + $crate::_request! { + http_version http_ver, + host $host, + path $path, + method $crate::http::Method::Post, + auth $crate::http::types::Authorization::None, + content_type $crate::some_or_none!($($content_type)?), + body $body, + $($header => $value;)* } } }; + // post, with authorization ( $host:tt post $path:tt $($content_type:expr)? $(; $http_version:expr)?, - $(authorization $auth:expr,)? - $($header:tt => $value:tt),* + authorization $auth:expr, + body $body:expr, + $($header:expr => $value:expr;)* + ) => { + { + use alloc::string::ToString; + use alloc::vec::Vec; + use alloc::vec as a_vec; + use psp_net::some_or_none; + let http_ver = $crate::some_or_none!($($http_version)?).unwrap_or($crate::http::HttpVersion::V1_1); + $crate::_request! { + http_version http_ver, + host $host, + path $path, + method $crate::http::Method::Post, + auth: $auth, + content_type $crate::some_or_none!($($content_type)?), + body $body, + $($header => $value;)* + }, + } + }; + + // put, no authorization + ( + $host:tt put $path:tt $($content_type:expr)? $(; $http_version:expr)?, + $($header:expr => $value:expr;)* $(body $body:expr)? ) => { { @@ -107,23 +158,27 @@ macro_rules! request { use alloc::vec as a_vec; use psp_net::some_or_none; let auth = some_or_none!($($auth)?).unwrap_or($crate::http::types::Authorization::None); + let body = $crate::some_or_none!($($body)?).unwrap_or(Vec::new()); + let http_ver = $crate::some_or_none!($($http_version)?).unwrap_or($crate::http::HttpVersion::V1_1); $crate::http::Request { - method: $crate::http::Method::Post, - path: $path.to_string(), - headers: a_vec![("Host".to_string(), $host.to_string()), $(($header.to_string(), $value.to_string()),)*], - authorization: auth, + http_version: http_ver, + host: $host, + path: $path, + method: $crate::http::Method::Put, + authorization: $crate::http::types::Authorization::None, content_type: $crate::some_or_none!($($content_type)?), - body: $crate::some_or_none!($($body)?).unwrap_or(Vec::new()), - http_version: $crate::some_or_none!($($http_version)?).unwrap_or($crate::http::HttpVersion::V1_1), + body: $body, + $($header => $value;)* } } }; + // put, with authorization ( $host:tt put $path:tt $($content_type:expr)? $(; $http_version:expr)?, - $(authorization $auth:expr,)? - $($header:tt => $value:tt),* - $(body $body:expr)? + authorization $auth:expr, + $($header:expr => $value:expr;)* + body $body:expr, ) => { { use alloc::string::ToString; @@ -131,22 +186,25 @@ macro_rules! request { use alloc::vec as a_vec; use psp_net::some_or_none; let auth = some_or_none!($($auth)?).unwrap_or($crate::http::types::Authorization::None); + let body = $crate::some_or_none!($($body)?).unwrap_or(Vec::new()); + let http_ver = $crate::some_or_none!($($http_version)?).unwrap_or($crate::http::HttpVersion::V1_1); $crate::http::Request { + http_version: http_ver, + host: $host, + path: $path, method: $crate::http::Method::Put, - path: $path.to_string(), - headers: a_vec![("Host".to_string(), $host.to_string()), $(($header.to_string(), $value.to_string()),)*], - authorization: auth, + authorization: $auth, content_type: $crate::some_or_none!($($content_type)?), - body: $crate::some_or_none!($($body)?).unwrap_or(Vec::new()), - http_version: $crate::some_or_none!($($http_version)?).unwrap_or($crate::http::HttpVersion::V1_1), + body: $body, + $($header => $value;)* } } }; + // delete, no authorization ( $host:tt delete $path:tt $($content_type:expr)? $(; $http_version:expr)?, - $(authorization $auth:expr,)? - $($header:tt => $value:tt),* + $($header:expr => $value:expr;)* $(body $body:expr)? ) => { { @@ -155,17 +213,21 @@ macro_rules! request { use alloc::vec as a_vec; use psp_net::some_or_none; let auth = some_or_none!($($auth)?).unwrap_or($crate::http::types::Authorization::None); + let http_ver = $crate::some_or_none!($($http_version)?).unwrap_or($crate::http::HttpVersion::V1_1); $crate::http::Request { + http_version: http_ver, + host: $host, + path: $path, method: $crate::http::Method::Delete, - path: $path.to_string(), - headers: a_vec![("Host".to_string(), $host.to_string()), $(($header.to_string(), $value.to_string()),)*], - authorization: auth, + authorization: $auth, content_type: $crate::some_or_none!($($content_type)?), - body: $crate::some_or_none!($($body)?).unwrap_or(Vec::new()), - http_version: $crate::some_or_none!($($http_version)?).unwrap_or($crate::http::HttpVersion::V1_1), + body: body, + $($header => $value;)* } } }; + + // delete, with authorization } #[macro_export] @@ -188,27 +250,10 @@ macro_rules! parse_response { #[macro_export] #[doc(hidden)] +/// Internal macro to create a [`crate::http::Request`]. +/// It is not intended to be used directly, but serves as a support to [`request!`] macro. macro_rules! _request { - // no body, no content type - ( - http_version $http_version:expr, - host $host:tt, - path $path:tt, - method $method:expr, - auth $auth:expr, - $($header:expr => $value:expr,)* - ) => { - $crate::http::Request { - method: $crate::http::Method::Get, - path: $path.to_string(), - headers: a_vec![("Host".to_string(), $host.to_string()), $(($header.to_string(), $value.to_string()),)*], - authorization: $auth, - content_type: None, - body: Vec::new(), - http_version: $http_version, - } - }; - // body, no content type + // no content type ( http_version $http_version:expr, host $host:tt, @@ -216,8 +261,10 @@ macro_rules! _request { method $method:expr, auth $auth:expr, body $body:expr, - $($header:expr => $value:expr,)* - ) => { + $($header:expr => $value:expr;)* + ) => {{ + use alloc::string::ToString; + use alloc::vec as a_vec; $crate::http::Request { method: $method, path: $path.to_string(), @@ -227,8 +274,8 @@ macro_rules! _request { body: $body, http_version: $http_version, } - }; - // body and content type + }}; + // content type ( http_version $http_version:expr, host $host:tt, @@ -237,8 +284,10 @@ macro_rules! _request { auth $auth:expr, content_type $content_type:expr, body $body:expr, - $($header:expr => $value:expr,)* - ) => { + $($header:expr => $value:expr;)* + ) => {{ + use alloc::string::ToString; + use alloc::vec as a_vec; $crate::http::Request { method: $method, path: $path.to_string(), @@ -248,5 +297,86 @@ macro_rules! _request { body: $body, http_version: $http_version, } + }}; +} + +#[cfg(test)] +mod test { + use crate::http::{ + types::{Authorization, BasicAuthorization, ContentType}, + HttpVersion, Method, }; + + #[test] + fn test_get_request_no_authorization() { + let req = request! { + "www.example.com" get "/", + "User-Agent" => "Mozilla/5.0"; + }; + assert_eq!( + req.to_string(), + "GET / HTTP/1.1\nHost: www.example.com\nUser-Agent: Mozilla/5.0\n\n" + ); + } + + #[test] + fn test_get_request_with_authorization() { + let auth = Authorization::Basic(BasicAuthorization::new_encoded("dXNlcjpwYXNzd29yZA==")); + let req = request! { + "www.example.com" get "/", + authorization auth, + "User-Agent" => "Mozilla/5.0"; + }; + assert_eq!( + req.to_string(), + "GET / HTTP/1.1\nAuthorization: Basic dXNlcjpwYXNzd29yZA==\nHost: www.example.com\nUser-Agent: Mozilla/5.0\n\n" + ); + } + + fn test_post_request_no_authorization() { + let ct = ContentType::ApplicationJson; + let hv = HttpVersion::V1_1; + let body = Vec::new(); + let req = request! { + "www.example.com" post "/test" ct; hv, + body body, + "User-Agent" => "Mozilla/5.0"; + }; + assert_eq!( + req.to_string(), + "POST /users/create HTTP/1.1\nHost: www.example.com\nUser-Agent: Mozilla/5.0\n\n" + ); + } + + /// Test the macro [`_request!`] that is used internally as a support to [`request!`] + /// macro to create a [`Request`](crate::http::Request). + fn test_internal_request() { + // no content-type + let req = _request! { + http_version HttpVersion::V1_1, + host "www.example.com", + path "/", + method Method::Get, + auth Authorization::None, + body Vec::new(), + }; + + assert_eq!(req.to_string(), "GET / HTTP/1.1\nHost: www.example.com\n\n"); + + // content-type + let req = _request! { + http_version HttpVersion::V1_1, + host "www.example.com", + path "/", + method Method::Get, + auth Authorization::None, + content_type Some(ContentType::ApplicationJson), + body Vec::new(), + }; + + assert_eq!( + req.to_string(), + "GET / HTTP/1.1\nHost: www.example.com\nContent-Type: application/json\n\n" + ); + } }