ocpi-tariffs 0.53.0

OCPI tariff calculations
Documentation
//! Tests for [`Json`], the value an [`Edit`](super::Edit) writes in place of an element.
//!
//! The type exists so a replacement is one valid JSON value by construction. Its constructors take
//! the content to write rather than JSON source, so the tests here are of the writing: what comes
//! out has to parse, and it has to still hold the content that went in.

use crate::json;

use super::Json;

/// The content a caller passes has to arrive in the document unchanged, so each value is parsed
/// back out of the built array and decoded, which is the only test that says the escaping is right
/// rather than merely present.
fn round_trip(values: &[&str]) -> Vec<String> {
    let json = Json::string_array(values);
    let doc = json::parse(json.as_str().into()).expect("a built array is JSON");
    let items = doc.root().as_array().expect("the value is an array");
    let mut decoded: Vec<String> = Vec::with_capacity(items.len());

    for item in items {
        let raw = item.to_raw_str().expect("each item is a string");

        decoded.push(raw.decode_escapes().ignore_warnings().into_owned());
    }

    decoded
}

#[test]
fn an_array_of_days_is_quoted_and_comma_separated() {
    let json = Json::string_array(&["MONDAY", "FRIDAY"]);

    assert_eq!(json.as_str(), r#"["MONDAY", "FRIDAY"]"#);
}

#[test]
fn an_empty_array_is_written_as_empty_brackets() {
    let json = Json::string_array(&[]);

    assert_eq!(json.as_str(), "[]");
}

/// The old fallible constructor left escaping to whichever producer built the text by hand. The
/// constructor owes it now, so a quote in the content is one quote in the document and not the end
/// of the string.
#[test]
fn a_quote_in_a_value_is_escaped() {
    let json = Json::string_array(&[r#"a"b"#]);

    assert_eq!(json.as_str(), r#"["a\"b"]"#);
    assert_eq!(round_trip(&[r#"a"b"#]), vec![r#"a"b"#.to_owned()]);
}

/// A single backslash in the content has to be written as two, which is the case that made the
/// hand-written payloads worth removing: `"a\\b"` and `"a\b"` both parse and hold different
/// strings, so only the round trip says which one was built.
#[test]
fn a_backslash_in_a_value_is_escaped() {
    let json = Json::string_array(&[r"a\b"]);

    assert_eq!(json.as_str(), r#"["a\\b"]"#);
    assert_eq!(round_trip(&[r"a\b"]), vec![r"a\b".to_owned()]);
}

/// JSON forbids a literal control character inside a string, so each one is written as an escape,
/// and a control character with no shorthand takes the `\u` form.
///
/// This case asserts the text only. `round_trip` cannot cover it: `json::decode` raises
/// `ControlCharacter` for any control character an escape decodes to, the legal `\n` and `\t`
/// shorthands included, and returns the text undecoded when it does.
#[test]
fn control_characters_are_escaped() {
    let json = Json::string_array(&["a\nb\tc\u{1}d"]);

    assert_eq!(json.as_str(), r#"["a\nb\tc\u0001d"]"#);
}

/// A value that is nothing but escapes exercises every branch that has a shorthand.
#[test]
fn every_shorthand_escape_is_used() {
    let json = Json::string_array(&["\u{8}\u{c}\n\r\t"]);

    assert_eq!(json.as_str(), r#"["\b\f\n\r\t"]"#);
}

#[test]
fn a_value_needing_no_escape_is_written_as_it_came() {
    assert_eq!(round_trip(&["NL", "de", "17"]), vec!["NL", "de", "17"]);
}

/// The guard behind every constructor. Nothing reachable can trip it, so the test reaches for the
/// verbatim constructor to prove the assertion is live rather than decorative. It is compiled out
/// of a release build along with the assertion it is testing.
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "not a single JSON value")]
fn text_that_is_not_one_value_trips_the_debug_assertion() {
    let _json = Json::raw("{oops");
}

/// Two values have room for neither the second value nor the comma before it, so the guard covers
/// that as well as text that does not parse at all.
#[test]
#[cfg(debug_assertions)]
#[should_panic(expected = "not a single JSON value")]
fn more_than_one_value_trips_the_debug_assertion() {
    let _json = Json::raw("1 2");
}