diff --git a/rust/Cargo.lock b/rust/Cargo.lock index bb0768afe..6a56f923d 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -155,9 +155,9 @@ version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" dependencies = [ - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -166,9 +166,9 @@ version = "0.1.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -592,10 +592,10 @@ checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" dependencies = [ "fnv", "ident_case", - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "quote 1.0.36", "strsim 0.10.0", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -606,7 +606,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", "quote 1.0.36", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -625,7 +625,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a2c35ab6e03642397cdda1dd58abbc05d418aef8e36297f336d5aba060fe8df" dependencies = [ - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "quote 1.0.36", "syn 1.0.109", ] @@ -864,9 +864,9 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -1161,9 +1161,9 @@ dependencies = [ [[package]] name = "hyper" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f24ce812868d86d19daa79bf3bf9175bc44ea323391147a5e3abde2a283871b" +checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" dependencies = [ "bytes", "futures-channel", @@ -1202,7 +1202,7 @@ checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" dependencies = [ "futures-util", "http 1.1.0", - "hyper 1.3.0", + "hyper 1.3.1", "hyper-util", "rustls 0.22.3", "rustls-pki-types", @@ -1234,7 +1234,7 @@ dependencies = [ "futures-util", "http 1.1.0", "http-body 1.0.0", - "hyper 1.3.0", + "hyper 1.3.1", "pin-project-lite", "socket2", "tokio", @@ -1492,10 +1492,10 @@ checksum = "dc487311295e0002e452025d6b580b77bb17286de87b57138f3b5db711cded68" dependencies = [ "beef", "fnv", - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "quote 1.0.36", "regex-syntax 0.6.29", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -1662,7 +1662,7 @@ version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "197eff6c12b80ff5de6173e438fa3c1340a9e708118c1626e690f65aee1e5332" dependencies = [ - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "quote 1.0.36", "syn 1.0.109", ] @@ -1674,7 +1674,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef492b5cf80f90c050b287e747228a1fa6517e9d754f364b5a7e0e038e49a25f" dependencies = [ "proc-macro-crate", - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "quote 1.0.36", "syn 1.0.109", ] @@ -2162,7 +2162,7 @@ dependencies = [ "bytes", "chrono", "chrono-tz 0.8.6", - "env_logger 0.10.2", + "env_logger 0.11.3", "expectest", "fs2", "gregorian", @@ -2185,7 +2185,7 @@ dependencies = [ "regex", "regex-syntax 0.6.29", "reqwest 0.11.27", - "rstest 0.17.0", + "rstest 0.19.0", "semver", "serde", "serde_json", @@ -2402,9 +2402,9 @@ version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -2459,8 +2459,8 @@ version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ac2cf0f2e4f42b49f5ffd07dae8d746508ef7526c13940e5f524012ae6c6550" dependencies = [ - "proc-macro2 1.0.80", - "syn 2.0.59", + "proc-macro2 1.0.81", + "syn 2.0.60", ] [[package]] @@ -2483,9 +2483,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.80" +version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56dea16b0a29e94408b9aa5e2940a4eedbd128a1ba20e8f7ae60fd3d465af0e" +checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" dependencies = [ "unicode-ident", ] @@ -2517,7 +2517,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.59", + "syn 2.0.60", "tempfile", ] @@ -2529,9 +2529,9 @@ checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" dependencies = [ "anyhow", "itertools 0.12.1", - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -2578,7 +2578,7 @@ version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", ] [[package]] @@ -2759,7 +2759,7 @@ dependencies = [ "http 1.1.0", "http-body 1.0.0", "http-body-util", - "hyper 1.3.0", + "hyper 1.3.1", "hyper-rustls 0.26.0", "hyper-util", "ipnet", @@ -2804,18 +2804,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rstest" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de1bb486a691878cd320c2f0d319ba91eeaa2e894066d8b5f8f117c000e9d962" -dependencies = [ - "futures", - "futures-timer", - "rstest_macros 0.17.0", - "rustc_version", -] - [[package]] name = "rstest" version = "0.18.2" @@ -2840,20 +2828,6 @@ dependencies = [ "rustc_version", ] -[[package]] -name = "rstest_macros" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290ca1a1c8ca7edb7c3283bd44dc35dd54fdec6253a3912e201ba1072018fca8" -dependencies = [ - "cfg-if", - "proc-macro2 1.0.80", - "quote 1.0.36", - "rustc_version", - "syn 1.0.109", - "unicode-ident", -] - [[package]] name = "rstest_macros" version = "0.18.2" @@ -2862,12 +2836,12 @@ checksum = "d428f8247852f894ee1be110b375111b586d4fa431f6c46e64ba5a0dcccbe605" dependencies = [ "cfg-if", "glob", - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "quote 1.0.36", "regex", "relative-path", "rustc_version", - "syn 2.0.59", + "syn 2.0.60", "unicode-ident", ] @@ -2879,12 +2853,12 @@ checksum = "04a9df72cc1f67020b0d63ad9bfe4a323e459ea7eb68e03bd9824db49f9a4c25" dependencies = [ "cfg-if", "glob", - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "quote 1.0.36", "regex", "relative-path", "rustc_version", - "syn 2.0.59", + "syn 2.0.60", "unicode-ident", ] @@ -3090,22 +3064,22 @@ checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" [[package]] name = "serde" -version = "1.0.197" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" +checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.197" +version = "1.0.198" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" +checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" dependencies = [ - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -3165,9 +3139,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6561dc161a9224638a31d876ccdfefbc1df91d3f3a8342eddb35f055d48c7655" dependencies = [ "darling", - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -3356,18 +3330,18 @@ version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "quote 1.0.36", "unicode-ident", ] [[package]] name = "syn" -version = "2.0.59" +version = "2.0.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a" +checksum = "909518bc7b1c9b779f1bbf07f2929d35af9f0f37e47c6e9ef7f9dddc1e1821f3" dependencies = [ - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "quote 1.0.36", "unicode-ident", ] @@ -3380,9 +3354,9 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" [[package]] name = "sysinfo" -version = "0.30.10" +version = "0.30.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d7c217777061d5a2d652aea771fb9ba98b6dade657204b08c4b9604d11555b" +checksum = "87341a165d73787554941cd5ef55ad728011566fe714e987d1b976c15dbc3a83" dependencies = [ "cfg-if", "core-foundation-sys", @@ -3452,7 +3426,7 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877189d680101869f65ef94168105d6c188b3a143c13a2d42cf8a09c4c704f8a" dependencies = [ - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "quote 1.0.36", "syn 1.0.109", ] @@ -3474,9 +3448,9 @@ version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c8f546451eaa38373f549093fe9fd05e7d2bade739e2ddf834b9968621d60107" dependencies = [ - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -3503,9 +3477,9 @@ version = "1.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -3601,9 +3575,9 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -3674,7 +3648,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.9", + "toml_edit 0.22.11", ] [[package]] @@ -3699,9 +3673,9 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.9" +version = "0.22.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" +checksum = "fb686a972ccef8537b39eead3968b0e8616cb5040dbb9bba93007c8e07c9215f" dependencies = [ "indexmap 2.2.6", "serde", @@ -3771,10 +3745,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d021fc044c18582b9a2408cd0dd05b1596e3ecdb5c4df822bb0183545683889" dependencies = [ "prettyplease", - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "prost-build", "quote 1.0.36", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -3784,10 +3758,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4ef6dd70a610078cb4e338a0f79d06bc759ff1b22d2120c2ff02ae264ba9c2" dependencies = [ "prettyplease", - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "prost-build", "quote 1.0.36", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -3852,9 +3826,9 @@ version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.59", + "syn 2.0.60", ] [[package]] @@ -3936,7 +3910,7 @@ dependencies = [ "serde", "shlex", "snapbox", - "toml_edit 0.22.9", + "toml_edit 0.22.11", ] [[package]] @@ -4065,7 +4039,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d257817081c7dffcdbab24b9e62d2def62e2ff7d00b1c20062551e6cccc145ff" dependencies = [ - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "quote 1.0.36", ] @@ -4122,9 +4096,9 @@ dependencies = [ "bumpalo", "log", "once_cell", - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.59", + "syn 2.0.60", "wasm-bindgen-shared", ] @@ -4156,9 +4130,9 @@ version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ - "proc-macro2 1.0.80", + "proc-macro2 1.0.81", "quote 1.0.36", - "syn 2.0.59", + "syn 2.0.60", "wasm-bindgen-backend", "wasm-bindgen-shared", ] diff --git a/rust/pact_consumer/src/builders/request_builder.rs b/rust/pact_consumer/src/builders/request_builder.rs index 9779a578a..d6b4e05ec 100644 --- a/rust/pact_consumer/src/builders/request_builder.rs +++ b/rust/pact_consumer/src/builders/request_builder.rs @@ -132,7 +132,7 @@ impl RequestBuilder { .get_defaulting() .entry(key.clone()) .or_insert_with(Default::default) - .push(value.to_example()); + .push(Some(value.to_example())); let mut path = DocPath::root(); path.push_field(key); diff --git a/rust/pact_ffi/src/log/sink.rs b/rust/pact_ffi/src/log/sink.rs index b7d248730..3e1b7f7ca 100644 --- a/rust/pact_ffi/src/log/sink.rs +++ b/rust/pact_ffi/src/log/sink.rs @@ -17,6 +17,7 @@ use crate::log::inmem_buffer::InMemBuffer; /// A sink for logs to be written to, based on a provider specifier. #[derive(Debug)] +#[allow(dead_code)] pub(crate) enum Sink { /// Write logs to stdout. Stdout(Stdout), diff --git a/rust/pact_ffi/src/mock_server/handles.rs b/rust/pact_ffi/src/mock_server/handles.rs index 392fbd756..0519f97d2 100644 --- a/rust/pact_ffi/src/mock_server/handles.rs +++ b/rust/pact_ffi/src/mock_server/handles.rs @@ -771,11 +771,11 @@ pub extern fn pactffi_with_query_parameter( if index >= values.len() { values.resize_with(index + 1, Default::default); } - values[index] = value; + values[index] = Some(value); } else { - let mut values: Vec = Vec::new(); + let mut values: Vec> = Vec::new(); values.resize_with(index + 1, Default::default); - values[index] = value; + values[index] = Some(value); q.insert(name.to_string(), values); }; q @@ -784,9 +784,9 @@ pub extern fn pactffi_with_query_parameter( path.push_field(name).push_index(index); #[allow(deprecated)] let value = from_integration_json(&mut reqres.request.matching_rules, &mut reqres.request.generators, &value.to_string(), path, "query"); - let mut values: Vec = Vec::new(); + let mut values: Vec> = Vec::new(); values.resize_with(index + 1, Default::default); - values[index] = value; + values[index] = Some(value); Some(hashmap! { name.to_string() => values }) }); !mock_server_started @@ -839,8 +839,12 @@ pub extern fn pactffi_with_query_parameter( /// const char* value = "{\"value\":[\"2\"], \"pact:matcher:type\":\"regex\", \"regex\":\"\\\\d+\"}"; /// pactffi_with_query_parameter_v2(handle, "id", 0, value); /// ``` +/// +/// For a query parameter with no value, the value parameter can be set to a NULL pointer. +/// /// # Safety -/// The name and value parameters must be valid pointers to NULL terminated strings. +/// The name parameter must be a valid pointer to a NULL terminated string. If the value +/// parameter is not NULL, it must point to a valid NULL terminated string. /// ``` #[no_mangle] pub extern fn pactffi_with_query_parameter_v2( @@ -850,7 +854,7 @@ pub extern fn pactffi_with_query_parameter_v2( value: *const c_char ) -> bool { if let Some(name) = convert_cstr("name", name) { - let value = convert_cstr("value", value).unwrap_or_default(); + let value = convert_cstr("value", value); trace!(?interaction, name, index, value, "pactffi_with_query_parameter_v2 called"); interaction.with_interaction(&|_, mock_server_started, inner| { if let Some(reqres) = inner.as_v4_http_mut() { @@ -860,31 +864,38 @@ pub extern fn pactffi_with_query_parameter_v2( path.push_index(index); } - let value = from_integration_json_v2( - &mut reqres.request.matching_rules, - &mut reqres.request.generators, - value, - path, - "query", - index - ); - match value { - Either::Left(value) => { - reqres.request.query = update_query_map(index, name, reqres, &value); - } - Either::Right(values) => if index == 0 { - reqres.request.query = reqres.request.query.clone().map(|mut q| { - if q.contains_key(name) { - let vec = q.get_mut(name).unwrap(); - vec.extend_from_slice(&values); - } else { - q.insert(name.to_string(), values.clone()); - }; - q - }).or_else(|| Some(hashmap! { name.to_string() => values })) - } else { - reqres.request.query = update_query_map(index, name, reqres, &values.first().cloned().unwrap_or_default()); + if let Some(value) = value { + let value = from_integration_json_v2( + &mut reqres.request.matching_rules, + &mut reqres.request.generators, + value, + path, + "query", + index + ); + match value { + Either::Left(value) => { + reqres.request.query = update_query_map(index, name, reqres, Some(value)); + } + Either::Right(values) => if index == 0 { + reqres.request.query = reqres.request.query.clone().map(|mut q| { + let values = values.iter().map(|v| Some(v.clone())).collect_vec(); + if q.contains_key(name) { + let vec = q.get_mut(name).unwrap(); + vec.extend_from_slice(&values); + } else { + q.insert(name.to_string(), values); + }; + q + }).or_else(|| Some(hashmap! { + name.to_string() => values.iter().map(|v| Some(v.clone())).collect_vec() + })) + } else { + reqres.request.query = update_query_map(index, name, reqres, values.first().cloned()); + } } + } else { + reqres.request.query = update_query_map(index, name, reqres, None); } !mock_server_started } else { @@ -898,25 +909,29 @@ pub extern fn pactffi_with_query_parameter_v2( } } -fn update_query_map(index: size_t, name: &str, reqres: &mut SynchronousHttp, value: &String) -> Option>> { +fn update_query_map( + index: size_t, + name: &str, + reqres: &mut SynchronousHttp, + value: Option +) -> Option>>> { reqres.request.query.clone().map(|mut q| { - if q.contains_key(name) { - let values = q.get_mut(name).unwrap(); - if index >= values.len() { - values.resize_with(index + 1, Default::default); + if let Some(entry) = q.get_mut(name) { + if index >= entry.len() { + entry.resize_with(index + 1, Default::default); } - values[index] = value.clone(); + entry[index] = value.clone(); } else { - let mut values: Vec = Vec::new(); + let mut values: Vec> = Vec::new(); values.resize_with(index + 1, Default::default); values[index] = value.clone(); q.insert(name.to_string(), values); }; q }).or_else(|| { - let mut values: Vec = Vec::new(); + let mut values: Vec> = Vec::new(); values.resize_with(index + 1, Default::default); - values[index] = value.clone(); + values[index] = value; Some(hashmap! { name.to_string() => values }) }) } @@ -2866,7 +2881,7 @@ mod tests { pactffi_free_pact_handle(pact_handle); expect!(interaction.request.query.clone()).to(be_some().value(hashmap!{ - "id".to_string() => vec!["100".to_string()] + "id".to_string() => vec![Some("100".to_string())] })); expect!(interaction.request.matching_rules.rules.get(&Category::QUERY).cloned().unwrap_or_default().is_empty()).to(be_true()); } @@ -2888,7 +2903,7 @@ mod tests { pactffi_free_pact_handle(pact_handle); expect!(interaction.request.query.clone()).to(be_some().value(hashmap!{ - "id".to_string() => vec!["100".to_string()] + "id".to_string() => vec![Some("100".to_string())] })); expect!(&interaction.request.matching_rules).to(be_equal_to(&matchingrules! { "query" => { "$.id" => [ MatchingRule::Regex("\\d+".to_string()) ] } @@ -2912,7 +2927,7 @@ mod tests { pactffi_free_pact_handle(pact_handle); expect!(interaction.request.query.clone()).to(be_some().value(hashmap!{ - "id".to_string() => vec!["1".to_string(), "2".to_string()] + "id".to_string() => vec![Some("1".to_string()), Some("2".to_string())] })); expect!(interaction.request.matching_rules.rules.get(&Category::QUERY).cloned().unwrap_or_default().is_empty()).to(be_true()); } @@ -2936,7 +2951,7 @@ mod tests { pactffi_free_pact_handle(pact_handle); expect!(interaction.request.query.clone()).to(be_some().value(hashmap!{ - "id".to_string() => vec!["100".to_string(), "abc".to_string()] + "id".to_string() => vec![Some("100".to_string()), Some("abc".to_string())] })); assert_eq!(&interaction.request.matching_rules, &matchingrules! { "query" => { @@ -2964,7 +2979,7 @@ mod tests { pactffi_free_pact_handle(pact_handle); expect!(interaction.request.query.clone()).to(be_some().value(hashmap!{ - "catId[]".to_string() => vec!["1".to_string()] + "catId[]".to_string() => vec![Some("1".to_string())] })); expect!(&interaction.request.matching_rules).to(be_equal_to(&matchingrules! { "query" => { "$['catId[]']" => [ MatchingRule::MinType(1) ] } diff --git a/rust/pact_ffi/tests/tests.rs b/rust/pact_ffi/tests/tests.rs index 018168496..7f7d25e48 100644 --- a/rust/pact_ffi/tests/tests.rs +++ b/rust/pact_ffi/tests/tests.rs @@ -140,7 +140,7 @@ fn create_query_parameter_with_multiple_values() { interaction.with_interaction(&|_, _, i| { let interaction = i.as_v4_http().unwrap(); expect!(interaction.request.query.as_ref()).to(be_some().value(&hashmap!{ - "q".to_string() => vec!["1".to_string(), "2".to_string(), "3".to_string()] + "q".to_string() => vec![Some("1".to_string()), Some("2".to_string()), Some("3".to_string())] })); }); } diff --git a/rust/pact_matching/src/form_urlencoded.rs b/rust/pact_matching/src/form_urlencoded.rs index 57e4cd468..4867db44b 100644 --- a/rust/pact_matching/src/form_urlencoded.rs +++ b/rust/pact_matching/src/form_urlencoded.rs @@ -55,11 +55,11 @@ pub(crate) fn match_form_urlencoded( (Ok(e), Ok(a)) => { let expected_params = super::group_by(e, |(k, _)| k.clone()) .iter() - .map(|(k, v)| (k.clone(), v.iter().map(|(_, v)| v.clone()).collect_vec())) + .map(|(k, v)| (k.clone(), v.iter().map(|(_, v)| Some(v.clone())).collect_vec())) .collect(); let actual_params = super::group_by(a, |(k, _)| k.clone()) .iter() - .map(|(k, v)| (k.clone(), v.iter().map(|(_, v)| v.clone()).collect_vec())) + .map(|(k, v)| (k.clone(), v.iter().map(|(_, v)| Some(v.clone())).collect_vec())) .collect(); let result: Vec<_> = match_query_maps(expected_params, actual_params, context) .values().flat_map(|m| m.iter().map(|mismatch| { @@ -220,7 +220,7 @@ mod tests { path: "$.a".to_string(), expected: Some("[\"b\"]".into()), actual: Some("".into()), - mismatch: "".to_string(), + mismatch: "Expected form post parameter 'a' but was missing".to_string() }); assert_eq!(mismatches[0].description(), "$.a -> Expected form post parameter 'a' but was missing"); } diff --git a/rust/pact_matching/src/generator_tests.rs b/rust/pact_matching/src/generator_tests.rs index 5ff22c245..411cc8816 100644 --- a/rust/pact_matching/src/generator_tests.rs +++ b/rust/pact_matching/src/generator_tests.rs @@ -75,8 +75,8 @@ async fn applies_header_generator_for_headers_to_the_copy_of_the_request() { #[tokio::test] async fn applies_query_generator_for_query_parameters_to_the_copy_of_the_request() { let request = HttpRequest { query: Some(hashmap!{ - "A".to_string() => vec![ "a".to_string() ], - "B".to_string() => vec![ "b".to_string() ] + "A".to_string() => vec![ Some("a".to_string()) ], + "B".to_string() => vec![ Some("b".to_string()) ] }), generators: generators! { "QUERY" => { "A" => Generator::Uuid(None) @@ -84,16 +84,16 @@ async fn applies_query_generator_for_query_parameters_to_the_copy_of_the_request }, .. HttpRequest::default() }; let query = generate_request(&request, &GeneratorTestMode::Provider, &hashmap!{}).await.query.unwrap().clone(); - let query_val = &query.get("A").unwrap()[0]; - expect!(query_val).to_not(be_equal_to("a")); + let query_val = query.get("A").unwrap()[0].as_ref(); + expect!(query_val.unwrap()).to_not(be_equal_to("a")); } #[test_log::test(tokio::test)] async fn applies_provider_state_generator_for_query_parameters_with_square_brackets() { let request = HttpRequest { query: Some(hashmap!{ - "A".to_string() => vec![ "a".to_string() ], - "q[]".to_string() => vec![ "q1".to_string(), "q2".to_string() ] + "A".to_string() => vec![ Some("a".to_string()) ], + "q[]".to_string() => vec![ Some("q1".to_string()), Some("q2".to_string()) ] }), generators: generators! { "QUERY" => { @@ -109,9 +109,9 @@ async fn applies_provider_state_generator_for_query_parameters_with_square_brack let result = generate_request(&request, &GeneratorTestMode::Provider, &context).await; let query = result.query.unwrap(); let a_val = query.get("A").unwrap(); - expect!(a_val).to(be_equal_to(&vec!["1234".to_string()])); + expect!(a_val).to(be_equal_to(&vec![Some("1234".to_string())])); let q_val = query.get("q[]").unwrap(); - expect!(q_val).to(be_equal_to(&vec!["5678".to_string(), "5678".to_string()])); + expect!(q_val).to(be_equal_to(&vec![Some("5678".to_string()), Some("5678".to_string())])); } #[tokio::test] diff --git a/rust/pact_matching/src/lib.rs b/rust/pact_matching/src/lib.rs index e657f3e82..8fc0914f3 100644 --- a/rust/pact_matching/src/lib.rs +++ b/rust/pact_matching/src/lib.rs @@ -789,8 +789,8 @@ impl CommonMismatch { pub fn to_query_mismatch(&self) -> Mismatch { Mismatch::QueryMismatch { parameter: self.path.clone(), - expected: self.expected.clone().into(), - actual: self.actual.clone().into(), + expected: self.expected.clone(), + actual: self.actual.clone(), mismatch: self.description.clone() } } @@ -1426,24 +1426,26 @@ pub fn match_path(expected: &str, actual: &str, context: &(dyn MatchingContext + /// Matches the actual query parameters to the expected ones. pub fn match_query( - expected: Option>>, - actual: Option>>, + expected: Option>>>, + actual: Option>>>, context: &(dyn MatchingContext + Send + Sync) ) -> HashMap> { match (actual, expected) { (Some(aqm), Some(eqm)) => match_query_maps(eqm, aqm, context), (Some(aqm), None) => aqm.iter().map(|(key, value)| { + let actual_value = value.iter().map(|v| v.clone().unwrap_or_default()).collect_vec(); (key.clone(), vec![Mismatch::QueryMismatch { parameter: key.clone(), expected: "".to_string(), - actual: format!("{:?}", value), + actual: format!("{:?}", actual_value), mismatch: format!("Unexpected query parameter '{}' received", key) }]) }).collect(), (None, Some(eqm)) => eqm.iter().map(|(key, value)| { + let expected_value = value.iter().map(|v| v.clone().unwrap_or_default()).collect_vec(); (key.clone(), vec![Mismatch::QueryMismatch { parameter: key.clone(), - expected: format!("{:?}", value), + expected: format!("{:?}", expected_value), actual: "".to_string(), mismatch: format!("Expected query parameter '{}' but was missing", key) }]) @@ -2103,22 +2105,19 @@ pub async fn generate_request(request: &HttpRequest, mode: &GeneratorTestMode, c if let Some(parameter) = parameters.get_mut(param) { let mut generated = parameter.clone(); for (index, val) in parameter.iter().enumerate() { - if let Ok(v) = generator.generate_value(val, context, &DefaultVariantMatcher.boxed()) { - generated[index] = v; + let value = val.clone().unwrap_or_default(); + if let Ok(v) = generator.generate_value(&value, context, &DefaultVariantMatcher.boxed()) { + generated[index] = Some(v); } } *parameter = generated; - } else { - if let Ok(v) = generator.generate_value(&"".to_string(), context, &DefaultVariantMatcher.boxed()) { - parameters.insert(param.to_string(), vec![ v.to_string() ]); - } - } - } else { - if let Ok(v) = generator.generate_value(&"".to_string(), context, &DefaultVariantMatcher.boxed()) { - request.query = Some(hashmap!{ - param.to_string() => vec![ v.to_string() ] - }) + } else if let Ok(v) = generator.generate_value(&"".to_string(), context, &DefaultVariantMatcher.boxed()) { + parameters.insert(param.to_string(), vec![ Some(v.to_string()) ]); } + } else if let Ok(v) = generator.generate_value(&"".to_string(), context, &DefaultVariantMatcher.boxed()) { + request.query = Some(hashmap!{ + param.to_string() => vec![ Some(v.to_string()) ] + }) } } }); diff --git a/rust/pact_matching/src/query.rs b/rust/pact_matching/src/query.rs index 3b7f88687..ece09faff 100644 --- a/rust/pact_matching/src/query.rs +++ b/rust/pact_matching/src/query.rs @@ -13,22 +13,24 @@ use crate::matchingrules::compare_lists_with_matchingrules; /// Match the query parameters as Maps pub(crate) fn match_query_maps( - expected: HashMap>, - actual: HashMap>, + expected: HashMap>>, + actual: HashMap>>, context: &dyn MatchingContext ) -> HashMap> { let mut result: HashMap> = hashmap!{}; for (key, value) in &expected { + let expected_value = value.iter().map(|v| v.clone().unwrap_or_default()).collect_vec(); match actual.get(key) { Some(actual_value) => { - let mismatches: Result<(), Vec> = match_query_values(key, value, actual_value, context) + let actual_value = actual_value.iter().map(|v| v.clone().unwrap_or_default()).collect_vec(); + let mismatches: Result<(), Vec> = match_query_values(key, &expected_value, &actual_value, context) .map_err(|mismatches| mismatches.iter().map(|mismatch| mismatch.to_query_mismatch()).collect()); let v = result.entry(key.clone()).or_default(); v.extend(mismatches.err().unwrap_or_default()); }, None => result.entry(key.clone()).or_default().push(Mismatch::QueryMismatch { parameter: key.clone(), - expected: format!("{:?}", value), + expected: format!("{:?}", expected_value), actual: "".to_string(), mismatch: format!("Expected query parameter '{}' but was missing", key) }) @@ -40,7 +42,7 @@ pub(crate) fn match_query_maps( None => result.entry(key.clone()).or_default().push(Mismatch::QueryMismatch { parameter: key.clone(), expected: "".to_string(), - actual: format!("{:?}", value), + actual: format!("{:?}", value.iter().map(|v| v.clone().unwrap_or_default()).collect_vec()), mismatch: format!("Unexpected query parameter '{}' received", key) }) } diff --git a/rust/pact_matching/src/tests.rs b/rust/pact_matching/src/tests.rs index c4e27e6f2..a881cfa6c 100644 --- a/rust/pact_matching/src/tests.rs +++ b/rust/pact_matching/src/tests.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use expectest::prelude::*; +use pretty_assertions::assert_eq; use pact_models::{matchingrules, matchingrules_list}; use pact_models::matchingrules::expressions::{MatchingRuleDefinition, ValueType}; @@ -69,8 +70,8 @@ fn match_query_returns_nothing_if_there_are_no_query_strings() { #[test] fn match_query_applies_matching_rules_when_param_has_an_underscore() { - let expected = hashmap! { "user_id".to_string() => vec!["1".to_string()] }; - let actual = hashmap! { "user_id".to_string() => vec!["2".to_string()] }; + let expected = hashmap! { "user_id".to_string() => vec![Some("1".to_string())] }; + let actual = hashmap! { "user_id".to_string() => vec![Some("2".to_string())] }; let rules = matchingrules! { "query" => { "user_id" => [ MatchingRule::Regex("^[0-9]+$".to_string()) ] } }; @@ -86,7 +87,7 @@ fn match_query_applies_matching_rules_when_param_has_an_underscore() { fn match_query_returns_a_mismatch_if_there_is_no_expected_query_string() { let expected = None; let mut query_map = HashMap::new(); - query_map.insert("a".to_string(), vec!["b".to_string()]); + query_map.insert("a".to_string(), vec![Some("b".to_string())]); let actual = Some(query_map); let result = match_query(expected, actual, &CoreMatchingContext::default()); let mismatches: Vec = result.values().flatten().cloned().collect(); @@ -102,7 +103,7 @@ fn match_query_returns_a_mismatch_if_there_is_no_expected_query_string() { #[test] fn match_query_returns_a_mismatch_if_there_is_no_actual_query_string() { let mut query_map = HashMap::new(); - query_map.insert("a".to_string(), vec!["b".to_string()]); + query_map.insert("a".to_string(), vec![Some("b".to_string())]); let expected = Some(query_map); let actual = None; let result = match_query(expected, actual, &CoreMatchingContext::default()); @@ -119,11 +120,11 @@ fn match_query_returns_a_mismatch_if_there_is_no_actual_query_string() { #[test] fn match_query_returns_a_mismatch_if_there_is_an_actual_query_parameter_that_is_not_expected() { let mut query_map = HashMap::new(); - query_map.insert("a".to_string(), vec!["b".to_string()]); + query_map.insert("a".to_string(), vec![Some("b".to_string())]); let expected = Some(query_map); query_map = HashMap::new(); - query_map.insert("a".to_string(), vec!["b".to_string()]); - query_map.insert("c".to_string(), vec!["d".to_string()]); + query_map.insert("a".to_string(), vec![Some("b".to_string())]); + query_map.insert("c".to_string(), vec![Some("d".to_string())]); let actual = Some(query_map); let result = match_query(expected, actual, &CoreMatchingContext::default()); let mismatches: Vec = result.values().flatten().cloned().collect(); @@ -139,11 +140,11 @@ fn match_query_returns_a_mismatch_if_there_is_an_actual_query_parameter_that_is_ #[test] fn match_query_returns_a_mismatch_if_there_is_an_expected_query_parameter_that_is_not_received() { let mut query_map = HashMap::new(); - query_map.insert("a".to_string(), vec!["b".to_string()]); - query_map.insert("c".to_string(), vec!["d".to_string()]); + query_map.insert("a".to_string(), vec![Some("b".to_string())]); + query_map.insert("c".to_string(), vec![Some("d".to_string())]); let expected = Some(query_map); query_map = HashMap::new(); - query_map.insert("a".to_string(), vec!["b".to_string()]); + query_map.insert("a".to_string(), vec![Some("b".to_string())]); let actual = Some(query_map); let result = match_query(expected, actual, &CoreMatchingContext::default()); let mismatches: Vec = result.values().flatten().cloned().collect(); @@ -159,12 +160,12 @@ fn match_query_returns_a_mismatch_if_there_is_an_expected_query_parameter_that_i #[test] fn match_query_returns_a_mismatch_if_there_is_an_empty_expected_query_parameter_and_a_non_empty_actual() { let mut query_map = HashMap::new(); - query_map.insert("a".to_string(), vec!["b".to_string()]); + query_map.insert("a".to_string(), vec![Some("b".to_string())]); query_map.insert("c".to_string(), vec![]); let expected = Some(query_map); query_map = HashMap::new(); - query_map.insert("a".to_string(), vec!["b".to_string()]); - query_map.insert("c".to_string(), vec!["d".to_string()]); + query_map.insert("a".to_string(), vec![Some("b".to_string())]); + query_map.insert("c".to_string(), vec![Some("d".to_string())]); let actual = Some(query_map); let result = match_query(expected, actual, &CoreMatchingContext::default()); let mismatches: Vec = result.values().flatten().cloned().collect(); @@ -180,12 +181,12 @@ fn match_query_returns_a_mismatch_if_there_is_an_empty_expected_query_parameter_ #[test] fn match_query_returns_a_mismatch_if_the_query_values_have_different_lengths() { let mut query_map = HashMap::new(); - query_map.insert("a".to_string(), vec!["b".to_string()]); - query_map.insert("c".to_string(), vec!["d".to_string(), "e".to_string()]); + query_map.insert("a".to_string(), vec![Some("b".to_string())]); + query_map.insert("c".to_string(), vec![Some("d".to_string()), Some("e".to_string())]); let expected = Some(query_map); query_map = HashMap::new(); - query_map.insert("a".to_string(), vec!["b".to_string()]); - query_map.insert("c".to_string(), vec!["d".to_string()]); + query_map.insert("a".to_string(), vec![Some("b".to_string())]); + query_map.insert("c".to_string(), vec![Some("d".to_string())]); let actual = Some(query_map); let result = match_query(expected, actual, &CoreMatchingContext::default()); let mismatches: Vec = result.values().flatten().cloned().collect(); @@ -207,10 +208,10 @@ fn match_query_returns_a_mismatch_if_the_query_values_have_different_lengths() { #[test] fn match_query_returns_a_mismatch_if_the_values_are_not_the_same() { let mut query_map = HashMap::new(); - query_map.insert("a".to_string(), vec!["b".to_string()]); + query_map.insert("a".to_string(), vec![Some("b".to_string())]); let expected = Some(query_map); query_map = HashMap::new(); - query_map.insert("a".to_string(), vec!["c".to_string()]); + query_map.insert("a".to_string(), vec![Some("c".to_string())]); let actual = Some(query_map); let result = match_query(expected, actual, &CoreMatchingContext::default()); let mismatches: Vec = result.values().flatten().cloned().collect(); @@ -225,12 +226,12 @@ fn match_query_returns_a_mismatch_if_the_values_are_not_the_same() { #[test] fn match_query_with_min_type_matching_rules() { - let expected = hashmap! { "id".to_string() => vec!["1".to_string(), "2".to_string()] }; + let expected = hashmap! { "id".to_string() => vec![Some("1".to_string()), Some("2".to_string())] }; let actual = hashmap! { "id".to_string() => vec![ - "1".to_string(), - "2".to_string(), - "3".to_string(), - "4".to_string() + Some("1".to_string()), + Some("2".to_string()), + Some("3".to_string()), + Some("4".to_string()) ]}; let rules = matchingrules! { "query" => { "id" => [ MatchingRule::MinType(2) ] } @@ -246,12 +247,12 @@ fn match_query_with_min_type_matching_rules() { #[test] fn match_query_with_min_type_matching_rules_fails() { let expected = hashmap! { "id".to_string() => vec![ - "1".to_string(), - "2".to_string(), - "3".to_string(), - "4".to_string() + Some("1".to_string()), + Some("2".to_string()), + Some("3".to_string()), + Some("4".to_string()) ]}; - let actual = hashmap! { "id".to_string() => vec!["1".to_string()] }; + let actual = hashmap! { "id".to_string() => vec![Some("1".to_string())] }; let rules = matchingrules! { "query" => { "id" => [ MatchingRule::MinType(2) ] } }; @@ -283,10 +284,10 @@ fn match_query_returns_no_mismatch_if_the_values_are_not_the_same_but_match_by_a }.rules_for_category("query").unwrap_or_default(), &hashmap!{} ); let mut query_map = HashMap::new(); - query_map.insert("a".to_string(), vec!["b".to_string()]); + query_map.insert("a".to_string(), vec![Some("b".to_string())]); let expected = Some(query_map); query_map = HashMap::new(); - query_map.insert("a".to_string(), vec!["c".to_string()]); + query_map.insert("a".to_string(), vec![Some("c".to_string())]); let actual = Some(query_map); let result = match_query(expected, actual, &context); expect!(result.get("a").unwrap().iter()).to(be_empty()); @@ -303,10 +304,10 @@ fn match_query_returns_a_mismatch_if_the_values_do_not_match_by_a_matcher() { }.rules_for_category("query").unwrap_or_default(), &hashmap!{} ); let mut query_map = HashMap::new(); - query_map.insert("a".to_string(), vec!["b".to_string()]); + query_map.insert("a".to_string(), vec![Some("b".to_string())]); let expected = Some(query_map); query_map = HashMap::new(); - query_map.insert("a".to_string(), vec!["b".to_string()]); + query_map.insert("a".to_string(), vec![Some("b".to_string())]); let actual = Some(query_map); let result = match_query(expected, actual, &context); expect!(result.iter()).to_not(be_empty()); @@ -340,10 +341,10 @@ fn match_query_with_query_parameters_with_brackets() { ); let expected = hashmap!{ - "Q[]".to_string() => vec!["1".to_string(), "2".to_string()] + "Q[]".to_string() => vec![Some("1".to_string()), Some("2".to_string())] }; let actual = hashmap!{ - "Q[]".to_string() => vec!["100".to_string(), "2000".to_string(), "999".to_string()] + "Q[]".to_string() => vec![Some("100".to_string()), Some("2000".to_string()), Some("999".to_string())] }; let result = match_query(Some(expected), Some(actual), &context); expect!(result.get("Q[]").unwrap().iter()).to(be_empty()); @@ -352,12 +353,12 @@ fn match_query_with_query_parameters_with_brackets() { #[test] fn match_query_with_array_contains_matching_rules() { - let expected = hashmap! { "id".to_string() => vec!["1".to_string(), "3".to_string()] }; + let expected = hashmap! { "id".to_string() => vec![Some("1".to_string()), Some("3".to_string())] }; let actual = hashmap! { "id".to_string() => vec![ - "1".to_string(), - "2".to_string(), - "3".to_string(), - "4".to_string() + Some("1".to_string()), + Some("2".to_string()), + Some("3".to_string()), + Some("4".to_string()) ]}; let rules = matchingrules! { "query" => { "id" => [ MatchingRule::ArrayContains(vec![]) ] } @@ -374,8 +375,8 @@ fn match_query_with_array_contains_matching_rules() { #[test] fn match_query_with_array_contains_matching_rules_fails() { - let expected = hashmap! { "id".to_string() => vec!["1".to_string(), "3".to_string()] }; - let actual = hashmap! { "id".to_string() => vec!["2".to_string(), "3".to_string(), "4".to_string()] }; + let expected = hashmap! { "id".to_string() => vec![Some("1".to_string()), Some("3".to_string())] }; + let actual = hashmap! { "id".to_string() => vec![Some("2".to_string()), Some("3".to_string()), Some("4".to_string())] }; let rules = matchingrules! { "query" => { "id" => [ MatchingRule::ArrayContains(vec![]) ] } }; @@ -398,8 +399,8 @@ fn match_query_with_array_contains_matching_rules_fails() { #[test] fn match_query_with_each_value_matching_rules() { - let expected = hashmap! { "id".to_string() => vec!["1".to_string(), "2".to_string()] }; - let actual = hashmap! { "id".to_string() => vec!["3".to_string(), "4".to_string(), "567".to_string()] }; + let expected = hashmap! { "id".to_string() => vec![Some("1".to_string()), Some("2".to_string())] }; + let actual = hashmap! { "id".to_string() => vec![Some("3".to_string()), Some("4".to_string()), Some("567".to_string())] }; let rules = matchingrules! { "query" => { "id" => [ MatchingRule::EachValue(MatchingRuleDefinition::new("100".to_string(), ValueType::String, MatchingRule::Regex("\\d+".to_string()), None)) ] } @@ -416,8 +417,8 @@ fn match_query_with_each_value_matching_rules() { #[test] fn match_query_with_each_value_matching_rules_fails() { - let expected = hashmap! { "id".to_string() => vec!["1".to_string(), "2".to_string()] }; - let actual = hashmap! { "id".to_string() => vec!["3".to_string(), "abc123".to_string(), "test".to_string()] }; + let expected = hashmap! { "id".to_string() => vec![Some("1".to_string()), Some("2".to_string())] }; + let actual = hashmap! { "id".to_string() => vec![Some("3".to_string()), Some("abc123".to_string()), Some("test".to_string())] }; let rules = matchingrules! { "query" => { "id" => [ MatchingRule::EachValue(MatchingRuleDefinition::new("100".to_string(), ValueType::String, MatchingRule::Regex("\\d+".to_string()), None)) ] } diff --git a/rust/pact_mock_server/src/hyper_server.rs b/rust/pact_mock_server/src/hyper_server.rs index 57e758f26..a64e38977 100644 --- a/rust/pact_mock_server/src/hyper_server.rs +++ b/rust/pact_mock_server/src/hyper_server.rs @@ -47,7 +47,7 @@ fn extract_path(uri: &hyper::Uri) -> String { .into() } -fn extract_query_string(uri: &hyper::Uri) -> Option>> { +fn extract_query_string(uri: &hyper::Uri) -> Option>>> { debug!("Extracting query from uri {:?}", uri); uri.path_and_query() .and_then(|path_and_query| { diff --git a/rust/pact_mock_server/src/tests.rs b/rust/pact_mock_server/src/tests.rs index a567c67b2..f8f79961f 100644 --- a/rust/pact_mock_server/src/tests.rs +++ b/rust/pact_mock_server/src/tests.rs @@ -78,7 +78,7 @@ async fn match_request_returns_a_match_for_multiple_requests() { #[tokio::test] async fn match_request_returns_a_mismatch_for_incorrect_request() { let request = HttpRequest::default(); - let expected_request = HttpRequest { query: Some(hashmap!{ "QueryA".to_string() => vec!["Value A".to_string()] }), + let expected_request = HttpRequest { query: Some(hashmap!{ "QueryA".to_string() => vec![Some("Value A".to_string())] }), .. HttpRequest::default() }; let interaction = SynchronousHttp { request: expected_request, @@ -114,11 +114,11 @@ async fn match_request_returns_the_most_appropriate_mismatch_for_multiple_reques let request = HttpRequest { method: "GET".to_string(), path: "/".to_string(), body: OptionalBody::Present("This is a body".into(), None, None), .. HttpRequest::default() }; let request2 = HttpRequest { method: "GET".to_string(), path: "/".to_string(), query: Some(hashmap!{ - "QueryA".to_string() => vec!["Value A".to_string()] + "QueryA".to_string() => vec![Some("Value A".to_string())] }), body: OptionalBody::Present("This is a body".into(), None, None), .. HttpRequest::default() }; let request3 = HttpRequest { method: "GET".to_string(), path: "/".to_string(), query: Some(hashmap!{ - "QueryA".to_string() => vec!["Value A".to_string()] + "QueryA".to_string() => vec![Some("Value A".to_string())] }), body: OptionalBody::Missing, .. HttpRequest::default() }; let interaction = SynchronousHttp { description: "test".to_string(), request: request.clone(), .. SynchronousHttp::default() }; let interaction2 = SynchronousHttp { description: "test2".to_string(), request: request2.clone(), .. SynchronousHttp::default() }; diff --git a/rust/pact_models/Cargo.toml b/rust/pact_models/Cargo.toml index b7b6c9d2d..3b870720a 100644 --- a/rust/pact_models/Cargo.toml +++ b/rust/pact_models/Cargo.toml @@ -58,10 +58,10 @@ uuid = { version = "1.3.3", features = ["v4"] } [dev-dependencies] expectest = "0.12.0" -env_logger = "0.10.0" +env_logger = "0.11.3" hamcrest2 = "0.3.0" pretty_assertions = "1.3.0" -rstest = "0.17.0" +rstest = "0.19.0" speculate = "0.1.2" test-log = { version = "0.2.11", features = ["trace"] } tracing-subscriber = { version = "0.3.16", features = ["env-filter", "tracing-log", "fmt"] } diff --git a/rust/pact_models/src/json_utils.rs b/rust/pact_models/src/json_utils.rs index 42c42809d..ddebc29c9 100644 --- a/rust/pact_models/src/json_utils.rs +++ b/rust/pact_models/src/json_utils.rs @@ -138,6 +138,7 @@ pub fn headers_to_json(headers: &HashMap>) -> Value { #[derive(Deserialize)] #[serde(untagged)] +#[allow(dead_code)] enum JsonParsable { JsonStringValue(String), KeyValue(HashMap) diff --git a/rust/pact_models/src/pact.rs b/rust/pact_models/src/pact.rs index 5f7ea9a41..0ad19e1bc 100644 --- a/rust/pact_models/src/pact.rs +++ b/rust/pact_models/src/pact.rs @@ -591,7 +591,10 @@ mod tests { expect!(interaction.request).to(be_equal_to(Request { method: "GET".to_string(), path: "/mallory".to_string(), - query: Some(hashmap!{ "name".to_string() => vec!["ron".to_string()], "status".to_string() => vec!["good".to_string()] }), + query: Some(hashmap!{ + "name".to_string() => vec![Some("ron".to_string())], + "status".to_string() => vec![Some("good".to_string())] + }), headers: None, body: OptionalBody::Missing, .. Request::default() @@ -666,7 +669,10 @@ mod tests { expect!(interaction.request).to(be_equal_to(Request { method: "GET".to_string(), path: "/".to_string(), - query: Some(hashmap!{ "q".to_string() => vec!["p".to_string(), "p2".to_string()], "r".to_string() => vec!["s".to_string()] }), + query: Some(hashmap!{ + "q".to_string() => vec![Some("p".to_string()), Some("p2".to_string())], + "r".to_string() => vec![Some("s".to_string())] + }), headers: Some(hashmap!{ "testreqheader".to_string() => vec!["testreqheadervalue".to_string()] }), body: "{\"test\":true}".into(), .. Request::default() @@ -742,7 +748,10 @@ mod tests { expect!(interaction.request).to(be_equal_to(Request { method: "GET".to_string(), path: "/".to_string(), - query: Some(hashmap!{ "q".to_string() => vec!["p".to_string(), "p2".to_string()], "r".to_string() => vec!["s".to_string()] }), + query: Some(hashmap!{ + "q".to_string() => vec![Some("p".to_string()), Some("p2".to_string())], + "r".to_string() => vec![Some("s".to_string())] + }), headers: Some(hashmap!{ "testreqheader".to_string() => vec!["testreqheadervalue".to_string()] }), body: OptionalBody::Present("{\"test\":true}".into(), None, None), .. Request::default() @@ -807,8 +816,10 @@ mod tests { expect!(interaction.request).to(be_equal_to(Request { method: "GET".to_string(), path: "/".to_string(), - query: Some(hashmap!{ "datetime".to_string() => vec!["2011-12-03T10:15:30+01:00".to_string()], - "description".to_string() => vec!["hello world!".to_string()] }), + query: Some(hashmap!{ + "datetime".to_string() => vec![Some("2011-12-03T10:15:30+01:00".to_string())], + "description".to_string() => vec![Some("hello world!".to_string())] + }), headers: Some(hashmap!{ "testreqheader".to_string() => vec!["testreqheadervalue".to_string()] }), body: OptionalBody::Present("{\"test\":true}".into(), None, None), .. Request::default() @@ -1529,9 +1540,9 @@ mod tests { provider_states: vec![ProviderState { name: "Good state to be in".to_string(), params: hashmap!{} }], request: Request { query: Some(hashmap!{ - "a".to_string() => vec!["1".to_string(), "2".to_string(), "3".to_string()], - "b".to_string() => vec!["bill".to_string(), "bob".to_string()], - }), + "a".to_string() => vec![Some("1".to_string()), Some("2".to_string()), Some("3".to_string())], + "b".to_string() => vec![Some("bill".to_string()), Some("bob".to_string())] + }), .. Request::default() }, .. RequestResponseInteraction::default() diff --git a/rust/pact_models/src/query_strings.rs b/rust/pact_models/src/query_strings.rs index b61302203..c409f1201 100644 --- a/rust/pact_models/src/query_strings.rs +++ b/rust/pact_models/src/query_strings.rs @@ -89,16 +89,14 @@ pub fn encode_query(query: &str) -> String { /// Parses a query string into an optional map. The query parameter name will be mapped to /// a list of values. Where the query parameter is repeated, the order of the values will be /// preserved. -pub fn parse_query_string(query: &str) -> Option>> { +pub fn parse_query_string(query: &str) -> Option>>> { if !query.is_empty() { Some(query.split('&').map(|kv| { trace!("kv = '{}'", kv); if kv.is_empty() { vec![] - } else if kv.contains('=') { - kv.splitn(2, '=').collect::>() } else { - vec![kv] + kv.splitn(2, '=').collect::>() } }).fold(HashMap::new(), |mut map, name_value| { trace!("name_value = '{:?}'", name_value); @@ -106,11 +104,11 @@ pub fn parse_query_string(query: &str) -> Option>> { let name = decode_query(name_value[0]) .unwrap_or_else(|_| name_value[0].to_owned()); let value = if name_value.len() > 1 { - decode_query(name_value[1]).unwrap_or_else(|_| name_value[1].to_owned()) + Some(decode_query(name_value[1]).unwrap_or_else(|_| name_value[1].to_owned())) } else { - String::default() + None }; - trace!("decoded: '{}' => '{}'", name, value); + trace!("decoded: '{}' => {:?}", name, value); map.entry(name).or_insert_with(|| vec![]).push(value); } map @@ -121,21 +119,24 @@ pub fn parse_query_string(query: &str) -> Option>> { } /// Converts a query string map into a query string -pub fn build_query_string(query: HashMap>) -> String { +pub fn build_query_string(query: HashMap>>) -> String { query.into_iter() .sorted_by(|a, b| Ord::cmp(&a.0, &b.0)) .flat_map(|kv| { kv.1.iter() - .map(|v| format!("{}={}", kv.0, encode_query(v))) + .map(|v| match v { + None => kv.0.clone(), + Some(s) => format!("{}={}", kv.0, encode_query(s)) + }) .collect_vec() }) .join("&") } /// Parses a V2 query string from a JSON struct -pub fn query_from_json(query_json: &Value, spec_version: &PactSpecification) -> Option>> { +pub fn query_from_json(query_json: &Value, spec_version: &PactSpecification) -> Option>>> { match query_json { - &Value::String(ref s) => parse_query_string(s), + Value::String(s) => parse_query_string(s), _ => { warn!("Only string versions of request query strings are supported with specification version {}, ignoring.", spec_version.to_string()); @@ -145,18 +146,22 @@ pub fn query_from_json(query_json: &Value, spec_version: &PactSpecification) -> } /// Parses a V3 query string from a JSON struct -pub fn v3_query_from_json(query_json: &Value, spec_version: &PactSpecification) -> Option>> { +pub fn v3_query_from_json( + query_json: &Value, + spec_version: &PactSpecification +) -> Option>>> { match query_json { - &Value::String(ref s) => parse_query_string(s), - &Value::Object(ref map) => Some(map.iter().map(|(k, v)| { + Value::String(s) => parse_query_string(s), + Value::Object(map) => Some(map.iter().map(|(k, v)| { (k.clone(), match v { - &Value::String(ref s) => vec![s.clone()], - &Value::Array(ref array) => array.iter().map(|item| match item { - &Value::String(ref s) => s.clone(), - _ => v.to_string() + Value::String(s) => vec![Some(s.clone())], + Value::Array(array) => array.iter().map(|item| match item { + Value::String(s) => Some(s.clone()), + Value::Null => None, + _ => Some(v.to_string()) }).collect(), _ => { - warn!("Query paramter value '{}' is not valid, ignoring", v); + warn!("Query parameter value '{}' is not valid, ignoring", v); vec![] } }) @@ -170,13 +175,16 @@ pub fn v3_query_from_json(query_json: &Value, spec_version: &PactSpecification) } /// Converts a query string structure into a JSON struct -pub fn query_to_json(query: HashMap>, spec_version: &PactSpecification) -> Value { +pub fn query_to_json(query: HashMap>>, spec_version: &PactSpecification) -> Value { match spec_version { PactSpecification::V3 | PactSpecification::V4 => Value::Object(query .iter() .sorted_by(|(a, _), (b, _)| Ord::cmp(a, b)) .map(|(k, v)| { - (k.clone(), Value::Array(v.iter().map(|q| Value::String(q.clone())).collect())) + (k.clone(), Value::Array(v.iter().map(|q| match q { + None => Value::Null, + Some(s) => Value::String(s.clone()) + }).collect())) }) .collect()), _ => Value::String(build_query_string(query)) @@ -189,6 +197,8 @@ mod tests { use expectest::prelude::*; use maplit::hashmap; + use pretty_assertions::assert_eq; + use rstest::rstest; use crate::query_strings::parse_query_string; @@ -196,8 +206,8 @@ mod tests { fn parse_query_string_test() { let query = "a=b&c=d".to_string(); let expected = hashmap!{ - "a".to_string() => vec!["b".to_string()], - "c".to_string() => vec!["d".to_string()] + "a".to_string() => vec![Some("b".to_string())], + "c".to_string() => vec![Some("d".to_string())] }; let result = parse_query_string(&query); expect!(result).to(be_some().value(expected)); @@ -215,8 +225,8 @@ mod tests { fn parse_query_string_handles_missing_values() { let query = "a=&c=d".to_string(); let mut expected = HashMap::new(); - expected.insert("a".to_string(), vec!["".to_string()]); - expected.insert("c".to_string(), vec!["d".to_string()]); + expected.insert("a".to_string(), vec![Some("".to_string())]); + expected.insert("c".to_string(), vec![Some("d".to_string())]); let result = parse_query_string(&query); assert_eq!(result, Some(expected)); } @@ -225,8 +235,8 @@ mod tests { fn parse_query_string_handles_equals_in_values() { let query = "a=b&c=d=e=f".to_string(); let mut expected = HashMap::new(); - expected.insert("a".to_string(), vec!["b".to_string()]); - expected.insert("c".to_string(), vec!["d=e=f".to_string()]); + expected.insert("a".to_string(), vec![Some("b".to_string())]); + expected.insert("c".to_string(), vec![Some("d=e=f".to_string())]); let result = parse_query_string(&query); assert_eq!(result, Some(expected)); } @@ -235,7 +245,7 @@ mod tests { fn parse_query_string_decodes_values() { let query = "a=a%20b%20c".to_string(); let expected = hashmap! { - "a".to_string() => vec!["a b c".to_string()] + "a".to_string() => vec![Some("a b c".to_string())] }; let result = parse_query_string(&query); expect!(result).to(be_some().value(expected)); @@ -245,10 +255,27 @@ mod tests { fn parse_query_string_decodes_non_ascii_values() { let query = "accountNumber=100&anotherValue=%E6%96%87%E4%BB%B6.txt".to_string(); let expected = hashmap! { - "accountNumber".to_string() => vec!["100".to_string()], - "anotherValue".to_string() => vec!["文件.txt".to_string()] + "accountNumber".to_string() => vec![Some("100".to_string())], + "anotherValue".to_string() => vec![Some("文件.txt".to_string())] }; let result = parse_query_string(&query); expect!(result).to(be_some().value(expected)); } + + #[test] + fn parse_query_string_handles_no_values() { + let query = "a&c=&c&a".to_string(); + let mut expected = HashMap::new(); + expected.insert("a".to_string(), vec![None, None]); + expected.insert("c".to_string(), vec![Some("".to_string()), None]); + let result = parse_query_string(&query); + assert_eq!(result, Some(expected)); + } + + #[rstest] + #[case(hashmap!{}, "")] + fn build_query_string_test(#[case] map: HashMap>>, #[case] expected: &str) { + let result = super::build_query_string(map); + assert_eq!(result, expected) + } } diff --git a/rust/pact_models/src/request.rs b/rust/pact_models/src/request.rs index bf1965072..4a1219882 100644 --- a/rust/pact_models/src/request.rs +++ b/rust/pact_models/src/request.rs @@ -29,7 +29,7 @@ pub struct Request { /// Request path pub path: String, /// Request query string - pub query: Option>>, + pub query: Option>>>, /// Request headers pub headers: Option>>, /// Request body @@ -416,8 +416,8 @@ mod tests { #[test] fn request_to_json_with_a_query() { let request = Request { query: Some(hashmap!{ - "a".to_string() => vec!["1".to_string(), "2".to_string()], - "b".to_string() => vec!["3".to_string()] + "a".to_string() => vec![Some("1".to_string()), Some("2".to_string())], + "b".to_string() => vec![Some("3".to_string())] }), .. Request::default() }; expect!(request.to_json(&PactSpecification::V2).to_string()).to( be_equal_to(r#"{"method":"GET","path":"/","query":"a=1&a=2&b=3"}"#) @@ -427,8 +427,8 @@ mod tests { #[test] fn request_to_json_with_a_query_must_encode_the_query() { let request = Request { query: Some(hashmap!{ - "datetime".to_string() => vec!["2011-12-03T10:15:30+01:00".to_string()], - "description".to_string() => vec!["hello world!".to_string()] }), .. Request::default() }; + "datetime".to_string() => vec![Some("2011-12-03T10:15:30+01:00".to_string())], + "description".to_string() => vec![Some("hello world!".to_string())] }), .. Request::default() }; expect!(request.to_json(&PactSpecification::V2).to_string()).to( be_equal_to(r#"{"method":"GET","path":"/","query":"datetime=2011-12-03T10%3a15%3a30%2b01%3a00&description=hello+world%21"}"#) ); @@ -437,7 +437,7 @@ mod tests { #[test] fn request_to_json_with_a_query_must_encode_the_query_with_utf8_chars() { let request = Request { query: Some(hashmap!{ - "a".to_string() => vec!["b=c&d❤".to_string()] + "a".to_string() => vec![Some("b=c&d❤".to_string())] }), .. Request::default() }; expect!(request.to_json(&PactSpecification::V2).to_string()).to( be_equal_to(r#"{"method":"GET","path":"/","query":"a=b%3dc%26d%27%64"}"#) @@ -447,8 +447,8 @@ mod tests { #[test] fn request_to_json_with_a_query_v3() { let request = Request { query: Some(hashmap!{ - "a".to_string() => vec!["1".to_string(), "2".to_string()], - "b".to_string() => vec!["3".to_string()] + "a".to_string() => vec![Some("1".to_string()), Some("2".to_string())], + "b".to_string() => vec![Some("3".to_string())] }), .. Request::default() }; expect!(request.to_json(&PactSpecification::V3).to_string()).to( be_equal_to(r#"{"method":"GET","path":"/","query":{"a":["1","2"],"b":["3"]}}"#) @@ -458,8 +458,8 @@ mod tests { #[test] fn request_to_json_with_a_query_v3_must_not_encode_the_query() { let request = Request { query: Some(hashmap!{ - "datetime".to_string() => vec!["2011-12-03T10:15:30+01:00".to_string()], - "description".to_string() => vec!["hello world!".to_string()] }), .. Request::default() }; + "datetime".to_string() => vec![Some("2011-12-03T10:15:30+01:00".to_string())], + "description".to_string() => vec![Some("hello world!".to_string())] }), .. Request::default() }; expect!(request.to_json(&PactSpecification::V3).to_string()).to( be_equal_to(r#"{"method":"GET","path":"/","query":{"datetime":["2011-12-03T10:15:30+01:00"],"description":["hello world!"]}}"#) ); @@ -468,7 +468,7 @@ mod tests { #[test] fn request_to_json_with_a_query_v3_must_not_encode_the_query_with_utf8_chars() { let request = Request { query: Some(hashmap!{ - "a".to_string() => vec!["b=c&d❤".to_string()] + "a".to_string() => vec![Some("b=c&d❤".to_string())] }), .. Request::default() }; expect!(request.to_json(&PactSpecification::V3).to_string()).to( be_equal_to(r#"{"method":"GET","path":"/","query":{"a":["b=c&d❤"]}}"#) diff --git a/rust/pact_models/src/sync_pact.rs b/rust/pact_models/src/sync_pact.rs index 1b2fbf968..bb6de48ba 100644 --- a/rust/pact_models/src/sync_pact.rs +++ b/rust/pact_models/src/sync_pact.rs @@ -564,9 +564,9 @@ mod tests { expect!(&first_interaction.request.path).to(be_equal_to("/api/v3/klines")); expect!(first_interaction.request.body.is_present()).to(be_false()); expect!(first_interaction.request.query.as_ref().unwrap()).to(be_equal_to(&hashmap!{ - "interval".to_string() => vec!["1w".to_string()], - "limit".to_string() => vec!["1".to_string()], - "symbol".to_string() => vec!["LUNCUSDT".to_string()] + "interval".to_string() => vec![Some("1w".to_string())], + "limit".to_string() => vec![Some("1".to_string())], + "symbol".to_string() => vec![Some("LUNCUSDT".to_string())] })); expect!(first_interaction.request.headers.clone()).to(be_none()); expect!(first_interaction.response.status).to(be_equal_to(200)); diff --git a/rust/pact_models/src/v4/http_parts.rs b/rust/pact_models/src/v4/http_parts.rs index d2b9bcb56..6128d6301 100644 --- a/rust/pact_models/src/v4/http_parts.rs +++ b/rust/pact_models/src/v4/http_parts.rs @@ -34,7 +34,7 @@ pub struct HttpRequest { /// Request path pub path: String, /// Request query string - pub query: Option>>, + pub query: Option>>>, /// Request headers pub headers: Option>>, /// Request body @@ -773,8 +773,8 @@ mod tests { #[test] fn http_request_to_json_with_a_query() { let request = HttpRequest { query: Some(hashmap!{ - "a".to_string() => vec!["1".to_string(), "2".to_string()], - "b".to_string() => vec!["3".to_string()] + "a".to_string() => vec![Some("1".to_string()), Some("2".to_string())], + "b".to_string() => vec![Some("3".to_string())] }), .. HttpRequest::default() }; expect!(request.to_json().to_string()).to( be_equal_to(r#"{"method":"GET","path":"/","query":{"a":["1","2"],"b":["3"]}}"#) @@ -1142,21 +1142,21 @@ mod tests { let r5 = HttpRequest { query: Some(hashmap!{ - "q1".to_string() => vec!["1".to_string()], - "q2".to_string() => vec!["2".to_string()] + "q1".to_string() => vec![Some("1".to_string())], + "q2".to_string() => vec![Some("2".to_string())] }), .. HttpRequest::default() }; - expect!(hash(&r5)).to(be_equal_to(359339694141654691)); + expect!(hash(&r5)).to(be_equal_to(12415309608656506796)); let r6 = HttpRequest { query: Some(hashmap!{ - "q2".to_string() => vec!["2".to_string()], - "q1".to_string() => vec!["1".to_string()] + "q2".to_string() => vec![Some("2".to_string())], + "q1".to_string() => vec![Some("1".to_string())] }), .. HttpRequest::default() }; - expect!(hash(&r6)).to(be_equal_to(359339694141654691)); + expect!(hash(&r6)).to(be_equal_to(12415309608656506796)); let r7 = HttpRequest { headers: Some(hashmap!{ "Content-Type".to_string() => vec![ "application/json".to_string() ] }), @@ -1200,15 +1200,15 @@ mod tests { }; let r5 = HttpRequest { query: Some(hashmap!{ - "q1".to_string() => vec!["1".to_string()], - "q2".to_string() => vec!["2".to_string()] + "q1".to_string() => vec![Some("1".to_string())], + "q2".to_string() => vec![Some("2".to_string())] }), .. HttpRequest::default() }; let r6 = HttpRequest { query: Some(hashmap!{ - "q2".to_string() => vec!["1".to_string()], - "q1".to_string() => vec!["1".to_string()] + "q2".to_string() => vec![Some("1".to_string())], + "q1".to_string() => vec![Some("1".to_string())] }), .. HttpRequest::default() }; diff --git a/rust/pact_models/src/v4/pact.rs b/rust/pact_models/src/v4/pact.rs index 8828b8956..f3f0c3be1 100644 --- a/rust/pact_models/src/v4/pact.rs +++ b/rust/pact_models/src/v4/pact.rs @@ -550,7 +550,10 @@ mod tests { expect!(request).to(be_equal_to(HttpRequest { method: "GET".into(), path: "/mallory".into(), - query: Some(hashmap!{ "name".to_string() => vec!["ron".to_string()], "status".to_string() => vec!["good".to_string()] }), + query: Some(hashmap!{ + "name".to_string() => vec![Some("ron".to_string())], + "status".to_string() => vec![Some("good".to_string())] + }), headers: None, body: OptionalBody::Missing, .. HttpRequest::default() @@ -600,8 +603,10 @@ mod tests { match v4pact.interactions[0].as_v4_http() { Some(SynchronousHttp { request, .. }) => { expect!(&request.query).to(be_equal_to( - &Some(hashmap!{ "datetime".to_string() => vec!["2011-12-03T10:15:30+01:00".to_string()], - "description".to_string() => vec!["hello world!".to_string()] }))); + &Some(hashmap!{ + "datetime".to_string() => vec![Some("2011-12-03T10:15:30+01:00".to_string())], + "description".to_string() => vec![Some("hello world!".to_string())] + }))); } _ => panic!("Was expecting an HTTP pact") } diff --git a/rust/pact_models/tests/tests.rs b/rust/pact_models/tests/tests.rs index 52e2cfe41..42917a27b 100644 --- a/rust/pact_models/tests/tests.rs +++ b/rust/pact_models/tests/tests.rs @@ -50,9 +50,9 @@ fn write_v4_and_read_v3_pact_test() { method: "GET".to_string(), path: "/api/v3/klines".to_string(), query: Some(hashmap!{ - "interval".to_string() => vec![ "1w".to_string() ], - "limit".to_string() => vec![ "1".to_string() ], - "symbol".to_string() => vec![ "LUNCUSDT".to_string() ] + "interval".to_string() => vec![ Some("1w".to_string()) ], + "limit".to_string() => vec![ Some("1".to_string()) ], + "symbol".to_string() => vec![ Some("LUNCUSDT".to_string()) ] }), .. Request::default() }, diff --git a/rust/pact_verifier/src/callback_executors.rs b/rust/pact_verifier/src/callback_executors.rs index 166ffff54..69a37983b 100644 --- a/rust/pact_verifier/src/callback_executors.rs +++ b/rust/pact_verifier/src/callback_executors.rs @@ -139,16 +139,16 @@ impl ProviderStateExecutor for HttpRequestProviderStateExecutor { state_change_request.body = OptionalBody::Present(json_body.to_string().into(), Some(JSON.clone()), None); state_change_request.headers = Some(hashmap!{ "Content-Type".to_string() => vec!["application/json".to_string()] }); } else { - let mut query = hashmap!{ "state".to_string() => vec![provider_state.name.clone()] }; + let mut query = hashmap!{ "state".to_string() => vec![Some(provider_state.name.clone())] }; if setup { - query.insert("action".to_string(), vec!["setup".to_string()]); + query.insert("action".to_string(), vec![Some("setup".to_string())]); } else { - query.insert("action".to_string(), vec!["teardown".to_string()]); + query.insert("action".to_string(), vec![Some("teardown".to_string())]); } for (k, v) in provider_state.params.clone() { query.insert(k, vec![match v { - Value::String(ref s) => s.clone(), - _ => v.to_string() + Value::String(ref s) => Some(s.clone()), + _ => Some(v.to_string()) }]); } state_change_request.query = Some(query); diff --git a/rust/pact_verifier/src/provider_client.rs b/rust/pact_verifier/src/provider_client.rs index c5535c09e..ac78aaac2 100644 --- a/rust/pact_verifier/src/provider_client.rs +++ b/rust/pact_verifier/src/provider_client.rs @@ -368,8 +368,8 @@ mod tests { let base_url = "http://example.test:8080".to_string(); let request = HttpRequest { query: Some(hashmap!{ - "a".to_string() => vec!["b".to_string()], - "c".to_string() => vec!["d".to_string(), "e".to_string()] + "a".to_string() => vec![Some("b".to_string())], + "c".to_string() => vec![Some("d".to_string()), Some("e".to_string())] }), .. HttpRequest::default() };