ocpi-tariffs 0.52.0

OCPI tariff calculations
Documentation
//! Tests for the `day_of_week` field: how the schema builder validates it and how the
//! resulting IR lowers into a `Weekday`.
//!
//! The kind check, the variant-membership check and the case-insensitive match all live in
//! the builder now, so the lowering is an infallible variant map. Each test drives a real
//! `v2.2.1` tariff through `build_tariff` and inspects the restrictions' `day_of_week`.

#![allow(
    clippy::indexing_slicing,
    reason = "unwraps and indexing are allowed anywhere in tests"
)]

use std::assert_matches;

use super::Weekday;
use crate::{
    json,
    schema::{self, Integrity},
    test, FromSchema as _,
};

/// A minimal, valid `v2.2.1` tariff. `{DAYS}` is replaced by each test with the literal
/// JSON to place in the `day_of_week` position.
const TARIFF: &str = r#"{
    "country_code": "NL",
    "party_id": "ENE",
    "currency": "EUR",
    "id": "ID",
    "last_updated": "2024-01-01T00:00:00Z",
    "elements": [
        {
            "price_components": [{"price": 0.25, "step_size": 1, "type": "ENERGY", "vat": 21}],
            "restrictions": {"day_of_week": {DAYS}}
        }
    ]
}"#;

/// The JSON path every test asserts against.
const DAY_PATH: &str = "$.elements[0].restrictions.day_of_week[0]";

/// Borrow the restrictions' `day_of_week` list out of a tariff.
fn day_of_week<'a, 'buf>(
    tariff: &'a schema::v221::Tariff<'buf>,
) -> &'a schema::List<'buf, schema::Enum<'buf, schema::v221::DayOfWeek>> {
    let Integrity::Ok(elements) = &tariff.elements else {
        panic!("elements should be built: {:?}", tariff.elements);
    };
    let Integrity::Ok(element) = &elements[0] else {
        panic!("the element should be built");
    };
    let Integrity::Ok(Some(restrictions)) = &element.restrictions else {
        panic!("restrictions should be built: {:?}", element.restrictions);
    };
    let Integrity::Ok(Some(days)) = &restrictions.day_of_week else {
        panic!(
            "day_of_week should be built: {:?}",
            restrictions.day_of_week
        );
    };
    days
}

#[test]
fn spec_value_lowers_to_weekday() {
    test::setup();

    let src = TARIFF.replace("{DAYS}", r#"["MONDAY"]"#);
    let doc = json::parse(src.as_str().into()).unwrap();
    let tariff = schema::v221::build_tariff(&doc).ignore_warnings();

    let days = day_of_week(&tariff);
    let Integrity::Ok(day) = &days[0] else {
        panic!("the day should be built: {:?}", days[0]);
    };

    let day = Weekday::from_schema(&day.value()).unwrap().unwrap();
    assert_matches!(day, Weekday::Monday);
}

#[test]
fn lower_case_value_is_accepted_without_warning() {
    test::setup();

    let src = TARIFF.replace("{DAYS}", r#"["sunday"]"#);
    let doc = json::parse(src.as_str().into()).unwrap();
    let (tariff, warnings) = schema::v221::build_tariff(&doc).into_parts();

    let days = day_of_week(&tariff);
    let Integrity::Ok(day) = &days[0] else {
        panic!("the day should be built: {:?}", days[0]);
    };

    let day = Weekday::from_schema(&day.value()).unwrap().unwrap();
    assert_matches!(day, Weekday::Sunday);

    // The builder matches variants case-insensitively and says nothing about the case. The
    // `prefer_upper_case` advice this used to carry is a lint concern; see
    // `docs/lint-catalogue.md`.
    assert!(warnings.path_map().is_empty(), "{:#?}", warnings.path_map());
}

#[test]
fn non_string_value_is_a_type_mismatch() {
    test::setup();

    let src = TARIFF.replace("{DAYS}", "[[]]");
    let doc = json::parse(src.as_str().into()).unwrap();
    let (tariff, warnings) = schema::v221::build_tariff(&doc).into_parts();

    assert_matches!(day_of_week(&tariff)[0], Integrity::Err(_));

    let warnings = warnings.path_map();
    assert_matches!(*warnings[DAY_PATH], [schema::Warning::TypeMismatch { .. }]);
}

#[test]
fn unknown_variant_is_an_invalid_value() {
    test::setup();

    let src = TARIFF.replace("{DAYS}", r#"["MOONDAY"]"#);
    let doc = json::parse(src.as_str().into()).unwrap();
    let (tariff, warnings) = schema::v221::build_tariff(&doc).into_parts();

    assert_matches!(day_of_week(&tariff)[0], Integrity::Err(_));

    let warnings = warnings.path_map();
    assert_matches!(
        *warnings[DAY_PATH],
        [schema::Warning::FieldInvalidValue { .. }]
    );
}