use std::collections::HashMap;
#[cfg(test)]
#[allow(unused_imports)]
use env_logger;
#[cfg(test)]
use regex::Regex;
#[cfg(test)]
use serde_json::json;
use pact_models::generators::{Generator, GeneratorCategory, Generators};
use pact_models::matchingrules::MatchingRules;
use pact_models::bodies::OptionalBody;
use pact_models::expression_parser::DataType;
use pact_models::path_exp::DocPath;
use crate::prelude::*;
pub trait HttpPartBuilder {
#[doc(hidden)]
fn headers_and_matching_rules_mut(&mut self) -> (&mut HashMap<String, Vec<String>>, &mut MatchingRules);
#[doc(hidden)]
fn generators(&mut self) -> &mut Generators;
#[doc(hidden)]
fn body_and_matching_rules_mut(&mut self) -> (&mut OptionalBody, &mut MatchingRules);
#[allow(clippy::option_map_unit_fn)]
fn header<N, V>(&mut self, name: N, value: V) -> &mut Self
where
N: Into<String>,
V: Into<StringPattern>,
{
let name = name.into();
let value = value.into();
{
let (headers, rules) = self.headers_and_matching_rules_mut();
if headers.contains_key(&name) {
headers.get_mut(&name).map(|val| val.push(value.to_example()));
} else {
headers.insert(name.clone(), vec![value.to_example()]);
}
let mut path = DocPath::root();
path.push_field(name);
value.extract_matching_rules(path, rules.add_category("header"))
}
self
}
#[allow(clippy::option_map_unit_fn)]
fn header_from_provider_state<N, E, V>(&mut self, name: N, expression: E, value: V) -> &mut Self
where
N: Into<String>,
E: Into<String>,
V: Into<StringPattern>,
{
let expression = expression.into();
let sub_category = name.into();
self.header(&sub_category, value);
let mut sub_category_path = DocPath::root();
sub_category_path.push_field(sub_category);
{
let generators = self.generators();
generators.add_generator_with_subcategory(
&GeneratorCategory::HEADER,
sub_category_path,
Generator::ProviderStateGenerator(expression, Some(DataType::STRING)),
)
}
self
}
fn content_type<CT>(&mut self, content_type: CT) -> &mut Self
where
CT: Into<StringPattern>,
{
self.header("Content-Type", content_type)
}
fn html(&mut self) -> &mut Self {
self.content_type("text/html")
}
fn json_utf8(&mut self) -> &mut Self {
self.content_type(term!(
"^application/json; charset=(utf|UTF)-8$",
"application/json; charset=utf-8"
))
}
fn body<B: Into<String>>(&mut self, body: B) -> &mut Self {
let body = body.into();
{
let (body_ref, _) = self.body_and_matching_rules_mut();
*body_ref = OptionalBody::Present(body.into(), None, None);
}
self
}
fn body2<B: Into<String>>(&mut self, body: B, content_type: B) -> &mut Self {
let body = body.into();
{
let (body_ref, _) = self.body_and_matching_rules_mut();
*body_ref = OptionalBody::Present(body.into(), content_type.into().parse().ok(), None);
}
self
}
fn json_body<B: Into<JsonPattern>>(&mut self, body: B) -> &mut Self {
let body = body.into();
{
let (body_ref, rules) = self.body_and_matching_rules_mut();
*body_ref = OptionalBody::Present(body.to_example().to_string().into(), Some("application/json".into()), None);
body.extract_matching_rules(DocPath::root(), rules.add_category("body"));
}
self
}
}
#[test]
fn header_pattern() {
let application_regex = Regex::new("application/.*").unwrap();
let pattern = PactBuilder::new("C", "P")
.interaction("I", "", |mut i| {
i.request.header(
"Content-Type",
Term::new(application_regex, "application/json"),
);
i
})
.build();
let good = PactBuilder::new("C", "P")
.interaction("I", "", |mut i| {
i.request.header("Content-Type", "application/xml");
i
})
.build();
let bad = PactBuilder::new("C", "P")
.interaction("I", "", |mut i| { i.request.header("Content-Type", "text/html"); i })
.build();
assert_requests_match!(good, pattern);
assert_requests_do_not_match!(bad, pattern);
}
#[test]
fn header_generator() {
let actual = PactBuilder::new("C", "P")
.interaction("I", "", |mut i| {
i.request.header_from_provider_state(
"Authorization",
"token",
"some-token",
);
i
}).build();
let expected = PactBuilder::new("C", "P")
.interaction("I", "", |mut i| {
i.request.header("Authorization", "from-provider-state");
i
})
.build();
let good_context = &mut HashMap::new();
good_context.insert("token", json!("from-provider-state"));
assert_requests_with_context_match!(actual, expected, good_context);
let bad_context = &mut HashMap::new();
bad_context.insert("token", json!("not-from-provider-state"));
assert_requests_with_context_do_not_match!(actual, expected, bad_context);
}
#[test]
fn body_literal() {
let pattern = PactBuilder::new("C", "P")
.interaction("I", "", |mut i| { i.request.body("Hello"); i })
.build();
let good = PactBuilder::new("C", "P")
.interaction("I", "", |mut i| { i.request.body("Hello"); i })
.build();
let bad = PactBuilder::new("C", "P")
.interaction("I", "", |mut i| { i.request.body("Bye"); i })
.build();
assert_requests_match!(good, pattern);
assert_requests_do_not_match!(bad, pattern);
}
#[test]
fn json_body_pattern() {
let pattern = PactBuilder::new("C", "P")
.interaction("I", "", |mut i| {
i.request.json_body(json_pattern!({
"message": Like::new(json_pattern!("Hello")),
}));
i
})
.build();
let good = PactBuilder::new("C", "P")
.interaction("I", "", |mut i| {
i.request.json_body(json_pattern!({ "message": "Goodbye" }));
i
})
.build();
let bad = PactBuilder::new("C", "P")
.interaction("I", "", |mut i| {
i.request.json_body(json_pattern!({ "message": false }));
i
})
.build();
assert_requests_match!(good, pattern);
assert_requests_do_not_match!(bad, pattern);
}