#![allow(
clippy::indexing_slicing,
clippy::unwrap_in_result,
clippy::panic,
clippy::unwrap_used,
reason = "test code with known-structure parsed data"
)]
use std::path::Path;
use crate::{
cdr, json,
price::{self, test::UnwrapReport as _},
tariff, test, Version,
};
use super::{apply, edits};
const VERSION: Version = Version::V221;
const TIMEZONE: chrono_tz::Tz = chrono_tz::Tz::Europe__Amsterdam;
#[test_each::file(
glob = "ocpi-tariffs/test_data/v221/fix/preserving/*/tariff.json",
name(segments = 2)
)]
fn fixing_a_tariff_does_not_change_what_a_session_costs(tariff_json: &str, path: &Path) {
test::setup();
let case = Case::at(path);
let tariff_json_fixed = parse_validate_fix(tariff_json);
case.assert_fixed_text(&tariff_json_fixed);
assert_eq!(
priced(&case.cdr_json, tariff_json),
priced(&case.cdr_json, &tariff_json_fixed),
"fixing the tariff changed the report"
);
assert_no_more_fixes(&tariff_json_fixed);
}
#[test_each::file(
glob = "ocpi-tariffs/test_data/v221/fix/repairing/*/tariff.json",
name(segments = 2)
)]
fn fixing_a_tariff_makes_an_unpriceable_session_priceable(tariff_json: &str, path: &Path) {
test::setup();
let case = Case::at(path);
let tariff_json_fixed = parse_validate_fix(tariff_json);
case.assert_fixed_text(&tariff_json_fixed);
assert!(
prices(&case.cdr_json, tariff_json).is_err(),
"the tariff was expected not to price before it was fixed"
);
assert!(
prices(&case.cdr_json, &tariff_json_fixed).is_ok(),
"the fixed tariff was expected to price"
);
assert_no_more_fixes(&tariff_json_fixed);
}
struct Case {
cdr_json: String,
expected: String,
}
impl Case {
fn at(tariff_path: &Path) -> Self {
let dir = tariff_path
.parent()
.expect("a tariff lives in a case directory");
Self {
cdr_json: read(&dir.join("cdr.json")),
expected: read(&dir.join("tariff_fixed.json")),
}
}
#[track_caller]
fn assert_fixed_text(&self, fixed: &str) {
assert_eq!(
fixed, self.expected,
"the fix did not leave the expected text"
);
}
}
#[track_caller]
fn assert_no_more_fixes(tariff_fixed_json: &str) {
assert_eq!(
parse_validate_fix(tariff_fixed_json),
tariff_fixed_json,
"fixing is not idempotent"
);
assert_eq!(remaining_edits(tariff_fixed_json), Vec::new());
}
fn read(path: &Path) -> String {
std::fs::read_to_string(path)
.unwrap_or_else(|err| panic!("unable to read `{}`:\n{err}", path.display()))
}
fn parse_validate_fix(tariff_json: &str) -> String {
let doc = json::parse_object(tariff_json).unwrap();
let (tariff, schema_warnings) = tariff::from_json(doc, VERSION).into_parts();
let doc = tariff.as_doc();
let lint_warnings = tariff::lint(&tariff).warnings;
let mut proposed = edits(doc, &schema_warnings).unwrap();
proposed.extend(edits(doc, &lint_warnings).unwrap());
apply(doc, &proposed).unwrap()
}
fn remaining_edits(tariff_json: &str) -> Vec<super::Edit> {
let doc = json::parse_object(tariff_json).unwrap();
let (tariff, schema_warnings) = tariff::from_json(doc, VERSION).into_parts();
let doc = tariff.as_doc();
let lint_warnings = tariff::lint(&tariff).warnings;
let mut proposed = edits(doc, &schema_warnings).unwrap();
proposed.extend(edits(doc, &lint_warnings).unwrap());
proposed
}
fn prices(cdr_json: &str, tariff_json: &str) -> Result<(), String> {
let doc = json::parse_object(tariff_json).unwrap();
let tariff = tariff::from_json(doc, VERSION).ignore_warnings();
let doc = json::parse_object(cdr_json).unwrap();
let cdr = cdr::from_json(doc, VERSION).ignore_warnings();
match price::cdr(&cdr, price::TariffSource::Override(vec![tariff]), TIMEZONE) {
Ok(_report) => Ok(()),
Err(errors) => Err(format!("{:?}", errors.into_parts().0)),
}
}
fn priced(cdr_json: &str, tariff_json: &str) -> String {
let doc = json::parse_object(tariff_json).unwrap();
let tariff = tariff::from_json(doc, VERSION).ignore_warnings();
let doc = json::parse_object(cdr_json).unwrap();
let cdr = cdr::from_json(doc, VERSION).ignore_warnings();
let report = price::cdr(&cdr, price::TariffSource::Override(vec![tariff]), TIMEZONE)
.unwrap_report(cdr.as_json_str());
rendered(&report.ignore_warnings())
}
fn rendered(report: &price::Report) -> String {
let price::Report {
periods,
tariff_used,
tariff_reports,
timezone,
billed_charging_time,
billed_energy,
billed_idle_time,
total_charging_time,
total_energy,
total_idle_time,
total_time,
total_cost,
total_energy_cost,
total_fixed_cost,
total_idle_cost,
total_charging_time_cost,
} = report;
format!(
"periods: {periods:#?}\n\
tariff_used: {tariff_used:#?}\n\
tariff_reports: {tariff_reports:#?}\n\
timezone: {timezone:#?}\n\
billed_charging_time: {billed_charging_time:#?}\n\
billed_energy: {billed_energy:#?}\n\
billed_idle_time: {billed_idle_time:#?}\n\
total_charging_time: {total_charging_time:#?}\n\
total_energy: {total_energy:#?}\n\
total_idle_time: {total_idle_time:#?}\n\
total_time: {total_time:#?}\n\
total_cost: {total_cost:#?}\n\
total_energy_cost: {total_energy_cost:#?}\n\
total_fixed_cost: {total_fixed_cost:#?}\n\
total_idle_cost: {total_idle_cost:#?}\n\
total_charging_time_cost: {total_charging_time_cost:#?}\n"
)
}