use std::collections::BTreeMap;
use crate::{cdr, json::FromJson as _, price, test, warning, ObjectType, Version};
const ELEM_TOTAL: usize = 13;
const WARN_TOTAL: usize = 13;
const WARN_TYPES_TOTAL: usize = 2;
const CDR_WITH_WARNINGS: &str = r#"{
"id": "12345",
"start_date_time": "2015-06-29T17:00:09Z",
"stop_date_time": "2015-06-29T23:00:32Z",
"currency": "EUR",
"charging_periods": [
{
"start_date_time": "2015-06-29T17:00:09Z",
"dimensions": [{
"type": "TIME",
"volume": 0.50003
}]
},
{
"start_date_time": "2015-06-29T17:30:09Z",
"dimensions": [{
"type": "TIME",
"volume": 0.50003
}]
}
{
"start_date_time": "2015-06-29T18:00:09Z",
"dimensions": [{
"type": "CONGESTION_TIME",
"volume": 0.50003
}]
}
{
"start_date_time": "2015-06-29T18:30:09Z",
"dimensions": [{
"type": "CONGESTION_TIME",
"volume": 0.50003
}]
}
{
"start_date_time": "2015-06-29T19:00:09Z",
"dimensions": [{
"type": "CONGESTION_TIME",
"volume": 0.50003
}]
}
{
"start_date_time": "2015-06-29T19:30:09Z",
"dimensions": [{
"type": "CONGESTION_TIME",
"volume": 0.50003
}]
}
],
"total_cost": 4.001234,
"total_energy": 15.342021,
"total_time": 6.03045
}"#;
#[test]
fn should_produce_expected_warnings() {
let warnings = parse_cdr(CDR_WITH_WARNINGS);
let warnings = warnings.path_id_map();
let expect_warnings = BTreeMap::from([
(
"$.charging_periods.0.dimensions.0.volume",
vec![id("excessive_precision")],
),
(
"$.charging_periods.1.dimensions.0.volume",
vec![id("excessive_precision")],
),
(
"$.charging_periods.2.dimensions.0.type",
vec![id("field_invalid_value(CONGESTION_TIME)")],
),
(
"$.charging_periods.2.dimensions.0.volume",
vec![id("excessive_precision")],
),
(
"$.charging_periods.3.dimensions.0.type",
vec![id("field_invalid_value(CONGESTION_TIME)")],
),
(
"$.charging_periods.3.dimensions.0.volume",
vec![id("excessive_precision")],
),
(
"$.charging_periods.4.dimensions.0.type",
vec![id("field_invalid_value(CONGESTION_TIME)")],
),
(
"$.charging_periods.4.dimensions.0.volume",
vec![id("excessive_precision")],
),
(
"$.charging_periods.5.dimensions.0.type",
vec![id("field_invalid_value(CONGESTION_TIME)")],
),
(
"$.charging_periods.5.dimensions.0.volume",
vec![id("excessive_precision")],
),
("$.total_cost", vec![id("excessive_precision")]),
("$.total_energy", vec![id("excessive_precision")]),
("$.total_time", vec![id("excessive_precision")]),
]);
assert_eq!(warnings, expect_warnings, "{warnings:#?}");
}
#[test]
fn should_allow_all() {
let warnings = parse_cdr(CDR_WITH_WARNINGS);
let outcome = warnings.id_path_map(warning::Limit::None);
let warning::IdPathMap {
warnings,
total_warnings,
total_elements,
elements_filtered,
warning_types_filtered: warnings_filtered,
} = outcome;
assert_eq!(total_warnings, WARN_TOTAL);
assert_eq!(total_elements, ELEM_TOTAL);
assert_eq!(elements_filtered, 0, "No elements are filtered");
assert_eq!(warnings_filtered, 0, "No warnings are filtered");
let expect_warnings = BTreeMap::from([
(
id("excessive_precision"),
vec![
"$.charging_periods.0.dimensions.0.volume",
"$.charging_periods.1.dimensions.0.volume",
"$.charging_periods.2.dimensions.0.volume",
"$.charging_periods.3.dimensions.0.volume",
"$.charging_periods.4.dimensions.0.volume",
"$.charging_periods.5.dimensions.0.volume",
"$.total_cost",
"$.total_energy",
"$.total_time",
],
),
(
id("field_invalid_value(CONGESTION_TIME)"),
vec![
"$.charging_periods.2.dimensions.0.type",
"$.charging_periods.3.dimensions.0.type",
"$.charging_periods.4.dimensions.0.type",
"$.charging_periods.5.dimensions.0.type",
],
),
]);
assert_eq!(warnings, expect_warnings, "{warnings:#?}");
}
#[test]
fn should_limit_warning_ids_only() {
const WARN_LIMIT: usize = 1;
let warnings = parse_cdr(CDR_WITH_WARNINGS);
let outcome = warnings.id_path_map(warning::Limit::WarningTypes(WARN_LIMIT));
let warning::IdPathMap {
warnings,
total_warnings,
total_elements,
elements_filtered,
warning_types_filtered: warnings_filtered,
} = outcome;
assert_eq!(total_warnings, WARN_TOTAL);
assert_eq!(total_elements, ELEM_TOTAL);
assert_eq!(
elements_filtered, 4,
"The second warning has 4 element paths that are filtered"
);
assert_eq!(
warnings_filtered, 1,
"There are two warning types raised; one is allowed"
);
let expect_warnings = BTreeMap::from([(
id("excessive_precision"),
vec![
"$.charging_periods.0.dimensions.0.volume",
"$.charging_periods.1.dimensions.0.volume",
"$.charging_periods.2.dimensions.0.volume",
"$.charging_periods.3.dimensions.0.volume",
"$.charging_periods.4.dimensions.0.volume",
"$.charging_periods.5.dimensions.0.volume",
"$.total_cost",
"$.total_energy",
"$.total_time",
],
)]);
assert_eq!(warnings, expect_warnings, "{warnings:#?}");
}
#[test]
fn should_limit_zero_warning_ids_only() {
const WARN_LIMIT: usize = 0;
let warnings = parse_cdr(CDR_WITH_WARNINGS);
let outcome = warnings.id_path_map(warning::Limit::WarningTypes(WARN_LIMIT));
let warning::IdPathMap {
warnings,
total_warnings,
total_elements,
elements_filtered,
warning_types_filtered: warnings_filtered,
} = outcome;
assert_eq!(total_warnings, WARN_TOTAL);
assert_eq!(total_elements, ELEM_TOTAL);
assert_eq!(
warnings_filtered, WARN_TYPES_TOTAL,
"All warning types are filtered"
);
assert_eq!(
elements_filtered, ELEM_TOTAL,
"All warning types are filtered, therefore all elements are filtered"
);
let expect_warnings = BTreeMap::new();
assert_eq!(warnings, expect_warnings, "{warnings:#?}");
}
#[test]
fn should_limit_element_paths() {
let warnings = parse_cdr(CDR_WITH_WARNINGS);
let outcome = warnings.id_path_map(warning::Limit::ElemPathsPerId(3));
let warning::IdPathMap {
warnings,
total_warnings,
total_elements,
elements_filtered,
warning_types_filtered: warnings_filtered,
} = outcome;
assert_eq!(total_warnings, WARN_TOTAL);
assert_eq!(total_elements, ELEM_TOTAL);
assert_eq!(elements_filtered, 7, "The first warning type has 6 elements filtered and the second warning type has 4 elements filtered.");
assert_eq!(
warnings_filtered, 0,
"Only element paths are filtered; all warning types pass-through"
);
let expect_warnings = BTreeMap::from([
(
id("excessive_precision"),
vec![
"$.charging_periods.0.dimensions.0.volume",
"$.charging_periods.1.dimensions.0.volume",
"$.charging_periods.2.dimensions.0.volume",
],
),
(
id("field_invalid_value(CONGESTION_TIME)"),
vec![
"$.charging_periods.2.dimensions.0.type",
"$.charging_periods.3.dimensions.0.type",
"$.charging_periods.4.dimensions.0.type",
],
),
]);
assert_eq!(warnings, expect_warnings, "{warnings:#?}");
}
#[test]
fn should_limit_zero_element_paths() {
let warnings = parse_cdr(CDR_WITH_WARNINGS);
let outcome = warnings.id_path_map(warning::Limit::ElemPathsPerId(0));
let warning::IdPathMap {
warnings,
total_warnings,
total_elements,
elements_filtered,
warning_types_filtered: warnings_filtered,
} = outcome;
assert_eq!(total_warnings, WARN_TOTAL);
assert_eq!(total_elements, ELEM_TOTAL);
assert_eq!(
elements_filtered, ELEM_TOTAL,
"All element paths are filtered"
);
assert_eq!(warnings_filtered, 0, "Only element paths are filtered");
let expect_warnings = BTreeMap::from([
(id("excessive_precision"), vec![]),
(id("field_invalid_value(CONGESTION_TIME)"), vec![]),
]);
assert_eq!(warnings, expect_warnings, "{warnings:#?}");
}
#[test]
fn should_limit_ids_and_paths() {
let warnings = parse_cdr(CDR_WITH_WARNINGS);
let outcome = warnings.id_path_map(warning::Limit::All {
max_warning_types: 1,
max_elem_paths_per_warning_id: 3,
});
let warning::IdPathMap {
warnings,
total_warnings,
total_elements,
elements_filtered,
warning_types_filtered: warnings_filtered,
} = outcome;
assert_eq!(total_warnings, WARN_TOTAL);
assert_eq!(total_elements, ELEM_TOTAL);
assert_eq!(
elements_filtered, 10,
"The first warning type has 6 elements filtered and the second warning type\
is filtered due to only one type being allowed"
);
assert_eq!(
warnings_filtered, 1,
"Only one warning type is allowed so the second is filtered"
);
let expect_warnings = BTreeMap::from([(
id("excessive_precision"),
vec![
"$.charging_periods.0.dimensions.0.volume",
"$.charging_periods.1.dimensions.0.volume",
"$.charging_periods.2.dimensions.0.volume",
],
)]);
assert_eq!(warnings, expect_warnings, "{warnings:#?}");
}
#[test]
fn should_limit_zero_ids_and_paths() {
let warnings = parse_cdr(CDR_WITH_WARNINGS);
let outcome = warnings.id_path_map(warning::Limit::All {
max_warning_types: 0,
max_elem_paths_per_warning_id: 3,
});
let warning::IdPathMap {
warnings,
total_warnings,
total_elements,
elements_filtered,
warning_types_filtered: warnings_filtered,
} = outcome;
assert_eq!(total_warnings, WARN_TOTAL);
assert_eq!(total_elements, ELEM_TOTAL);
assert_eq!(
warnings_filtered, WARN_TYPES_TOTAL,
"All warning types should be filtered"
);
assert_eq!(
elements_filtered, ELEM_TOTAL,
"All elements are filtered as zero warning types are allowed"
);
let expect_warnings = BTreeMap::new();
assert_eq!(warnings, expect_warnings, "{warnings:#?}");
}
#[test]
fn should_limit_ids_and_zero_paths() {
let warnings = parse_cdr(CDR_WITH_WARNINGS);
let outcome = warnings.id_path_map(warning::Limit::All {
max_warning_types: 1,
max_elem_paths_per_warning_id: 0,
});
let warning::IdPathMap {
warnings,
total_warnings,
total_elements,
elements_filtered,
warning_types_filtered: warnings_filtered,
} = outcome;
assert_eq!(total_warnings, WARN_TOTAL);
assert_eq!(total_elements, ELEM_TOTAL);
assert_eq!(elements_filtered, ELEM_TOTAL);
assert_eq!(warnings_filtered, 1,);
let expect_warnings = BTreeMap::from([(id("excessive_precision"), vec![])]);
assert_eq!(warnings, expect_warnings, "{warnings:#?}");
}
#[test]
fn should_limit_zero_ids_and_zero_paths() {
let warnings = parse_cdr(CDR_WITH_WARNINGS);
let outcome = warnings.id_path_map(warning::Limit::All {
max_warning_types: 0,
max_elem_paths_per_warning_id: 0,
});
let warning::IdPathMap {
warnings,
total_warnings,
total_elements,
elements_filtered,
warning_types_filtered: warnings_filtered,
} = outcome;
assert_eq!(total_warnings, WARN_TOTAL);
assert_eq!(total_elements, ELEM_TOTAL);
assert_eq!(elements_filtered, ELEM_TOTAL);
assert_eq!(
warnings_filtered, WARN_TYPES_TOTAL,
"All warning types should be filtered"
);
let expect_warnings = BTreeMap::new();
assert_eq!(warnings, expect_warnings, "{warnings:#?}");
}
fn parse_cdr(json: &str) -> warning::Set<price::Warning> {
let report = cdr::parse_with_version(json, Version::V211).unwrap();
let cdr::ParseReport {
cdr,
unexpected_fields,
} = report;
test::assert_no_unexpected_fields(ObjectType::Cdr, &unexpected_fields);
let (_cdr, warnings) = price::v211::cdr::WithTariffs::from_json(cdr.as_element())
.unwrap()
.into_parts();
warnings
}
const fn id(s: &'static str) -> warning::Id {
warning::Id::from_static(s)
}