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