ocpi-tariffs 0.53.0

OCPI tariff calculations
Documentation
//! Tests for [`edits`]: turning a set of warnings into the edits that resolve them.
//!
//! The warning used here is local to the test, so what is asserted is the driver's own
//! behaviour rather than any particular warning's policy about what it wants fixed.

#![allow(
    clippy::indexing_slicing,
    clippy::panic,
    clippy::unwrap_used,
    reason = "test code with known-structure parsed data"
)]

use crate::{json, warning};

use super::{edits, Edit, Error, Fixable};

/// A warning that asks for the element it was raised on to be removed.
#[derive(Debug)]
struct Removable;

impl crate::Warning for Removable {
    fn id(&self) -> warning::Id {
        warning::Id::from_static("removable")
    }
}

impl std::fmt::Display for Removable {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str("This element can be removed.")
    }
}

impl super::sealed::Sealed for Removable {}

impl Fixable for Removable {
    fn fix(&self, element: &json::Element<'_>) -> Option<Edit> {
        Some(Edit::remove(element.id()))
    }
}

/// A warning that nothing mechanical resolves.
#[derive(Debug)]
struct Unfixable;

impl crate::Warning for Unfixable {
    fn id(&self) -> warning::Id {
        warning::Id::from_static("unfixable")
    }
}

impl std::fmt::Display for Unfixable {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str("This element needs a human.")
    }
}

impl super::sealed::Sealed for Unfixable {}

impl Fixable for Unfixable {
    fn fix(&self, _element: &json::Element<'_>) -> Option<Edit> {
        None
    }
}

fn field(doc: &json::Document<'_>, key: &str) -> json::ElemId {
    doc.root().find_field(key).unwrap().element().id()
}

#[test]
fn an_empty_warning_set_proposes_no_edits() {
    let doc = json::parse_object(r#"{"a": 1}"#).unwrap();
    let warnings = warning::Set::<Removable>::new();

    assert_eq!(edits(&doc, &warnings), Ok(Vec::new()));
}

#[test]
fn a_warning_that_resolves_to_nothing_proposes_no_edits() {
    let doc = json::parse_object(r#"{"a": 1}"#).unwrap();
    let mut warnings = warning::Set::<Unfixable>::new();

    warnings.insert(doc.root().find_field("a").unwrap().element(), Unfixable);

    assert_eq!(edits(&doc, &warnings), Ok(Vec::new()));
}

#[test]
fn edits_come_back_in_document_order() {
    let doc = json::parse_object(r#"{"a": 1, "b": 2, "c": 3}"#).unwrap();
    let mut warnings = warning::Set::<Removable>::new();

    // Inserted out of order, to prove the order comes from the element's position.
    warnings.insert(doc.root().find_field("c").unwrap().element(), Removable);
    warnings.insert(doc.root().find_field("a").unwrap().element(), Removable);

    assert_eq!(
        edits(&doc, &warnings),
        Ok(vec![
            Edit::remove(field(&doc, "a")),
            Edit::remove(field(&doc, "c")),
        ])
    );
}

/// Every warning on one element is asked, not just the first.
#[test]
fn each_warning_on_an_element_is_asked_for_an_edit() {
    let doc = json::parse_object(r#"{"a": 1}"#).unwrap();
    let mut warnings = warning::Set::<Removable>::new();
    let element = doc.root().find_field("a").unwrap().element();

    warnings.insert(element, Removable);
    warnings.insert(element, Removable);

    assert_eq!(
        edits(&doc, &warnings),
        Ok(vec![Edit::remove(element.id()), Edit::remove(element.id()),])
    );
}

#[test]
fn a_warning_from_another_document_is_reported() {
    let other = json::parse_object(r#"{"a": 1, "b": 2, "c": 3, "d": 4}"#).unwrap();
    let doc = json::parse_object(r#"{"a": 1}"#).unwrap();
    let mut warnings = warning::Set::<Removable>::new();

    let stranger = other.root().find_field("d").unwrap().element();
    warnings.insert(stranger, Removable);

    assert_eq!(
        edits(&doc, &warnings),
        Err(Error::UnknownElement(stranger.id()))
    );
}