#![deny(
missing_docs,
unused_imports,
missing_debug_implementations,
missing_copy_implementations,
trivial_casts,
trivial_numeric_casts,
unsafe_code,
unstable_features,
unused_import_braces,
unused_qualifications,
unknown_lints
)]
#![doc(html_root_url = "https://docs.rs/assert-json-diff/1.0.2")]
extern crate serde;
#[allow(unused_imports)]
#[macro_use]
extern crate serde_json;
use diff::diff;
use serde_json::Value;
mod core_ext;
mod diff;
#[macro_export]
macro_rules! assert_json_include {
(actual: $actual:expr, expected: $expected:expr) => {{
let actual: serde_json::Value = $actual;
let expected: serde_json::Value = $expected;
if let Err(error) = $crate::assert_json_no_panic(actual, expected, $crate::Mode::Lenient) {
panic!("\n\n{}\n\n", error);
}
}};
(actual: $actual:expr, expected: $expected:expr,) => {{
$crate::assert_json_include!(actual: $actual, expected: $expected)
}};
(expected: $expected:expr, actual: $actual:expr) => {{
$crate::assert_json_include!(actual: $actual, expected: $expected)
}};
(expected: $expected:expr, actual: $actual:expr,) => {{
$crate::assert_json_include!(actual: $actual, expected: $expected)
}};
}
#[macro_export]
macro_rules! assert_json_eq {
($lhs:expr, $rhs:expr) => {{
let actual: serde_json::Value = $lhs;
let expected: serde_json::Value = $rhs;
if let Err(error) = $crate::assert_json_no_panic(actual, expected, $crate::Mode::Strict) {
panic!("\n\n{}\n\n", error);
}
}};
($lhs:expr, $rhs:expr,) => {{
$crate::assert_json_eq!($lhs, $rhs)
}};
}
#[doc(hidden)]
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Mode {
Lenient,
Strict,
}
#[doc(hidden)]
pub fn assert_json_no_panic(lhs: Value, rhs: Value, mode: Mode) -> Result<(), String> {
let diffs = diff(&lhs, &rhs, mode);
if diffs.is_empty() {
Ok(())
} else {
let msg = diffs
.into_iter()
.map(|d| d.to_string())
.collect::<Vec<_>>()
.join("\n\n");
Err(msg)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fmt::Write;
#[test]
fn boolean_root() {
let result = test_partial_match(json!(true), json!(true));
assert_output_eq(result, Ok(()));
let result = test_partial_match(json!(false), json!(false));
assert_output_eq(result, Ok(()));
let result = test_partial_match(json!(false), json!(true));
assert_output_eq(
result,
Err(r#"json atoms at path "(root)" are not equal:
expected:
true
actual:
false"#),
);
let result = test_partial_match(json!(true), json!(false));
assert_output_eq(
result,
Err(r#"json atoms at path "(root)" are not equal:
expected:
false
actual:
true"#),
);
}
#[test]
fn string_root() {
let result = test_partial_match(json!("true"), json!("true"));
assert_output_eq(result, Ok(()));
let result = test_partial_match(json!("false"), json!("false"));
assert_output_eq(result, Ok(()));
let result = test_partial_match(json!("false"), json!("true"));
assert_output_eq(
result,
Err(r#"json atoms at path "(root)" are not equal:
expected:
"true"
actual:
"false""#),
);
let result = test_partial_match(json!("true"), json!("false"));
assert_output_eq(
result,
Err(r#"json atoms at path "(root)" are not equal:
expected:
"false"
actual:
"true""#),
);
}
#[test]
fn number_root() {
let result = test_partial_match(json!(1), json!(1));
assert_output_eq(result, Ok(()));
let result = test_partial_match(json!(0), json!(0));
assert_output_eq(result, Ok(()));
let result = test_partial_match(json!(0), json!(1));
assert_output_eq(
result,
Err(r#"json atoms at path "(root)" are not equal:
expected:
1
actual:
0"#),
);
let result = test_partial_match(json!(1), json!(0));
assert_output_eq(
result,
Err(r#"json atoms at path "(root)" are not equal:
expected:
0
actual:
1"#),
);
}
#[test]
fn null_root() {
let result = test_partial_match(json!(null), json!(null));
assert_output_eq(result, Ok(()));
let result = test_partial_match(json!(null), json!(1));
assert_output_eq(
result,
Err(r#"json atoms at path "(root)" are not equal:
expected:
1
actual:
null"#),
);
let result = test_partial_match(json!(1), json!(null));
assert_output_eq(
result,
Err(r#"json atoms at path "(root)" are not equal:
expected:
null
actual:
1"#),
);
}
#[test]
fn into_object() {
let result = test_partial_match(json!({ "a": true }), json!({ "a": true }));
assert_output_eq(result, Ok(()));
let result = test_partial_match(json!({ "a": false }), json!({ "a": true }));
assert_output_eq(
result,
Err(r#"json atoms at path ".a" are not equal:
expected:
true
actual:
false"#),
);
let result =
test_partial_match(json!({ "a": { "b": true } }), json!({ "a": { "b": true } }));
assert_output_eq(result, Ok(()));
let result = test_partial_match(json!({ "a": true }), json!({ "a": { "b": true } }));
assert_output_eq(
result,
Err(r#"json atoms at path ".a" are not equal:
expected:
{
"b": true
}
actual:
true"#),
);
let result = test_partial_match(json!({}), json!({ "a": true }));
assert_output_eq(
result,
Err(r#"json atom at path ".a" is missing from actual"#),
);
let result = test_partial_match(json!({ "a": { "b": true } }), json!({ "a": true }));
assert_output_eq(
result,
Err(r#"json atoms at path ".a" are not equal:
expected:
true
actual:
{
"b": true
}"#),
);
}
#[test]
fn into_array() {
let result = test_partial_match(json!([1]), json!([1]));
assert_output_eq(result, Ok(()));
let result = test_partial_match(json!([2]), json!([1]));
assert_output_eq(
result,
Err(r#"json atoms at path "[0]" are not equal:
expected:
1
actual:
2"#),
);
let result = test_partial_match(json!([1, 2, 4]), json!([1, 2, 3]));
assert_output_eq(
result,
Err(r#"json atoms at path "[2]" are not equal:
expected:
3
actual:
4"#),
);
let result = test_partial_match(json!({ "a": [1, 2, 3]}), json!({ "a": [1, 2, 4]}));
assert_output_eq(
result,
Err(r#"json atoms at path ".a[2]" are not equal:
expected:
4
actual:
3"#),
);
let result = test_partial_match(json!({ "a": [1, 2, 3]}), json!({ "a": [1, 2]}));
assert_output_eq(result, Ok(()));
let result = test_partial_match(json!({ "a": [1, 2]}), json!({ "a": [1, 2, 3]}));
assert_output_eq(
result,
Err(r#"json atom at path ".a[2]" is missing from actual"#),
);
}
#[test]
fn exact_matching() {
let result = test_exact_match(json!(true), json!(true));
assert_output_eq(result, Ok(()));
let result = test_exact_match(json!("s"), json!("s"));
assert_output_eq(result, Ok(()));
let result = test_exact_match(json!("a"), json!("b"));
assert_output_eq(
result,
Err(r#"json atoms at path "(root)" are not equal:
lhs:
"a"
rhs:
"b""#),
);
let result = test_exact_match(
json!({ "a": [1, { "b": 2 }] }),
json!({ "a": [1, { "b": 3 }] }),
);
assert_output_eq(
result,
Err(r#"json atoms at path ".a[1].b" are not equal:
lhs:
2
rhs:
3"#),
);
}
#[test]
fn exact_match_output_message() {
let result = test_exact_match(json!({ "a": { "b": 1 } }), json!({ "a": {} }));
assert_output_eq(
result,
Err(r#"json atom at path ".a.b" is missing from rhs"#),
);
let result = test_exact_match(json!({ "a": {} }), json!({ "a": { "b": 1 } }));
assert_output_eq(
result,
Err(r#"json atom at path ".a.b" is missing from lhs"#),
);
}
fn assert_output_eq(actual: Result<(), String>, expected: Result<(), &str>) {
match (actual, expected) {
(Ok(()), Ok(())) => {}
(Err(actual_error), Ok(())) => {
let mut f = String::new();
writeln!(f, "Did not expect error, but got").unwrap();
writeln!(f, "{}", actual_error).unwrap();
panic!("{}", f);
}
(Ok(()), Err(expected_error)) => {
let expected_error = expected_error.to_string();
let mut f = String::new();
writeln!(f, "Expected error, but did not get one. Expected error:").unwrap();
writeln!(f, "{}", expected_error).unwrap();
panic!("{}", f);
}
(Err(actual_error), Err(expected_error)) => {
let expected_error = expected_error.to_string();
if actual_error != expected_error {
let mut f = String::new();
writeln!(f, "Errors didn't match").unwrap();
writeln!(f, "Expected:").unwrap();
writeln!(f, "{}", expected_error).unwrap();
writeln!(f, "Got:").unwrap();
writeln!(f, "{}", actual_error).unwrap();
panic!("{}", f);
}
}
}
}
fn test_partial_match(lhs: Value, rhs: Value) -> Result<(), String> {
assert_json_no_panic(lhs, rhs, Mode::Lenient)
}
fn test_exact_match(lhs: Value, rhs: Value) -> Result<(), String> {
assert_json_no_panic(lhs, rhs, Mode::Strict)
}
}