use super::{build_cdr, Integrity, Warning};
use crate::{json, warning};
use std::assert_matches;
fn any_warning(warnings: &warning::Set<Warning>, pred: impl Fn(&Warning) -> bool) -> bool {
warnings
.iter()
.any(|group| group.to_parts().1.into_iter().any(&pred))
}
const CDR: &str = r#"{
"currency": "EUR",
"start_date_time": "2024-01-01T00:00:00Z",
"end_date_time": "2024-01-01T01:00:00Z",
"charging_periods": [
{
"start_date_time": "2024-01-01T00:00:00Z",
"dimensions": [{"type": "ENERGY", "volume": 10.0}]
}
],
"total_cost": {"excl_vat": 5.0, "incl_vat": 6.05},
"total_energy": 10.0,
"total_time": 1.0,
"tariffs": [
{
"country_code": "NL",
"party_id": "CPO",
"currency": "EUR",
"id": "T1",
"last_updated": "2024-01-01T00:00:00Z",
"elements": [
{"price_components": [{"price": 0.5, "step_size": 1, "type": "ENERGY"}]}
]
}
]
}"#;
#[test]
fn cdr_builds_its_modeled_fields() {
let doc = json::parse(CDR.into()).unwrap();
let (cdr, _warnings) = build_cdr(&doc).into_parts();
let Integrity::Ok(currency) = &cdr.currency else {
panic!("currency should be built: {:?}", cdr.currency);
};
assert_eq!(
currency.element().to_raw_str().unwrap().as_unescaped_str(),
"EUR"
);
let Integrity::Ok(total_cost) = &cdr.total_cost else {
panic!("total_cost should be built: {:?}", cdr.total_cost);
};
assert_matches!(total_cost.excl_vat, Integrity::Ok(_));
assert_matches!(total_cost.incl_vat, Integrity::Ok(_));
assert_matches!(cdr.total_energy, Integrity::Ok(_));
let Integrity::Ok(periods) = &cdr.charging_periods else {
panic!(
"charging_periods should be built: {:?}",
cdr.charging_periods
);
};
assert_eq!(periods.len(), 1);
let Integrity::Ok(period) = &periods[0] else {
panic!("the charging period should be built");
};
let Integrity::Ok(dimensions) = &period.dimensions else {
panic!("dimensions should be built");
};
assert_eq!(dimensions.len(), 1);
let Integrity::Ok(dimension) = &dimensions[0] else {
panic!("the dimension should be built");
};
let Integrity::Ok(dimension_type) = &dimension.dimension_type else {
panic!("the dimension type should be built");
};
assert_eq!(dimension_type.canonical(), "ENERGY");
assert_matches!(dimension.volume, Integrity::Ok(_));
let Integrity::Ok(Some(tariffs)) = &cdr.tariffs else {
panic!("tariffs should be built: {:?}", cdr.tariffs);
};
assert_eq!(tariffs.len(), 1);
let Integrity::Ok(tariff) = &tariffs[0] else {
panic!("the embedded tariff should be built");
};
assert_matches!(tariff.currency, Integrity::Ok(_));
assert_matches!(tariff.elements, Integrity::Ok(_));
}
#[test]
fn unknown_cdr_dimension_type_is_err() {
let src = CDR.replace(
r#""type": "ENERGY", "volume""#,
r#""type": "FOO", "volume""#,
);
let doc = json::parse(src.as_str().into()).unwrap();
let (cdr, warnings) = build_cdr(&doc).into_parts();
let Integrity::Ok(periods) = &cdr.charging_periods else {
panic!("charging_periods should be built");
};
let Integrity::Ok(period) = &periods[0] else {
panic!("the charging period should be built");
};
let Integrity::Ok(dimensions) = &period.dimensions else {
panic!("dimensions should be built");
};
let Integrity::Ok(dimension) = &dimensions[0] else {
panic!("the dimension should be built");
};
assert_matches!(dimension.dimension_type, Integrity::Err);
assert!(any_warning(&warnings, |w| matches!(
w,
Warning::FieldInvalidValue { .. }
)));
}
#[test]
fn building_is_total_on_degenerate_input() {
for src in ["{}", "[]", "\"not an object\""] {
let doc = json::parse(src.into()).unwrap();
let (cdr, _warnings) = build_cdr(&doc).into_parts();
assert_matches!(cdr.currency, Integrity::Missing);
assert_matches!(cdr.charging_periods, Integrity::Missing);
assert_matches!(cdr.total_cost, Integrity::Missing);
assert_matches!(cdr.tariffs, Integrity::Ok(None) | Integrity::Missing);
}
}