#![allow(clippy::missing_panics_doc, reason = "tests are allowed to panic")]
#![allow(clippy::panic, reason = "tests are allowed to panic")]
use rust_decimal_macros::dec;
use crate::{
cdr, json, price,
price::test::UnwrapReport as _,
tariff,
test::{self},
Version,
};
fn simple_cdr_json(total_cost_excl: f64) -> String {
serde_json::json!({
"country_code": "NL",
"party_id": "TDR",
"start_date_time": "2022-01-13T10:00:00Z",
"end_date_time": "2022-01-13T11:00:00Z",
"currency": "EUR",
"tariffs": [],
"cdr_location": { "country": "NLD" },
"charging_periods": [
{
"start_date_time": "2022-01-13T10:00:00Z",
"dimensions": [
{ "type": "ENERGY", "volume": 10.0 }
]
}
],
"total_cost": { "excl_vat": total_cost_excl },
"total_time": 1.0,
"total_energy": 10.0,
"last_updated": "2022-01-13T00:00:00Z"
})
.to_string()
}
fn tariff_with_reservation_element_json(reservation_type: &str) -> String {
serde_json::json!({
"id": "T1",
"country_code": "NL",
"party_id": "TDR",
"currency": "EUR",
"elements": [
{
"price_components": [
{ "type": "ENERGY", "price": 1.00, "vat": 0.0, "step_size": 1 }
],
"restrictions": {
"reservation": reservation_type
}
},
{
"price_components": [
{ "type": "ENERGY", "price": 0.30, "vat": 0.0, "step_size": 1 }
]
}
],
"last_updated": "2022-01-01T00:00:00Z"
})
.to_string()
}
#[test]
fn should_skip_reservation_element_and_emit_warning() {
test::setup();
let cdr_json = simple_cdr_json(3.0);
let tariff_json = tariff_with_reservation_element_json("RESERVATION");
let (cdr, _) = cdr::build(json::parse_object(&cdr_json).unwrap(), Version::V221).into_parts();
let (tariff, _) =
tariff::build(json::parse_object(&tariff_json).unwrap(), Version::V221).into_parts();
let report = cdr::price(
&cdr,
price::TariffSource::Override(vec![tariff]),
chrono_tz::Tz::UTC,
)
.unwrap_report(cdr.as_json_str());
let (report, warnings) = report.into_parts();
let total_cost = report.total_cost.calculated.unwrap();
assert_eq!(
total_cost.excl_vat,
dec!(3.00).into(),
"reservation element must not apply; fallback (0.30/kWh) should be used"
);
let id_map = warnings.path_id_map();
let has_warning = id_map
.values()
.flatten()
.any(|id| id.as_str() == "reservation_element_skipped");
assert!(
has_warning,
"expected reservation_element_skipped warning, got: {id_map:#?}"
);
}
#[test]
fn should_skip_reservation_expires_element_and_emit_warning() {
test::setup();
let cdr_json = simple_cdr_json(3.0);
let tariff_json = tariff_with_reservation_element_json("RESERVATION_EXPIRES");
let (cdr, _) = cdr::build(json::parse_object(&cdr_json).unwrap(), Version::V221).into_parts();
let (tariff, _) =
tariff::build(json::parse_object(&tariff_json).unwrap(), Version::V221).into_parts();
let report = cdr::price(
&cdr,
price::TariffSource::Override(vec![tariff]),
chrono_tz::Tz::UTC,
)
.unwrap_report(cdr.as_json_str());
let (report, warnings) = report.into_parts();
let total_cost = report.total_cost.calculated.unwrap();
assert_eq!(
total_cost.excl_vat,
dec!(3.00).into(),
"reservation_expires element must not apply; fallback (0.30/kWh) should be used"
);
let id_map = warnings.path_id_map();
let has_warning = id_map
.values()
.flatten()
.any(|id| id.as_str() == "reservation_element_skipped");
assert!(
has_warning,
"expected reservation_element_skipped warning, got: {id_map:#?}"
);
}
fn tariff_without_reservation_element_json() -> String {
serde_json::json!({
"id": "T2",
"country_code": "NL",
"party_id": "TDR",
"currency": "EUR",
"elements": [
{
"price_components": [
{ "type": "ENERGY", "price": 0.30, "vat": 0.0, "step_size": 1 }
]
}
],
"last_updated": "2022-01-01T00:00:00Z"
})
.to_string()
}
#[test]
fn should_not_emit_reservation_warning_for_regular_tariff() {
test::setup();
let cdr_json = simple_cdr_json(3.0);
let tariff_json = tariff_without_reservation_element_json();
let (cdr, _) = cdr::build(json::parse_object(&cdr_json).unwrap(), Version::V221).into_parts();
let (tariff, _) =
tariff::build(json::parse_object(&tariff_json).unwrap(), Version::V221).into_parts();
let report = cdr::price(
&cdr,
price::TariffSource::Override(vec![tariff]),
chrono_tz::Tz::UTC,
)
.unwrap_report(cdr.as_json_str());
let (_report, warnings) = report.into_parts();
let id_map = warnings.path_id_map();
let has_warning = id_map
.values()
.flatten()
.any(|id| id.as_str() == "reservation_element_skipped");
assert!(
!has_warning,
"unexpected reservation_element_skipped warning: {id_map:#?}"
);
}