1010use crate :: { Match , Request } ;
1111use assert_json_diff:: { assert_json_matches_no_panic, CompareMode } ;
1212use base64:: prelude:: { Engine as _, BASE64_STANDARD } ;
13- use http_types:: headers:: { HeaderName , HeaderValue , HeaderValues } ;
14- use http_types:: { Method , Url } ;
13+ use http:: { HeaderName , HeaderValue , Method } ;
1514use log:: debug;
1615use regex:: Regex ;
1716use serde:: Serialize ;
1817use serde_json:: Value ;
1918use std:: convert:: TryInto ;
20- use std:: ops:: Deref ;
2119use std:: str;
20+ use url:: Url ;
2221
2322/// Implement the `Match` trait for all closures, out of the box,
2423/// if their signature is compatible.
@@ -342,7 +341,7 @@ impl Match for PathRegexMatcher {
342341/// assert_eq!(status, 200);
343342/// }
344343/// ```
345- pub struct HeaderExactMatcher ( HeaderName , HeaderValues ) ;
344+ pub struct HeaderExactMatcher ( HeaderName , Vec < HeaderValue > ) ;
346345
347346/// Shorthand for [`HeaderExactMatcher::new`].
348347pub fn header < K , V > ( key : K , value : V ) -> HeaderExactMatcher
@@ -352,7 +351,7 @@ where
352351 V : TryInto < HeaderValue > ,
353352 <V as TryInto < HeaderValue > >:: Error : std:: fmt:: Debug ,
354353{
355- HeaderExactMatcher :: new ( key, value. try_into ( ) . map ( HeaderValues :: from ) . unwrap ( ) )
354+ HeaderExactMatcher :: new ( key, vec ! [ value] )
356355}
357356
358357/// Shorthand for [`HeaderExactMatcher::new`] supporting multi valued headers.
@@ -363,38 +362,44 @@ where
363362 V : TryInto < HeaderValue > ,
364363 <V as TryInto < HeaderValue > >:: Error : std:: fmt:: Debug ,
365364{
366- let values = values
367- . into_iter ( )
368- . filter_map ( |v| v. try_into ( ) . ok ( ) )
369- . collect :: < HeaderValues > ( ) ;
370365 HeaderExactMatcher :: new ( key, values)
371366}
372367
373368impl HeaderExactMatcher {
374- pub fn new < K , V > ( key : K , value : V ) -> Self
369+ pub fn new < K , V > ( key : K , values : Vec < V > ) -> Self
375370 where
376371 K : TryInto < HeaderName > ,
377372 <K as TryInto < HeaderName > >:: Error : std:: fmt:: Debug ,
378- V : TryInto < HeaderValues > ,
379- <V as TryInto < HeaderValues > >:: Error : std:: fmt:: Debug ,
373+ V : TryInto < HeaderValue > ,
374+ <V as TryInto < HeaderValue > >:: Error : std:: fmt:: Debug ,
380375 {
381376 let key = key. try_into ( ) . expect ( "Failed to convert to header name." ) ;
382- let value = value
383- . try_into ( )
384- . expect ( "Failed to convert to header value." ) ;
385- Self ( key, value)
377+ let values = values
378+ . into_iter ( )
379+ . map ( |value| {
380+ value
381+ . try_into ( )
382+ . expect ( "Failed to convert to header value." )
383+ } )
384+ . collect ( ) ;
385+ Self ( key, values)
386386 }
387387}
388388
389389impl Match for HeaderExactMatcher {
390390 fn matches ( & self , request : & Request ) -> bool {
391- match request. headers . get ( & self . 0 ) {
392- None => false ,
393- Some ( values) => {
394- let headers: Vec < & str > = self . 1 . iter ( ) . map ( HeaderValue :: as_str) . collect ( ) ;
395- values. eq ( headers. as_slice ( ) )
396- }
397- }
391+ let values = request
392+ . headers
393+ . get_all ( & self . 0 )
394+ . iter ( )
395+ . filter_map ( |v| v. to_str ( ) . ok ( ) )
396+ . flat_map ( |v| {
397+ v. split ( ',' )
398+ . map ( str:: trim)
399+ . filter_map ( |v| HeaderValue :: from_str ( v) . ok ( ) )
400+ } )
401+ . collect :: < Vec < _ > > ( ) ;
402+ values == self . 1 // order matters
398403 }
399404}
400405
@@ -513,12 +518,16 @@ impl HeaderRegexMatcher {
513518
514519impl Match for HeaderRegexMatcher {
515520 fn matches ( & self , request : & Request ) -> bool {
516- match request. headers . get ( & self . 0 ) {
517- None => false ,
518- Some ( values) => {
519- let has_values = values. iter ( ) . next ( ) . is_some ( ) ;
520- has_values && values. iter ( ) . all ( |v| self . 1 . is_match ( v. as_str ( ) ) )
521- }
521+ let mut it = request
522+ . headers
523+ . get_all ( & self . 0 )
524+ . iter ( )
525+ . filter_map ( |v| v. to_str ( ) . ok ( ) )
526+ . peekable ( ) ;
527+ if it. peek ( ) . is_some ( ) {
528+ it. all ( |v| self . 1 . is_match ( v) )
529+ } else {
530+ false
522531 }
523532 }
524533}
@@ -861,6 +870,64 @@ impl Match for QueryParamExactMatcher {
861870 }
862871}
863872
873+ #[ derive( Debug ) ]
874+ /// Match when a query parameter contains the specified value as a substring.
875+ ///
876+ /// ### Example:
877+ /// ```rust
878+ /// use wiremock::{MockServer, Mock, ResponseTemplate};
879+ /// use wiremock::matchers::query_param_contains;
880+ ///
881+ /// #[async_std::main]
882+ /// async fn main() {
883+ /// // Arrange
884+ /// let mock_server = MockServer::start().await;
885+ ///
886+ /// // It matches since "world" is a substring of "some_world".
887+ /// Mock::given(query_param_contains("hello", "world"))
888+ /// .respond_with(ResponseTemplate::new(200))
889+ /// .mount(&mock_server)
890+ /// .await;
891+ ///
892+ /// // Act
893+ /// let status = surf::get(format!("{}?hello=some_world", &mock_server.uri()))
894+ /// .await
895+ /// .unwrap()
896+ /// .status();
897+ ///
898+ /// // Assert
899+ /// assert_eq!(status, 200);
900+ /// }
901+ /// ```
902+ pub struct QueryParamContainsMatcher ( String , String ) ;
903+
904+ impl QueryParamContainsMatcher {
905+ /// Specify the substring that the query parameter should contain.
906+ pub fn new < K : Into < String > , V : Into < String > > ( key : K , value : V ) -> Self {
907+ let key = key. into ( ) ;
908+ let value = value. into ( ) ;
909+ Self ( key, value)
910+ }
911+ }
912+
913+ /// Shorthand for [`QueryParamContainsMatcher::new`].
914+ pub fn query_param_contains < K , V > ( key : K , value : V ) -> QueryParamContainsMatcher
915+ where
916+ K : Into < String > ,
917+ V : Into < String > ,
918+ {
919+ QueryParamContainsMatcher :: new ( key, value)
920+ }
921+
922+ impl Match for QueryParamContainsMatcher {
923+ fn matches ( & self , request : & Request ) -> bool {
924+ request
925+ . url
926+ . query_pairs ( )
927+ . any ( |q| q. 0 == self . 0 . as_str ( ) && q. 1 . contains ( self . 1 . as_str ( ) ) )
928+ }
929+ }
930+
864931#[ derive( Debug ) ]
865932/// Only match requests that do **not** contain a specified query parameter.
866933///
@@ -994,7 +1061,6 @@ where
9941061/// use wiremock::{MockServer, Mock, ResponseTemplate};
9951062/// use wiremock::matchers::basic_auth;
9961063/// use serde::{Deserialize, Serialize};
997- /// use http_types::auth::BasicAuth;
9981064/// use std::convert::TryInto;
9991065///
10001066/// #[async_std::main]
@@ -1008,10 +1074,9 @@ where
10081074/// .mount(&mock_server)
10091075/// .await;
10101076///
1011- /// let auth = BasicAuth::new("username", "password");
10121077/// let client: surf::Client = surf::Config::new()
10131078/// .set_base_url(surf::Url::parse(&mock_server.uri()).unwrap())
1014- /// .add_header(auth.name(), auth.value() ).unwrap()
1079+ /// .add_header("Authorization", "Basic dXNlcm5hbWU6cGFzc3dvcmQ=" ).unwrap()
10151080/// .try_into().unwrap();
10161081///
10171082/// // Act
@@ -1040,7 +1105,7 @@ impl BasicAuthMatcher {
10401105 pub fn from_token ( token : impl AsRef < str > ) -> Self {
10411106 Self ( header (
10421107 "Authorization" ,
1043- format ! ( "Basic {}" , token. as_ref( ) ) . deref ( ) ,
1108+ & * format ! ( "Basic {}" , token. as_ref( ) ) ,
10441109 ) )
10451110 }
10461111}
@@ -1069,7 +1134,6 @@ impl Match for BasicAuthMatcher {
10691134/// use wiremock::{MockServer, Mock, ResponseTemplate};
10701135/// use wiremock::matchers::bearer_token;
10711136/// use serde::{Deserialize, Serialize};
1072- /// use http_types::auth::BasicAuth;
10731137///
10741138/// #[async_std::main]
10751139/// async fn main() {
@@ -1098,7 +1162,7 @@ impl BearerTokenMatcher {
10981162 pub fn from_token ( token : impl AsRef < str > ) -> Self {
10991163 Self ( header (
11001164 "Authorization" ,
1101- format ! ( "Bearer {}" , token. as_ref( ) ) . deref ( ) ,
1165+ & * format ! ( "Bearer {}" , token. as_ref( ) ) ,
11021166 ) )
11031167 }
11041168}
0 commit comments