use crate::JsonType;
use crate::expect::ExpectObject;
use crate::expect::ops::expect_array::ExpectArraySubOp;
use crate::expect_core::Context;
use crate::expect_core::ExpectOp;
use crate::expect_core::ExpectOpResult;
use crate::expect_core::expect_op;
use serde_json::Value;
use std::fmt::Debug;
#[expect_op(internal, name = "array")]
#[derive(Debug, Clone, Default, PartialEq)]
pub struct ExpectArray {
sub_ops: Vec<ExpectArraySubOp>,
}
impl ExpectArray {
pub(crate) fn new() -> Self {
Self { sub_ops: vec![] }
}
pub fn empty(mut self) -> Self {
self.sub_ops.push(ExpectArraySubOp::Empty);
self
}
pub fn not_empty(mut self) -> Self {
self.sub_ops.push(ExpectArraySubOp::NotEmpty);
self
}
pub fn len(mut self, len: usize) -> Self {
self.sub_ops.push(ExpectArraySubOp::Len(len));
self
}
pub fn min_len(mut self, min_len: usize) -> Self {
self.sub_ops.push(ExpectArraySubOp::MinLen(min_len));
self
}
pub fn max_len(mut self, max_len: usize) -> Self {
self.sub_ops.push(ExpectArraySubOp::MaxLen(max_len));
self
}
pub fn contains<I, V>(mut self, expected_values: I) -> Self
where
I: IntoIterator<Item = V>,
V: Into<Value>,
{
let inner_expected_values = expected_values
.into_iter()
.map(Into::into)
.collect::<Vec<_>>();
self.sub_ops
.push(ExpectArraySubOp::Contains(inner_expected_values));
self
}
pub fn eq_unordered<I, V>(mut self, expected_values: I) -> Self
where
I: IntoIterator<Item = V>,
V: Into<Value>,
{
let inner_expected_values = expected_values
.into_iter()
.map(Into::into)
.collect::<Vec<_>>();
self.sub_ops
.push(ExpectArraySubOp::EqUnordered(inner_expected_values));
self
}
pub fn all<V>(mut self, expected: V) -> Self
where
V: Into<Value>,
{
self.sub_ops
.push(ExpectArraySubOp::AllEqual(expected.into()));
self
}
pub fn all_contains<V>(mut self, expected: V) -> Self
where
V: Into<Value>,
{
let obj_contains = ExpectObject::new().contains(expected);
self.sub_ops
.push(ExpectArraySubOp::AllEqual(obj_contains.into()));
self
}
pub fn unique(mut self) -> Self {
self.sub_ops.push(ExpectArraySubOp::AllUnique);
self
}
}
impl ExpectOp for ExpectArray {
fn on_array(&self, context: &mut Context, received: &[Value]) -> ExpectOpResult<()> {
for sub_op in &self.sub_ops {
sub_op.on_array(self, context, received)?;
}
Ok(())
}
fn debug_supported_types(&self) -> &'static [JsonType] {
&[JsonType::Array]
}
}
#[cfg(test)]
mod test_contains {
use crate::expect;
use crate::expect_json_eq;
use pretty_assertions::assert_eq;
use serde_json::json;
#[test]
fn it_should_be_equal_for_identical_numeric_arrays() {
let left = json!([1, 2, 3]);
let right = json!(expect::array().contains([1, 2, 3]));
let output = expect_json_eq(&left, &right);
assert!(output.is_ok());
}
#[test]
fn it_should_be_equal_for_reversed_identical_numeric_arrays() {
let left = json!([1, 2, 3]);
let right = json!(expect::array().contains([3, 2, 1]));
let output = expect_json_eq(&left, &right);
assert!(output.is_ok());
}
#[test]
fn it_should_be_equal_for_partial_contains() {
let left = json!([0, 1, 2, 3, 4, 5]);
let right = json!(expect::array().contains([1, 2, 3]));
let output = expect_json_eq(&left, &right);
assert!(output.is_ok());
}
#[test]
fn it_should_error_for_totall_different_values() {
let left = json!([0, 1, 2, 3]);
let right = json!(expect::array().contains([4, 5, 6]));
let output = expect_json_eq(&left, &right).unwrap_err().to_string();
assert_eq!(
output,
r#"Json array at root does not contain expected value:
expected array to contain 4, but it was not found.
received [0, 1, 2, 3]"#
);
}
#[test]
fn it_should_be_ok_for_empty_contains() {
let left = json!([0, 1, 2, 3]);
let right = json!(expect::array().contains([] as [u32; 0]));
let output = expect_json_eq(&left, &right);
assert!(output.is_ok());
}
#[test]
fn it_should_error_if_used_against_the_wrong_type() {
let left = json!("🦊");
let right = json!(expect::array().contains([4, 5, 6]));
let output = expect_json_eq(&left, &right).unwrap_err().to_string();
assert_eq!(
output,
r#"Json expect::array() at root, received wrong type:
expected array
received string "🦊""#
);
}
#[test]
fn it_should_handle_nested_contains() {
let left = json!([
{
"text": "Hello",
"author": "Jane Candle"
},
{
"text": "Goodbye",
"author": "John Lighthouse"
}
]);
let right = json!(expect::array().contains([json!({
"text": "Hello",
"author": expect::string().contains("Jane"),
}),]));
let output = expect_json_eq(&left, &right);
assert!(output.is_ok(), "{}", output.unwrap_err().to_string());
}
#[test]
fn it_should_fail_nested_contains_that_do_not_match() {
let left = json!([
{
"text": "Hello",
"author": "Jane Candle"
},
{
"text": "Goodbye",
"author": "John Lighthouse"
}
]);
let right = json!(expect::array().contains([json!({
"text": "Hello",
"author": expect::string().contains("🦊"),
}),]));
let output = expect_json_eq(&left, &right).unwrap_err().to_string();
assert_eq!(
output,
r#"Json array at root does not contain expected value:
expected array to contain {
"author": expect::string(),
"text": "Hello"
}, but it was not found.
received [
{
"author": "Jane Candle",
"text": "Hello"
},
{
"author": "John Lighthouse",
"text": "Goodbye"
}
]"#
);
}
}
#[cfg(test)]
mod test_empty {
use crate::expect;
use crate::expect_json_eq;
use pretty_assertions::assert_eq;
use serde_json::json;
#[test]
fn it_should_pass_when_array_is_empty() {
let left = json!([]);
let right = json!(expect::array().empty());
let output = expect_json_eq(&left, &right);
assert!(output.is_ok(), "assertion error: {output:#?}");
}
#[test]
fn it_should_fail_when_array_is_not_empty() {
let left = json!([1, 2, 3]);
let right = json!(expect::array().empty());
let output = expect_json_eq(&left, &right).unwrap_err().to_string();
assert_eq!(
output,
r#"Json expect::array() error at root:
expected empty array
received [1, 2, 3]"#
);
}
}
#[cfg(test)]
mod test_not_empty {
use crate::expect;
use crate::expect_json_eq;
use pretty_assertions::assert_eq;
use serde_json::json;
#[test]
fn it_should_pass_when_array_is_not_empty() {
let left = json!([1]);
let right = json!(expect::array().not_empty());
let output = expect_json_eq(&left, &right);
assert!(output.is_ok(), "assertion error: {output:#?}");
}
#[test]
fn it_should_fail_when_array_is_empty() {
let left = json!([]);
let right = json!(expect::array().not_empty());
let output = expect_json_eq(&left, &right).unwrap_err().to_string();
assert_eq!(
output,
r#"Json expect::array() error at root:
expected non-empty array
received []"#
);
}
}
#[cfg(test)]
mod test_min_len {
use crate::expect;
use crate::expect_json_eq;
use pretty_assertions::assert_eq;
use serde_json::json;
#[test]
fn it_should_pass_when_array_has_exactly_enough_elements() {
let left = json!([1, 2, 3]);
let right = json!(expect::array().min_len(3));
let output = expect_json_eq(&left, &right);
assert!(output.is_ok(), "assertion error: {output:#?}");
}
#[test]
fn it_should_pass_when_array_has_more_than_enough_elements() {
let left = json!([1, 2, 3, 4, 5]);
let right = json!(expect::array().min_len(3));
let output = expect_json_eq(&left, &right);
assert!(output.is_ok(), "assertion error: {output:#?}");
}
#[test]
fn it_should_fail_when_array_has_too_few_elements() {
let left = json!([1, 2, 3]);
let right = json!(expect::array().min_len(4));
let output = expect_json_eq(&left, &right).unwrap_err().to_string();
assert_eq!(
output,
r#"Json expect::array() error at root:
expected array to have at least 4 elements, but it has 3.
received [1, 2, 3]"#
);
}
}
#[cfg(test)]
mod test_len {
use crate::expect;
use crate::expect_json_eq;
use pretty_assertions::assert_eq;
use serde_json::json;
#[test]
fn it_should_pass_when_array_has_exactly_enough_elements() {
let left = json!([1, 2, 3]);
let right = json!(expect::array().len(3));
let output = expect_json_eq(&left, &right);
assert!(output.is_ok(), "assertion error: {output:#?}");
}
#[test]
fn it_should_fail_when_array_has_more_than_enough_elements() {
let left = json!([1, 2, 3, 4, 5]);
let right = json!(expect::array().len(3));
let output = expect_json_eq(&left, &right).unwrap_err().to_string();
assert_eq!(
output,
r#"Json expect::array() error at root:
expected array to have 3 elements, but it has 5.
received [1, 2, 3, 4, 5]"#
);
}
#[test]
fn it_should_fail_when_array_has_too_few_elements() {
let left = json!([1, 2, 3]);
let right = json!(expect::array().len(4));
let output = expect_json_eq(&left, &right).unwrap_err().to_string();
assert_eq!(
output,
r#"Json expect::array() error at root:
expected array to have 4 elements, but it has 3.
received [1, 2, 3]"#
);
}
}
#[cfg(test)]
mod test_max_len {
use crate::expect;
use crate::expect_json_eq;
use pretty_assertions::assert_eq;
use serde_json::json;
#[test]
fn it_should_pass_when_array_has_exactly_enough_elements() {
let left = json!([1, 2, 3]);
let right = json!(expect::array().max_len(3));
let output = expect_json_eq(&left, &right);
assert!(output.is_ok(), "assertion error: {output:#?}");
}
#[test]
fn it_should_pass_when_array_has_less_than_enough_elements() {
let left = json!([1, 2]);
let right = json!(expect::array().max_len(6));
let output = expect_json_eq(&left, &right);
assert!(output.is_ok(), "assertion error: {output:#?}");
}
#[test]
fn it_should_fail_when_array_has_too_few_elements() {
let left = json!([1, 2, 3, 4]);
let right = json!(expect::array().max_len(3));
let output = expect_json_eq(&left, &right).unwrap_err().to_string();
assert_eq!(
output,
r#"Json expect::array() error at root:
expected array to have at most 3 elements, but it has 4.
received [1, 2, 3, 4]"#
);
}
}
#[cfg(test)]
mod test_eq_unordered {
use crate::expect;
use crate::expect_json_eq;
use pretty_assertions::assert_eq;
use serde_json::json;
#[test]
fn it_should_pass_when_arrays_match_unordered() {
let left = json!([1, 2, 3]);
let right = json!(expect::array().eq_unordered([3, 2, 1]));
let output = expect_json_eq(&left, &right);
assert!(output.is_ok(), "assertion error: {output:#?}");
}
#[test]
fn it_should_fail_when_arrays_do_not_match_unordered() {
let left = json!([1, 2, 3]);
let right = json!(expect::array().eq_unordered([4, 5, 6]));
let output = expect_json_eq(&left, &right).unwrap_err().to_string();
assert_eq!(
output,
r#"Json expect::array() error at root, mismatch:
expected array (up to order): [4, 5, 6],
received array: [1, 2, 3]"#
);
}
#[test]
fn it_should_fail_when_arrays_have_different_lengths() {
let left = json!([1, 2, 3]);
let right = json!(expect::array().eq_unordered([1, 2, 3, 4]));
let output = expect_json_eq(&left, &right).unwrap_err().to_string();
assert_eq!(
output,
r#"Json expect::array() error at root, mismatch:
expected array (up to order): [1, 2, 3, 4],
received array: [1, 2, 3]"#
);
}
#[test]
fn it_should_pass_with_complex_matches() {
let left = json!(["Alice", "Bob", "Charlie"]);
let right = json!(expect::array().eq_unordered([
expect::string().contains("C"),
expect::string().contains("A"),
expect::string().contains("B"),
]));
let output = expect_json_eq(&left, &right);
assert!(output.is_ok(), "assertion error: {output:#?}");
}
#[test]
fn it_should_pass_not_using_greedy_matching() {
let left = json!(["Alice", "Bob", "Charlie"]);
let right = json!(expect::array().eq_unordered([
expect::string(),
expect::string().contains("B"),
expect::string().contains("A"),
]));
let output = expect_json_eq(&left, &right);
assert!(output.is_ok(), "assertion error: {output:#?}");
}
#[test]
fn it_should_pass_with_equal_elements() {
let left = json!(["Alice", "Alice", "Alice"]);
let right = json!(expect::array().eq_unordered([
expect::string().contains("A"),
expect::string().contains("A"),
expect::string().contains("A"),
]));
let output = expect_json_eq(&left, &right);
assert!(output.is_ok(), "assertion error: {output:#?}");
}
#[test]
fn it_should_pass_on_a_multiset_with_bijection() {
let left = json!(["Alice", "Alice", "Bob"]);
let right = json!(expect::array().eq_unordered(["Bob", "Alice", "Alice"]));
let output = expect_json_eq(&left, &right);
assert!(output.is_ok(), "assertion error: {output:#?}");
}
#[test]
fn it_should_fail_when_there_is_no_bijection() {
let left = json!(["Alice", "Bob", "Boris"]);
let right = json!(expect::array().eq_unordered([
expect::string().contains("A"),
expect::string().contains("A"),
expect::string().contains("B"),
]));
let output = expect_json_eq(&left, &right).unwrap_err().to_string();
assert_eq!(
output,
r#"Json expect::array() error at root, mismatch:
expected array (up to order): [
expect::string(),
expect::string(),
expect::string()
],
received array: ["Alice", "Bob", "Boris"]"#
);
}
}
#[cfg(test)]
mod test_all {
use crate::expect;
use crate::expect_json_eq;
use pretty_assertions::assert_eq;
use serde_json::json;
#[test]
fn it_should_pass_when_array_is_empty() {
let left = json!([]);
let right = json!(expect::array().all(expect::string()));
let output = expect_json_eq(&left, &right);
assert!(output.is_ok(), "assertion error: {output:#?}");
}
#[test]
fn it_should_pass_with_mix_of_operations() {
let left = json!([
"123e4567-e89b-12d3-a456-426614174000",
"123e4567-e89b-12d3-a456-426614174001",
"123e4567-e89b-12d3-a456-426614174002",
]);
let right = json!(expect::array().all(expect::uuid()).len(3).unique());
let output = expect_json_eq(&left, &right);
assert!(output.is_ok(), "assertion error: {output:#?}");
}
#[test]
fn it_should_fail_with_mix_of_operations() {
let left = json!([
"123e4567-e89b-12d3-a456-426614174000",
"123e4567-e89b-12d3-a456-426614174001",
"123e4567-e89b-12d3-a456-426614174002",
"123e4567-e89b-12d3-a456-426614174003",
]);
let right = json!(expect::array().all(expect::uuid()).len(3).unique());
let output = expect_json_eq(&left, &right).unwrap_err().to_string();
assert_eq!(
output,
r#"Json expect::array() error at root:
expected array to have 3 elements, but it has 4.
received ["123e4567-e89b-12d3-a456-426614174000", "123e4567-e89b-12d3-a456-426614174001", "123e4567-e89b-12d3-a456-426614174002", "123e4567-e89b-12d3-a456-426614174003"]"#
);
}
#[test]
fn it_should_fail_when_array_values_do_not_match_expect_op() {
let left = json!([1, 2, 3]);
let right = json!(expect::array().all(expect::string()));
let output = expect_json_eq(&left, &right).unwrap_err().to_string();
assert_eq!(
output,
r#"Json expect::string() at root[0], received wrong type:
expected string
received integer 1
received full array [1, 2, 3]"#
);
}
#[test]
fn it_should_fail_when_array_values_do_not_match_values() {
let left = json!([1, 2, 3]);
let right = json!(expect::array().all("🦊"));
let output = expect_json_eq(&left, &right).unwrap_err().to_string();
assert_eq!(
output,
r#"Json values at root[0] are different types:
expected string "🦊"
received integer 1
received full array [1, 2, 3]"#
);
}
#[test]
fn it_should_pass_when_array_values_do_match_expect_op() {
let left = json!(["alice@example.com", "bob@example.com"]);
let right = json!(expect::array().all(expect::email()));
let output = expect_json_eq(&left, &right);
assert!(output.is_ok(), "assertion error: {output:#?}");
}
#[test]
fn it_should_pass_when_array_values_do_match_values() {
let left = json!([1, 1, 1]);
let right = json!(expect::array().all(1));
let output = expect_json_eq(&left, &right);
assert!(output.is_ok(), "assertion error: {output:#?}");
}
#[test]
fn it_should_pass_when_array_values_do_match_complex_objects() {
let left = json!([
{
"name": "Alice Candles",
"email": "alice@example.com"
},
{
"name": "Bob Kettles",
"email": "bob@example.com"
},
]);
let right = json!(expect::array().all(json!({
"name": expect::string().not_empty(),
"email": expect::email(),
})));
let output = expect_json_eq(&left, &right);
assert!(output.is_ok(), "assertion error: {output:#?}");
}
#[test]
fn it_should_not_pass_when_array_values_do_not_match_complex_objects() {
let left = json!([
{
"name": "Alice Candles",
"email": "alice@example.com"
},
{
"name": "",
"email": "bob@example.com"
},
]);
let right = json!(expect::array().all(json!({
"name": expect::string().not_empty(),
"email": expect::email(),
})));
let output = expect_json_eq(&left, &right).unwrap_err().to_string();
assert_eq!(
output,
r#"Json expect::string() error at root[1].name:
expected non-empty string
received ""
received full array [
{
"email": "alice@example.com",
"name": "Alice Candles"
},
{
"email": "bob@example.com",
"name": ""
}
]"#
);
}
#[test]
fn it_should_pass_with_nested_contains() {
let left = json!([
{
"name": "Alice Candles",
"email": "alice@example.com"
},
{
"name": "Bob Kettles",
"email": "bob@example.com"
},
]);
let right = json!(expect::array().all(expect::object().contains(json!({
"name": expect::string().not_empty(),
})),));
let output = expect_json_eq(&left, &right);
assert!(output.is_ok(), "assertion error: {output:#?}");
}
}
#[cfg(test)]
mod test_unique {
use crate::expect;
use crate::expect_json_eq;
use pretty_assertions::assert_eq;
use serde_json::json;
#[test]
fn it_should_pass_when_array_is_empty() {
let left = json!([]);
let right = json!(expect::array().unique());
let output = expect_json_eq(&left, &right);
assert!(output.is_ok(), "assertion error: {output:#?}");
}
#[test]
fn it_should_fail_when_array_is_not_unique() {
let left = json!([1, 1, 2]);
let right = json!(expect::array().unique());
let output = expect_json_eq(&left, &right).unwrap_err().to_string();
assert_eq!(
output,
r#"Json expect::array() error at root[1],
expected array to contain all unique values.
found duplicate 1
received full array [1, 1, 2]"#
);
}
}