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