10
10
use crate :: { Match , Request } ;
11
11
use assert_json_diff:: { assert_json_matches_no_panic, CompareMode } ;
12
12
use 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 } ;
15
14
use log:: debug;
16
15
use regex:: Regex ;
17
16
use serde:: Serialize ;
18
17
use serde_json:: Value ;
19
18
use std:: convert:: TryInto ;
20
- use std:: ops:: Deref ;
21
19
use std:: str;
20
+ use url:: Url ;
22
21
23
22
/// Implement the `Match` trait for all closures, out of the box,
24
23
/// if their signature is compatible.
@@ -342,7 +341,7 @@ impl Match for PathRegexMatcher {
342
341
/// assert_eq!(status, 200);
343
342
/// }
344
343
/// ```
345
- pub struct HeaderExactMatcher ( HeaderName , HeaderValues ) ;
344
+ pub struct HeaderExactMatcher ( HeaderName , Vec < HeaderValue > ) ;
346
345
347
346
/// Shorthand for [`HeaderExactMatcher::new`].
348
347
pub fn header < K , V > ( key : K , value : V ) -> HeaderExactMatcher
@@ -352,7 +351,7 @@ where
352
351
V : TryInto < HeaderValue > ,
353
352
<V as TryInto < HeaderValue > >:: Error : std:: fmt:: Debug ,
354
353
{
355
- HeaderExactMatcher :: new ( key, value. try_into ( ) . map ( HeaderValues :: from ) . unwrap ( ) )
354
+ HeaderExactMatcher :: new ( key, vec ! [ value] )
356
355
}
357
356
358
357
/// Shorthand for [`HeaderExactMatcher::new`] supporting multi valued headers.
@@ -363,38 +362,44 @@ where
363
362
V : TryInto < HeaderValue > ,
364
363
<V as TryInto < HeaderValue > >:: Error : std:: fmt:: Debug ,
365
364
{
366
- let values = values
367
- . into_iter ( )
368
- . filter_map ( |v| v. try_into ( ) . ok ( ) )
369
- . collect :: < HeaderValues > ( ) ;
370
365
HeaderExactMatcher :: new ( key, values)
371
366
}
372
367
373
368
impl HeaderExactMatcher {
374
- pub fn new < K , V > ( key : K , value : V ) -> Self
369
+ pub fn new < K , V > ( key : K , values : Vec < V > ) -> Self
375
370
where
376
371
K : TryInto < HeaderName > ,
377
372
<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 ,
380
375
{
381
376
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)
386
386
}
387
387
}
388
388
389
389
impl Match for HeaderExactMatcher {
390
390
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
398
403
}
399
404
}
400
405
@@ -513,12 +518,16 @@ impl HeaderRegexMatcher {
513
518
514
519
impl Match for HeaderRegexMatcher {
515
520
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
522
531
}
523
532
}
524
533
}
@@ -861,6 +870,64 @@ impl Match for QueryParamExactMatcher {
861
870
}
862
871
}
863
872
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
+
864
931
#[ derive( Debug ) ]
865
932
/// Only match requests that do **not** contain a specified query parameter.
866
933
///
@@ -994,7 +1061,6 @@ where
994
1061
/// use wiremock::{MockServer, Mock, ResponseTemplate};
995
1062
/// use wiremock::matchers::basic_auth;
996
1063
/// use serde::{Deserialize, Serialize};
997
- /// use http_types::auth::BasicAuth;
998
1064
/// use std::convert::TryInto;
999
1065
///
1000
1066
/// #[async_std::main]
@@ -1008,10 +1074,9 @@ where
1008
1074
/// .mount(&mock_server)
1009
1075
/// .await;
1010
1076
///
1011
- /// let auth = BasicAuth::new("username", "password");
1012
1077
/// let client: surf::Client = surf::Config::new()
1013
1078
/// .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()
1015
1080
/// .try_into().unwrap();
1016
1081
///
1017
1082
/// // Act
@@ -1040,7 +1105,7 @@ impl BasicAuthMatcher {
1040
1105
pub fn from_token ( token : impl AsRef < str > ) -> Self {
1041
1106
Self ( header (
1042
1107
"Authorization" ,
1043
- format ! ( "Basic {}" , token. as_ref( ) ) . deref ( ) ,
1108
+ & * format ! ( "Basic {}" , token. as_ref( ) ) ,
1044
1109
) )
1045
1110
}
1046
1111
}
@@ -1069,7 +1134,6 @@ impl Match for BasicAuthMatcher {
1069
1134
/// use wiremock::{MockServer, Mock, ResponseTemplate};
1070
1135
/// use wiremock::matchers::bearer_token;
1071
1136
/// use serde::{Deserialize, Serialize};
1072
- /// use http_types::auth::BasicAuth;
1073
1137
///
1074
1138
/// #[async_std::main]
1075
1139
/// async fn main() {
@@ -1098,7 +1162,7 @@ impl BearerTokenMatcher {
1098
1162
pub fn from_token ( token : impl AsRef < str > ) -> Self {
1099
1163
Self ( header (
1100
1164
"Authorization" ,
1101
- format ! ( "Bearer {}" , token. as_ref( ) ) . deref ( ) ,
1165
+ & * format ! ( "Bearer {}" , token. as_ref( ) ) ,
1102
1166
) )
1103
1167
}
1104
1168
}
0 commit comments