expect_json/expect/ops/expect_object/
expect_object.rs1use crate::expect::ops::expect_object::ExpectObjectSubOp;
2use crate::expect_core::expect_op;
3use crate::expect_core::Context;
4use crate::expect_core::ExpectOp;
5use crate::expect_core::ExpectOpResult;
6use crate::JsonType;
7use serde_json::Map;
8use serde_json::Value;
9
10#[expect_op(internal, name = "object")]
11#[derive(Debug, Clone, Default, PartialEq)]
12pub struct ExpectObject {
13 sub_ops: Vec<ExpectObjectSubOp>,
14}
15
16impl ExpectObject {
17 pub(crate) fn new() -> Self {
18 Self { sub_ops: vec![] }
19 }
20
21 pub fn empty(mut self) -> Self {
22 self.sub_ops.push(ExpectObjectSubOp::Empty);
23 self
24 }
25
26 pub fn not_empty(mut self) -> Self {
27 self.sub_ops.push(ExpectObjectSubOp::NotEmpty);
28 self
29 }
30
31 pub fn contains<V>(mut self, expected_values: V) -> Self
32 where
33 V: Into<Value>,
34 {
35 let value = Into::<Value>::into(expected_values);
36 let sub_op = match value {
37 Value::Object(values_object) => ExpectObjectSubOp::Contains(values_object),
38 _ => {
39 let value_type = JsonType::from(&value);
40 panic!("object().contains() expected to take object. Received: {value_type}");
41 }
42 };
43
44 self.sub_ops.push(sub_op);
45 self
46 }
47}
48
49impl ExpectOp for ExpectObject {
50 fn on_object(
51 &self,
52 context: &mut Context,
53 received: &Map<String, Value>,
54 ) -> ExpectOpResult<()> {
55 for sub_op in &self.sub_ops {
56 sub_op.on_object(self, context, received)?;
57 }
58
59 Ok(())
60 }
61
62 fn debug_supported_types(&self) -> &'static [JsonType] {
63 &[JsonType::Object]
64 }
65}
66
67#[cfg(test)]
68mod test_contains {
69 use crate::expect;
70 use crate::expect_json_eq;
71 use pretty_assertions::assert_eq;
72 use serde_json::json;
73
74 #[test]
75 fn it_should_be_equal_for_identical_objects() {
76 let left = json!({ "name": "John", "age": 30, "scores": [1, 2, 3] });
77 let right =
78 json!(expect::object()
79 .contains(json!({ "name": "John", "age": 30, "scores": [1, 2, 3] })));
80
81 let output = expect_json_eq(&left, &right);
82 assert!(output.is_ok());
83 }
84
85 #[test]
86 fn it_should_be_equal_for_reversed_identical_objects() {
87 let left = json!({ "name": "John", "age": 30, "scores": [1, 2, 3] });
88 let right =
89 json!(expect::object()
90 .contains(json!({ "scores": [1, 2, 3], "age": 30, "name": "John" })));
91
92 let output = expect_json_eq(&left, &right);
93 assert!(output.is_ok());
94 }
95
96 #[test]
97 fn it_should_be_equal_for_partial_contains() {
98 let left = json!({ "name": "John", "age": 30, "scores": [1, 2, 3] });
99 let right = json!(expect::object().contains(json!({ "name": "John", "age": 30 })));
100
101 let output = expect_json_eq(&left, &right);
102 assert!(output.is_ok());
103 }
104
105 #[test]
106 fn it_should_be_equal_for_nested_contains() {
107 let left = json!({ "name": "John", "comments": [
108 {
109 "text": "Hello",
110 "author": {
111 "name": "Jane",
112 "age": 25
113 }
114 },
115 {
116 "text": "Goodbye",
117 "author": {
118 "name": "John",
119 "age": 30
120 }
121 }
122 ]});
123
124 let right = json!(expect::object().contains(
125 json!({ "comments": expect::array().contains([
126 json!({
127 "text": "Hello",
128 "author": expect::object().contains(
129 json!({
130 "name": "Jane",
131 })
132 )
133 }),
134 ])})
135 ));
136
137 let output = expect_json_eq(&left, &right);
138 assert!(output.is_ok(), "{}", output.unwrap_err().to_string());
139 }
140
141 #[test]
142 fn it_should_error_for_same_fields_but_different_values() {
143 let left = json!({ "name": "John", "age": 30, "scores": [1, 2, 3] });
144 let right = json!(
145 expect::object().contains(json!({ "name": "Joe", "age": 31, "scores": [4, 5, 6] }))
146 );
147
148 let output = expect_json_eq(&left, &right).unwrap_err().to_string();
149 assert_eq!(
150 output,
151 r#"Json integers at root.age are not equal:
152 expected 31
153 received 30"#
154 );
155 }
156
157 #[test]
158 fn it_should_be_ok_for_empty_contains() {
159 let left = json!({ "name": "John", "age": 30, "scores": [1, 2, 3] });
160 let right = json!(expect::object().contains(json!({})));
161
162 let output = expect_json_eq(&left, &right);
163 assert!(output.is_ok());
164 }
165
166 #[test]
167 fn it_should_be_ok_for_empty_on_empty_object() {
168 let left = json!({});
169 let right = json!(expect::object().contains(json!({})));
170
171 let output = expect_json_eq(&left, &right);
172 assert!(output.is_ok());
173 }
174
175 #[test]
176 fn it_should_error_if_used_against_the_wrong_type() {
177 let left = json!("🦊");
178 let right = json!(expect::object().contains(json!({})));
179
180 let output = expect_json_eq(&left, &right).unwrap_err().to_string();
181 assert_eq!(
182 output,
183 r#"Json expect::object() at root, received wrong type:
184 expected object
185 received string "🦊""#
186 );
187 }
188
189 #[test]
190 fn it_should_error_for_nested_contains_via_array_on_differences() {
191 let left = json!({ "name": "John", "comments": [
192 {
193 "text": "Hello",
194 "author": {
195 "name": "🦊",
196 "age": 25
197 }
198 },
199 {
200 "text": "Goodbye",
201 "author": {
202 "name": "John",
203 "age": 30
204 }
205 }
206 ]});
207
208 let right = json!(expect::object().contains(
209 json!({ "comments": expect::array().contains([
210 json!({
211 "text": "Hello",
212 "author": expect::object().contains(
213 json!({
214 "name": "Jane",
215 })
216 )
217 }),
218 ])})
219 ));
220
221 let output = expect_json_eq(&left, &right).unwrap_err().to_string();
222 assert_eq!(
223 output,
224 r#"Json array at root.comments does not contain expected value:
225 expected array to contain {
226 "author": expect::object(),
227 "text": "Hello"
228 }, but it was not found.
229 received [
230 {
231 "author": {
232 "age": 25,
233 "name": "🦊"
234 },
235 "text": "Hello"
236 },
237 {
238 "author": {
239 "age": 30,
240 "name": "John"
241 },
242 "text": "Goodbye"
243 }
244 ]"#
245 );
246 }
247
248 #[test]
249 fn it_should_error_for_nested_contains_via_object_with_inner_contains_error() {
250 let left = json!({
251 "name": "John",
252 "comment": {
253 "text": "Hello",
254 "author": {
255 "name": "🦊",
256 "age": 25
257 }
258 },
259 });
260
261 let right = json!(expect::object().contains(json!({ "comment":
262 expect::object().contains(
263 json!({
264 "text": "Hello",
265 "author": expect::object().contains(
266 json!({
267 "name": "Jane",
268 })
269 )
270 })
271 )
272 })));
273
274 let output = expect_json_eq(&left, &right).unwrap_err().to_string();
275 assert_eq!(
276 output,
277 r#"Json strings at root.comment.author.name are not equal:
278 expected "Jane"
279 received "🦊""#
280 );
281 }
282
283 #[test]
286 fn it_should_error_for_nested_contains_via_different_object_with_inner_contains_error() {
287 let left = json!({
288 "name": "John",
289 "comment": {
290 "text": "Hello",
291 "author": {
292 "name": "Jane",
293 "age": 25
294 }
295 },
296 });
297
298 let right = json!(expect::object().contains(json!({ "comment":
299 expect::object().contains(
300 json!({
301 "text": "Hello",
302 "author": expect::object().contains(
303 json!({
304 "something_else": "🦊",
305 })
306 )
307 })
308 )
309 })));
310
311 let output = expect_json_eq(&left, &right).unwrap_err().to_string();
312 assert_eq!(
313 output,
314 r#"Json object at root.comment.author is missing key for object:
315 expected field 'something_else',
316 but it was not found"#
317 );
318 }
319}
320
321#[cfg(test)]
322mod test_empty {
323 use crate::expect;
324 use crate::expect_json_eq;
325 use pretty_assertions::assert_eq;
326 use serde_json::json;
327
328 #[test]
329 fn it_should_pass_when_object_is_empty() {
330 let left = json!({});
331 let right = json!(expect::object().empty());
332
333 let output = expect_json_eq(&left, &right);
334 assert!(output.is_ok(), "assertion error: {output:#?}");
335 }
336
337 #[test]
338 fn it_should_fail_when_object_is_not_empty() {
339 let left = json!({ "foo": "bar" });
340 let right = json!(expect::object().empty());
341
342 let output = expect_json_eq(&left, &right).unwrap_err().to_string();
343 assert_eq!(
344 output,
345 r#"Json expect::object() error at root:
346 expected empty object
347 received {
348 "foo": "bar"
349 }"#
350 );
351 }
352}
353
354#[cfg(test)]
355mod test_not_empty {
356 use crate::expect;
357 use crate::expect_json_eq;
358 use pretty_assertions::assert_eq;
359 use serde_json::json;
360
361 #[test]
362 fn it_should_pass_when_object_is_not_empty() {
363 let left = json!({ "foo": "bar" });
364 let right = json!(expect::object().not_empty());
365
366 let output = expect_json_eq(&left, &right);
367 assert!(output.is_ok(), "assertion error: {output:#?}");
368 }
369
370 #[test]
371 fn it_should_fail_when_object_is_empty() {
372 let left = json!({});
373 let right = json!(expect::object().not_empty());
374
375 let output = expect_json_eq(&left, &right).unwrap_err().to_string();
376 assert_eq!(
377 output,
378 r#"Json expect::object() error at root:
379 expected non-empty object
380 received {}"#
381 );
382 }
383}