1 module fluentasserts.vibe.json;
2 
3 version(Have_vibe_d_data):
4 
5 import std.exception, std.conv, std.traits;
6 import std.array, std.algorithm, std.typecons;
7 import std.uni, std.string, std.math;
8 
9 import vibe.data.json;
10 import fluentasserts.core.base;
11 import fluentasserts.core.results;
12 
13 import fluentasserts.core.serializers;
14 import fluentasserts.core.operations.equal;
15 import fluentasserts.core.operations.arrayEqual;
16 import fluentasserts.core.operations.contain;
17 import fluentasserts.core.operations.startWith;
18 import fluentasserts.core.operations.endWith;
19 import fluentasserts.core.operations.registry;
20 import fluentasserts.core.operations.lessThan;
21 import fluentasserts.core.operations.greaterThan;
22 import fluentasserts.core.operations.between;
23 import fluentasserts.core.operations.approximately;
24 
25 static this() {
26   SerializerRegistry.instance.register(&jsonToString);
27   Registry.instance.register!(Json, Json[])("equal", &fluentasserts.core.operations.equal.equal);
28   Registry.instance.register!(Json[], Json)("equal", &fluentasserts.core.operations.equal.equal);
29   Registry.instance.register!(Json[], Json[])("equal", &fluentasserts.core.operations.arrayEqual.arrayEqual);
30   Registry.instance.register!(Json[][], Json[][])("equal", &fluentasserts.core.operations.arrayEqual.arrayEqual);
31   Registry.instance.register!(Json, Json[][])("equal", &fluentasserts.core.operations.arrayEqual.arrayEqual);
32 
33   static foreach(Type; BasicNumericTypes) {
34     Registry.instance.register!(Json, Type[])("equal", &fluentasserts.core.operations.equal.equal);
35     Registry.instance.register!(Json, Type)("lessThan", &fluentasserts.core.operations.lessThan.lessThan!Type);
36     Registry.instance.register!(Json, Type)("greaterThan", &fluentasserts.core.operations.greaterThan.greaterThan!Type);
37     Registry.instance.register!(Json, Type)("between", &fluentasserts.core.operations.between.between!Type);
38     Registry.instance.register!(Json, Type)("approximately", &fluentasserts.core.operations.approximately.approximately);
39   }
40 
41   static foreach(Type; StringTypes) {
42     Registry.instance.register(extractTypes!(Json[])[0], "void[]", "equal", &arrayEqual);
43 
44     Registry.instance.register!(Type[], Json[])("equal", &arrayEqual);
45 
46     Registry.instance.register!(Type, Json[])("contain", &contain);
47     Registry.instance.register!(Type, Json)("contain", &contain);
48     Registry.instance.register!(Type[], Json[])("contain", &arrayContain);
49     Registry.instance.register!(Type[], Json[])("containOnly", &arrayContainOnly);
50 
51     Registry.instance.register!(Type, Json)("startWith", &startWith);
52     Registry.instance.register!(Type, Json)("endWith", &endWith);
53   }
54 }
55 
56 string jsonToString(Json value) {
57   return jsonToString(value, 0);
58 }
59 
60 string jsonToString(Json value, size_t level) {
61 
62   if(value.type == Json.Type.array) {
63     string prefix = rightJustifier(``, level * 2, ' ').array;
64     return `[` ~ value.byValue.map!(a => jsonToString(a, level)).join(", ") ~ `]`;
65   }
66 
67   if(value.type == Json.Type.object) {
68     auto keys = value.keys
69       .sort
70       .filter!(key => value[key].type != Json.Type.null_ && value[key].type != Json.Type.undefined);
71 
72     if(keys.empty) {
73       return `{}`;
74     }
75 
76     string prefix = rightJustifier(``, 2 + level * 2, ' ').array;
77     string endPrefix = rightJustifier(``, level * 2, ' ').array;
78 
79     return "{\n" ~ keys.map!(key => prefix ~ `"` ~ key ~ `": ` ~ value[key].jsonToString(level + 1)).join(",\n") ~ "\n" ~ endPrefix ~ "}";
80   }
81 
82   if(value.type == Json.Type.null_) {
83     return "null";
84   }
85 
86   if(value.type == Json.Type.undefined) {
87     return "undefined";
88   }
89 
90   if(value.type == Json.Type..string) {
91     return `"` ~ value.to!string ~ `"`;
92   }
93 
94   if(value.type == Json.Type.float_) {
95     return format("%.10f", value.to!double).strip("0").strip(".");
96   }
97 
98   return value.to!string;
99 }
100 
101 
102 /// it should convert a double to a string
103 unittest {
104   Json(2.3).jsonToString.should.equal("2.3");
105   Json(59.0 / 15.0).jsonToString.should.equal("3.9333333333");
106 }
107 
108 /// Get all the keys from your Json object
109 string[] keys(Json obj, const string file = __FILE__, const size_t line = __LINE__) @trusted {
110   string[] list;
111 
112   if(obj.type != Json.Type.object) {
113     IResult[] results = [ cast(IResult) new MessageResult("Invalid Json type."),
114                           cast(IResult) new ExpectedActualResult("object", obj.type.to!string),
115                           cast(IResult) new SourceResult(file, line) ];
116 
117     throw new TestException(results, file, line);
118   }
119 
120   static if(typeof(obj.byKeyValue).stringof == "Rng") {
121     foreach(string key, Json value; obj.byKeyValue) {
122       list ~= key;
123     }
124 
125     list = list.sort.array;
126 
127     return list;
128   } else {
129     pragma(msg, "Json.keys is not compatible with your vibe.d version");
130     assert(false, "Json.keys is not compatible with your vibe.d version");
131   }
132 }
133 
134 /// Empty Json object keys
135 unittest {
136   Json.emptyObject.keys.length.should.equal(0);
137 }
138 
139 /// Json object keys
140 unittest {
141   auto obj = Json.emptyObject;
142   obj["key1"] = 1;
143   obj["key2"] = 3;
144 
145   obj.keys.should.containOnly(["key1", "key2"]);
146 }
147 
148 /// Json array keys
149 unittest {
150   auto obj = Json.emptyArray;
151 
152   ({
153     obj.keys.should.contain(["key1", "key2"]);
154   }).should.throwAnyException.msg.should.startWith("Invalid Json type.");
155 }
156 
157 /// Get all the keys from your Json object. The levels will be separated by `.` or `[]`
158 string[] nestedKeys(Json obj) @trusted {
159   return obj.flatten.byKeyValue.map!"a.key".array;
160 }
161 
162 /// Empty Json object keys
163 unittest {
164   Json.emptyObject.nestedKeys.length.should.equal(0);
165 }
166 
167 /// Get all keys from nested object
168 unittest {
169   auto obj = Json.emptyObject;
170   obj["key1"] = 1;
171   obj["key2"] = 2;
172   obj["key3"] = Json.emptyObject;
173   obj["key3"]["item1"] = "3";
174   obj["key3"]["item2"] = Json.emptyObject;
175   obj["key3"]["item2"]["item4"] = Json.emptyObject;
176   obj["key3"]["item2"]["item5"] = Json.emptyObject;
177   obj["key3"]["item2"]["item5"]["item6"] = Json.emptyObject;
178 
179   obj.nestedKeys.should.containOnly(["key1", "key2", "key3.item1", "key3.item2.item4", "key3.item2.item5.item6"]);
180 }
181 
182 /// Get all keys from nested objects inside an array
183 unittest {
184   auto obj = Json.emptyObject;
185   Json elm = Json.emptyObject;
186   elm["item5"] = Json.emptyObject;
187   elm["item5"]["item6"] = Json.emptyObject;
188 
189   obj["key2"] = Json.emptyArray;
190   obj["key3"] = Json.emptyArray;
191   obj["key3"] ~= Json("3");
192   obj["key3"] ~= Json.emptyObject;
193   obj["key3"] ~= elm;
194   obj["key3"] ~= [ Json.emptyArray ];
195 
196   obj.nestedKeys.should.containOnly(["key2", "key3[0]", "key3[1]", "key3[2].item5.item6", "key3[3]"]);
197 }
198 
199 /// Takes a nested Json object and moves the values to a Json assoc array where the key
200 /// is the path from the original object to that value
201 Json[string] flatten(Json object) @trusted {
202   Json[string] elements;
203 
204   auto root = tuple("", object);
205   Tuple!(string, Json)[] queue = [ root ];
206 
207   while(queue.length > 0) {
208     auto element = queue[0];
209 
210     if(element[0] != "") {
211       if(element[1].type != Json.Type.object && element[1].type != Json.Type.array) {
212         elements[element[0]] = element[1];
213       }
214 
215       if(element[1].type == Json.Type.object && element[1].length == 0) {
216         elements[element[0]] = element[1];
217       }
218 
219       if(element[1].type == Json.Type.array && element[1].length == 0) {
220         elements[element[0]] = element[1];
221       }
222     }
223 
224     if(element[1].type == Json.Type.object) {
225       foreach(string key, value; element[1].byKeyValue) {
226         string nextKey = key;
227 
228         if(element[0] != "") {
229           nextKey = element[0] ~ "." ~ nextKey;
230         }
231 
232         queue ~= tuple(nextKey, value);
233       }
234     }
235 
236     if(element[1].type == Json.Type.array) {
237       size_t index;
238 
239       foreach(value; element[1].byValue) {
240         string nextKey = element[0] ~ "[" ~ index.to!string ~ "]";
241 
242         queue ~= tuple(nextKey, value);
243         index++;
244       }
245     }
246 
247     queue = queue[1..$];
248   }
249 
250   return elements;
251 }
252 
253 /// Get a flatten object
254 unittest {
255   auto obj = Json.emptyObject;
256   obj["key1"] = 1;
257   obj["key2"] = 2;
258   obj["key3"] = Json.emptyObject;
259   obj["key3"]["item1"] = "3";
260   obj["key3"]["item2"] = Json.emptyObject;
261   obj["key3"]["item2"]["item4"] = Json.emptyObject;
262   obj["key3"]["item2"]["item5"] = Json.emptyObject;
263   obj["key3"]["item2"]["item5"]["item6"] = Json.emptyObject;
264 
265   auto result = obj.flatten;
266   result.byKeyValue.map!(a => a.key).should.containOnly(["key1", "key2", "key3.item1", "key3.item2.item4", "key3.item2.item5.item6"]);
267   result["key1"].should.equal(1);
268   result["key2"].should.equal(2);
269   result["key3.item1"].should.equal("3");
270   result["key3.item2.item4"].should.equal(Json.emptyObject);
271   result["key3.item2.item5.item6"].should.equal(Json.emptyObject);
272 }
273 
274 auto unpackJsonArray(T : U[], U)(Json data) if(!isArray!U && isBasicType!U) {
275   return data.byValue.map!(a => a.to!U).array.dup;
276 }
277 
278 auto unpackJsonArray(T : U[], U)(Json data) if(!isArray!U && is(Unqual!U == Json)) {
279   U[] result;
280 
281   foreach(element; data.byValue) {
282     result ~= element;
283   }
284 
285   return result;
286 }
287 
288 auto unpackJsonArray(T : U[], U)(Json data) if(isArray!(U) && !isSomeString!(U[])) {
289   U[] result;
290 
291   foreach(element; data.byValue) {
292     result ~= unpackJsonArray!(U)(element);
293   }
294 
295   return result;
296 }
297 
298 version(unittest) {
299   import std.string;
300 }
301 
302 /// Two sepparate json objects are equal
303 unittest {
304   Json.emptyObject.should.equal(Json.emptyObject);
305 }
306 
307 /// An json object with an undefined value is iqual to an empty object json with no values
308 unittest {
309   auto value = Json.emptyObject;
310   value["key"] = Json();
311 
312   value.should.equal(Json.emptyObject);
313 }
314 
315 /// It should be able to compare an empty object with an empty array
316 unittest {
317   auto msg = ({
318     Json.emptyObject.should.equal(Json.emptyArray);
319   }).should.throwException!TestException.msg;
320 
321   msg.split("\n")[0].strip.should.equal(`Json.emptyObject should equal []. {} is not equal to [].`);
322   msg.split("\n")[2].strip.should.equal(`[-[]][+{}]`);
323   msg.split("\n")[4].strip.should.equal("Expected:[]");
324   msg.split("\n")[5].strip.should.equal("Actual:{}");
325 
326   ({
327     Json.emptyObject.should.not.equal(Json.emptyArray);
328   }).should.not.throwException!TestException;
329 }
330 
331 /// It should be able to compare two strings
332 unittest {
333   ({
334     Json("test string").should.equal("test string");
335     Json("other string").should.not.equal("test");
336   }).should.not.throwAnyException;
337 
338   ({
339     Json("test string").should.equal(Json("test string"));
340     Json("other string").should.not.equal(Json("test"));
341   }).should.not.throwAnyException;
342 
343   auto msg = ({
344     Json("test string").should.equal("test");
345   }).should.throwException!TestException.msg;
346 
347   msg.split("\n")[0].should.equal(`Json("test string") should equal "test". "test string" is not equal to "test".`);
348 }
349 
350 /// It throw on comparing a Json number with a string
351 unittest {
352   auto msg = ({
353     Json(4).should.equal("some string");
354   }).should.throwException!TestException.msg;
355 
356   msg.split("\n")[0].strip.should.equal(`Json(4) should equal "some string". 4 is not equal to "some string".`);
357   msg.split("\n")[2].strip.should.equal(`[-"some string"][+4]`);
358   msg.split("\n")[4].strip.should.equal(`Expected:"some string"`);
359   msg.split("\n")[5].strip.should.equal(`Actual:4`);
360 }
361 
362 /// It throws when you compare a Json string with integer values
363 unittest {
364   auto msg = ({
365     byte val = 4;
366     Json("some string").should.equal(val);
367   }).should.throwException!TestException.msg;
368 
369   msg.split("\n")[0].strip.should.equal(`Json("some string") should equal 4. "some string" is not equal to 4.`);
370   msg.split("\n")[2].strip.should.equal(`[-4][+"some string"]`);
371   msg.split("\n")[4].strip.should.equal("Expected:4");
372   msg.split("\n")[5].strip.should.equal(`Actual:"some string"`);
373 
374   msg = ({
375     short val = 4;
376     Json("some string").should.equal(val);
377   }).should.throwException!TestException.msg;
378 
379   msg.split("\n")[0].strip.should.equal(`Json("some string") should equal 4. "some string" is not equal to 4.`);
380 
381   msg = ({
382     int val = 4;
383     Json("some string").should.equal(val);
384   }).should.throwException!TestException.msg;
385 
386   msg.split("\n")[0].strip.should.equal(`Json("some string") should equal 4. "some string" is not equal to 4.`);
387 
388   msg = ({
389     long val = 4;
390     Json("some string").should.equal(val);
391   }).should.throwException!TestException.msg;
392 
393   msg.split("\n")[0].strip.should.equal(`Json("some string") should equal 4. "some string" is not equal to 4.`);
394 }
395 
396 /// It throws when you compare a Json string with unsigned integer values
397 unittest {
398   auto msg = ({
399     ubyte val = 4;
400     Json("some string").should.equal(val);
401   }).should.throwException!TestException.msg;
402 
403   msg.split("\n")[0].strip.should.equal(`Json("some string") should equal 4. "some string" is not equal to 4.`);
404 
405   msg = ({
406     ushort val = 4;
407     Json("some string").should.equal(val);
408   }).should.throwException!TestException.msg;
409 
410   msg.split("\n")[0].strip.should.equal(`Json("some string") should equal 4. "some string" is not equal to 4.`);
411 
412   msg = ({
413     uint val = 4;
414     Json("some string").should.equal(val);
415   }).should.throwException!TestException.msg;
416 
417   msg.split("\n")[0].strip.should.equal(`Json("some string") should equal 4. "some string" is not equal to 4.`);
418 
419   msg = ({
420     ulong val = 4;
421     Json("some string").should.equal(val);
422   }).should.throwException!TestException.msg;
423 
424   msg.split("\n")[0].strip.should.equal(`Json("some string") should equal 4. "some string" is not equal to 4.`);
425 }
426 
427 /// It throws when you compare a Json string with floating point values
428 unittest {
429   auto msg = ({
430     float val = 3.14;
431     Json("some string").should.equal(val);
432   }).should.throwException!TestException.msg;
433 
434   msg.split("\n")[0].strip.should.equal(`Json("some string") should equal 3.14. "some string" is not equal to 3.14.`);
435   msg.split("\n")[2].strip.should.equal(`[-3.14][+"some string"]`);
436   msg.split("\n")[4].strip.should.equal("Expected:3.14");
437   msg.split("\n")[5].strip.should.equal(`Actual:"some string"`);
438 
439   msg = ({
440     double val = 3.14;
441     Json("some string").should.equal(val);
442   }).should.throwException!TestException.msg;
443 
444   msg.split("\n")[0].strip.should.equal(`Json("some string") should equal 3.14. "some string" is not equal to 3.14.`);
445 }
446 
447 /// It throws when you compare a Json string with bool values
448 unittest {
449   auto msg = ({
450     Json("some string").should.equal(false);
451   }).should.throwException!TestException.msg;
452 
453   msg.split("\n")[0].strip.should.equal(`Json("some string") should equal false. "some string" is not equal to false.`);
454 }
455 
456 /// It should be able to compare two integers
457 unittest {
458   Json(4L).should.equal(4f);
459   Json(4).should.equal(4);
460   Json(4).should.not.equal(5);
461 
462   Json(4).should.equal(Json(4));
463   Json(4).should.not.equal(Json(5));
464   Json(4L).should.not.equal(Json(5f));
465 
466   auto msg = ({
467     Json(4).should.equal(5);
468   }).should.throwException!TestException.msg;
469 
470   msg.split("\n")[0].should.equal(`Json(4) should equal 5. 4 is not equal to 5.`);
471 
472   msg = ({
473     Json(4).should.equal(Json(5));
474   }).should.throwException!TestException.msg;
475 
476   msg.split("\n")[0].should.equal(`Json(4) should equal 5. 4 is not equal to 5.`);
477 }
478 
479 /// It throws on comparing an integer Json with a string
480 unittest {
481   auto msg = ({
482     Json(4).should.equal("5");
483   }).should.throwException!TestException.msg;
484 
485   msg.split("\n")[0].should.equal(`Json(4) should equal "5". 4 is not equal to "5".`);
486 
487   msg = ({
488     Json(4).should.equal(Json("5"));
489   }).should.throwException!TestException.msg;
490 
491   msg.split("\n")[0].should.equal(`Json(4) should equal "5". 4 is not equal to "5".`);
492 }
493 
494 /// It should be able to compare two floating point numbers
495 unittest {
496   Json(4f).should.equal(4L);
497   Json(4.3).should.equal(4.3);
498   Json(4.3).should.not.equal(5.3);
499 
500   Json(4.3).should.equal(Json(4.3));
501   Json(4.3).should.not.equal(Json(5.3));
502 
503   auto msg = ({
504     Json(4.3).should.equal(5.3);
505   }).should.throwException!TestException.msg;
506 
507   msg.split("\n")[0].should.equal("Json(4.3) should equal 5.3. 4.3 is not equal to 5.3.");
508 
509   msg = ({
510     Json(4.3).should.equal(Json(5.3));
511   }).should.throwException!TestException.msg;
512 
513   msg.split("\n")[0].should.equal("Json(4.3) should equal 5.3. 4.3 is not equal to 5.3.");
514 }
515 
516 /// It throws on comparing an floating point Json with a string
517 unittest {
518   auto msg = ({
519     Json(4f).should.equal("5");
520   }).should.throwException!TestException.msg;
521 
522   msg.split("\n")[0].should.equal(`Json(4f) should equal "5". 4 is not equal to "5".`);
523 
524   msg = ({
525     Json(4f).should.equal(Json("5"));
526   }).should.throwException!TestException.msg;
527 
528   msg.split("\n")[0].should.equal(`Json(4f) should equal "5". 4 is not equal to "5".`);
529 }
530 
531 /// It should be able to compare two booleans
532 unittest {
533   Json(true).should.equal(true);
534   Json(true).should.not.equal(false);
535 
536   Json(true).should.equal(Json(true));
537   Json(true).should.not.equal(Json(false));
538 
539   auto msg = ({
540     Json(true).should.equal(false);
541   }).should.throwException!TestException.msg;
542 
543   msg.split("\n")[0].should.equal("Json(true) should equal false. true is not equal to false.");
544 
545   msg = ({
546     Json(true).should.equal(Json(false));
547   }).should.throwException!TestException.msg;
548 
549   msg.split("\n")[0].should.equal("Json(true) should equal false. true is not equal to false.");
550 }
551 
552 /// It throws on comparing a bool Json with a string
553 unittest {
554   auto msg = ({
555     Json(true).should.equal("5");
556   }).should.throwException!TestException.msg;
557 
558   msg.split("\n")[0].should.equal(`Json(true) should equal "5". true is not equal to "5".`);
559 
560   msg = ({
561     Json(true).should.equal(Json("5"));
562   }).should.throwException!TestException.msg;
563 
564   msg.split("\n")[0].should.equal(`Json(true) should equal "5". true is not equal to "5".`);
565   msg.split("\n")[2].should.equal(`[-"5"][+true]`);
566   msg.split("\n")[4].should.equal(` Expected:"5"`);
567   msg.split("\n")[5].should.equal(`   Actual:true`);
568 }
569 
570 /// It should be able to compare two arrays
571 unittest {
572   Json[] elements = [Json(1), Json(2)];
573   Json[] otherElements = [Json(1), Json(2), Json(3)];
574 
575   Json(elements).should.equal([1, 2]);
576   Json(elements).should.not.equal([1, 2, 3]);
577 
578   Json(elements).should.equal(Json(elements));
579   Json(elements).should.not.equal(Json(otherElements));
580 
581   auto msg = ({
582     Json(elements).should.equal(otherElements);
583   }).should.throwException!TestException.msg;
584 
585   msg.split("\n")[0].should.equal("Json(elements) should equal [1, 2, 3]. [1, 2] is not equal to [1, 2, 3].");
586 
587   msg = ({
588     Json(elements).should.equal(otherElements);
589   }).should.throwException!TestException.msg;
590 
591   msg.split("\n")[0].should.equal("Json(elements) should equal [1, 2, 3]. [1, 2] is not equal to [1, 2, 3].");
592 }
593 
594 /// It throws on comparing a Json array with a string
595 unittest {
596   Json[] elements = [Json(1), Json(2)];
597   auto msg = ({
598     Json(elements).should.equal("5");
599   }).should.throwException!TestException.msg;
600 
601   msg.split("\n")[0].should.equal(`Json(elements) should equal "5". [1, 2] is not equal to "5".`);
602 
603   msg = ({
604     Json(elements).should.equal(Json("5"));
605   }).should.throwException!TestException.msg;
606 
607   msg.split("\n")[0].should.equal(`Json(elements) should equal "5". [1, 2] is not equal to "5".`);
608 }
609 
610 /// It should be able to compare two nested arrays
611 unittest {
612   Json[] element1 = [Json(1), Json(2)];
613   Json[] element2 = [Json(10), Json(20)];
614 
615   Json[] elements = [Json(element1), Json(element2)];
616   Json[] otherElements = [Json(element1), Json(element2), Json(element1)];
617 
618   Json(elements).should.equal([element1, element2]);
619   Json(elements).should.not.equal([element1, element2, element1]);
620 
621   Json(elements).should.equal(Json(elements));
622   Json(elements).should.not.equal(Json(otherElements));
623 
624   auto msg = ({
625     Json(elements).should.equal(otherElements);
626   }).should.throwException!TestException.msg;
627 
628   msg.split("\n")[0].should.equal("Json(elements) should equal [[1, 2], [10, 20], [1, 2]]. [[1, 2], [10, 20]] is not equal to [[1, 2], [10, 20], [1, 2]].");
629 
630   msg = ({
631     Json(elements).should.equal(Json(otherElements));
632   }).should.throwException!TestException.msg;
633 
634   msg.split("\n")[0].should.equal("Json(elements) should equal [[1, 2], [10, 20], [1, 2]]. [[1, 2], [10, 20]] is not equal to [[1, 2], [10, 20], [1, 2]].");
635 }
636 
637 /// It should be able to compare two nested arrays with different levels
638 unittest {
639   Json nestedElement = Json([Json(1), Json(2)]);
640 
641   Json[] elements = [nestedElement, Json(1)];
642   Json[] otherElements = [nestedElement, Json(1), nestedElement];
643 
644   Json(elements).should.equal([nestedElement, Json(1)]);
645   Json(elements).should.not.equal([nestedElement, Json(1), nestedElement]);
646 
647   Json(elements).should.equal(Json(elements));
648   Json(elements).should.not.equal(Json(otherElements));
649 
650   auto msg = ({
651     Json(elements).should.equal(otherElements);
652   }).should.throwException!TestException.msg;
653 
654   msg.split("\n")[0].should.equal("Json(elements) should equal [[1, 2], 1, [1, 2]]. [[1, 2], 1] is not equal to [[1, 2], 1, [1, 2]].");
655 
656   msg = ({
657     Json(elements).should.equal(Json(otherElements));
658   }).should.throwException!TestException.msg;
659 
660   msg.split("\n")[0].should.equal("Json(elements) should equal [[1, 2], 1, [1, 2]]. [[1, 2], 1] is not equal to [[1, 2], 1, [1, 2]].");
661 }
662 
663 /// It should find the key differences inside a Json object
664 unittest {
665   Json expectedObject = Json.emptyObject;
666   Json testObject = Json.emptyObject;
667   testObject["key"] = "some value";
668   testObject["nested"] = Json.emptyObject;
669   testObject["nested"]["item1"] = "hello";
670   testObject["nested"]["item2"] = Json.emptyObject;
671   testObject["nested"]["item2"]["value"] = "world";
672 
673   expectedObject["other"] = "other value";
674 
675   auto msg = ({
676     testObject.should.equal(expectedObject);
677   }).should.throwException!TestException.msg;
678 
679   msg.should.startWith(`testObject should equal {`);
680 }
681 
682 /// It should find the value differences inside a Json object
683 unittest {
684   Json expectedObject = Json.emptyObject;
685   Json testObject = Json.emptyObject;
686   testObject["key1"] = "some value";
687   testObject["key2"] = 1;
688 
689   expectedObject["key1"] = "other value";
690   expectedObject["key2"] = 2;
691 
692   auto msg = ({
693     testObject.should.equal(expectedObject);
694   }).should.throwException!TestException.msg;
695 
696   msg.should.startWith("testObject should equal {");
697 }
698 
699 /// greaterThan support for Json Objects
700 unittest {
701   Json(5).should.be.greaterThan(4);
702   Json(4).should.not.be.greaterThan(5);
703 
704   Json(5f).should.be.greaterThan(4f);
705   Json(4f).should.not.be.greaterThan(5f);
706 
707   auto msg = ({
708     Json("").should.greaterThan(3);
709   }).should.throwException!TestException.msg;
710 
711   msg.split("\n")[0].should.equal(`Json("") should greater than 3.`);
712   msg.split("\n")[1].should.equal("Can't convert the values to int");
713 
714   msg = ({
715     Json(false).should.greaterThan(3f);
716   }).should.throwException!TestException.msg;
717 
718   msg.split("\n")[0].should.equal(`Json(false) should greater than 3.`);
719   msg.split("\n")[1].should.equal("Can't convert the values to float");
720 }
721 
722 /// lessThan support for Json Objects
723 unittest {
724   Json(4).should.be.lessThan(5);
725   Json(5).should.not.be.lessThan(4);
726 
727   Json(4f).should.be.lessThan(5f);
728   Json(5f).should.not.be.lessThan(4f);
729 
730   auto msg = ({
731     Json("").should.lessThan(3);
732   }).should.throwException!TestException.msg;
733 
734   msg.split("\n")[0].should.equal(`Json("") should less than 3.`);
735   msg.split("\n")[1].should.equal(`Can't convert the values to int`);
736 
737   msg = ({
738     Json(false).should.lessThan(3f);
739   }).should.throwException!TestException.msg;
740 
741   msg.split("\n")[0].should.equal(`Json(false) should less than 3.`);
742   msg.split("\n")[1].should.equal(`Can't convert the values to float`);
743 }
744 
745 /// between support for Json Objects
746 unittest {
747   Json(5).should.be.between(6, 4);
748   Json(5).should.not.be.between(5, 6);
749 
750   Json(5f).should.be.between(6f, 4f);
751   Json(5f).should.not.be.between(5f, 6f);
752 
753   auto msg = ({
754     Json(true).should.be.between(6f, 4f);
755   }).should.throwException!TestException.msg;
756 
757   msg.split("\n")[0].should.equal("Json(true) should be between 6 and 4. ");
758   msg.split("\n")[1].should.equal("Can't convert the values to float");
759 }
760 
761 /// should be able to use approximately for jsons
762 unittest {
763   Json(10f/3f).should.be.approximately(3, 0.34);
764   Json(10f/3f).should.not.be.approximately(3, 0.24);
765 
766   auto msg = ({
767     Json("").should.be.approximately(3, 0.34);
768   }).should.throwException!TestException.msg;
769 
770   msg.should.contain(`Can't parse the provided arguments!`);
771 }