ocpi-tariffs 0.53.0

OCPI tariff calculations
Documentation
use std::collections::BTreeMap;

use crate::{
    json, schema, test,
    test::Expectation,
    warning::test::{assert_path_map_warnings, build_warnings, expect},
};

/// A tariff whose `currency` and whose every `step_round` draw a warning.
const TARIFF: &str = r#"{
    "party_id": "ENE",
    "currency": "EUR",
    "elements": [
        { "price_components": [ { "step_round": 1 } ] },
        { "price_components": [ { "step_round": 1 } ] }
    ]
}"#;

/// Where the expectations would have been written; it only appears in panic messages.
const EXPECTATION: &str = "`tariff_reports` in the `output_price__cdr.json` file";

/// A path map is matched against the same expectation a `Set` would be.
#[test]
fn exact_paths_match_their_own_element() {
    test::setup();

    let doc = json::parse_object(TARIFF).unwrap();
    let warnings = build_warnings(&doc).into_path_map();

    let expect = expect(vec![
        ("$.currency", vec!["null_field"]),
        (
            "$.elements[0].price_components[0].step_round",
            vec!["unexpected_field"],
        ),
        (
            "$.elements[1].price_components[0].step_round",
            vec!["unexpected_field"],
        ),
    ]);

    assert_path_map_warnings(EXPECTATION, &warnings, expect);
}

/// The path map reaches the same glob matching, so a wildcard entry stands in for every element
/// it matches here too.
#[test]
fn a_wildcard_entry_covers_every_element_it_matches() {
    test::setup();

    let doc = json::parse_object(TARIFF).unwrap();
    let warnings = build_warnings(&doc).into_path_map();

    let expect = expect(vec![
        ("$.currency", vec!["null_field"]),
        (
            "$.elements.*.price_components[0].step_round",
            vec!["unexpected_field"],
        ),
    ]);

    assert_path_map_warnings(EXPECTATION, &warnings, expect);
}

/// An element the expectation does not name fails, as it does for a `Set`.
#[test]
#[should_panic(expected = "Elements with warnings that no entry of `tariff_reports`")]
fn an_element_matched_by_no_entry_fails() {
    test::setup();

    let doc = json::parse_object(TARIFF).unwrap();
    let warnings = build_warnings(&doc).into_path_map();

    let expect = expect(vec![(
        "$.elements.*.price_components[0].step_round",
        vec!["unexpected_field"],
    )]);

    assert_path_map_warnings(EXPECTATION, &warnings, expect);
}

/// An entry that matches nothing is a stale expectation here too.
#[test]
#[should_panic(expected = "that match no element with warnings")]
fn an_entry_matching_no_element_fails() {
    test::setup();

    let doc = json::parse_object(TARIFF).unwrap();
    let warnings = build_warnings(&doc).into_path_map();

    let expect = expect(vec![
        ("$.currency", vec!["null_field"]),
        ("$.party_id", vec!["null_field"]),
        (
            "$.elements.*.price_components[0].step_round",
            vec!["unexpected_field"],
        ),
    ]);

    assert_path_map_warnings(EXPECTATION, &warnings, expect);
}

/// The ids are what the expectation is compared against, so a wrong one fails.
#[test]
#[should_panic(expected = "Elements whose warnings are not the warnings listed by")]
fn an_entry_listing_the_wrong_ids_fails() {
    test::setup();

    let doc = json::parse_object(TARIFF).unwrap();
    let warnings = build_warnings(&doc).into_path_map();

    let expect = expect(vec![
        ("$.currency", vec!["unexpected_field"]),
        (
            "$.elements.*.price_components[0].step_round",
            vec!["unexpected_field"],
        ),
    ]);

    assert_path_map_warnings(EXPECTATION, &warnings, expect);
}

/// The adapter carries the message of each warning, not only its id, so a failure says what the
/// warning was about.
#[test]
#[should_panic(expected = "`null` fields have no semantic meaning for OCPI objects")]
fn a_failure_shows_what_the_warning_says() {
    test::setup();

    let doc = json::parse_object(TARIFF).unwrap();
    let warnings = build_warnings(&doc).into_path_map();

    assert_path_map_warnings(EXPECTATION, &warnings, Expectation::Absent);
}

/// An absent expectation and an empty map agree.
#[test]
fn an_absent_expectation_passes_when_there_are_no_warnings() {
    test::setup();

    let warnings = BTreeMap::<json::Path, Vec<schema::Warning>>::new();

    assert_path_map_warnings(EXPECTATION, &warnings, Expectation::Absent);
}