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; 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.sort; 69 70 if(keys.length == 0) { 71 return `{}`; 72 } 73 74 string prefix = rightJustifier(``, 2 + level * 2, ' ').array; 75 string endPrefix = rightJustifier(``, level * 2, ' ').array; 76 77 return "{\n" ~ keys.map!(key => prefix ~ `"` ~ key ~ `": ` ~ value[key].jsonToString(level + 1)).join(",\n") ~ "\n" ~ endPrefix ~ "}"; 78 } 79 80 if(value.type == Json.Type.null_) { 81 return "null"; 82 } 83 84 if(value.type == Json.Type.undefined) { 85 return "undefined"; 86 } 87 88 if(value.type == Json.Type..string) { 89 return `"` ~ value.to!string ~ `"`; 90 } 91 92 return value.to!string; 93 } 94 95 /// Get all the keys from your Json object 96 string[] keys(Json obj, const string file = __FILE__, const size_t line = __LINE__) @trusted { 97 string[] list; 98 99 if(obj.type != Json.Type.object) { 100 IResult[] results = [ cast(IResult) new MessageResult("Invalid Json type."), 101 cast(IResult) new ExpectedActualResult("object", obj.type.to!string), 102 cast(IResult) new SourceResult(file, line) ]; 103 104 throw new TestException(results, file, line); 105 } 106 107 static if(typeof(obj.byKeyValue).stringof == "Rng") { 108 foreach(string key, Json value; obj.byKeyValue) { 109 list ~= key; 110 } 111 112 list = list.sort.array; 113 114 return list; 115 } else { 116 pragma(msg, "Json.keys is not compatible with your vibe.d version"); 117 assert(false, "Json.keys is not compatible with your vibe.d version"); 118 } 119 } 120 121 /// Empty Json object keys 122 unittest { 123 Json.emptyObject.keys.length.should.equal(0); 124 } 125 126 /// Json object keys 127 unittest { 128 auto obj = Json.emptyObject; 129 obj["key1"] = 1; 130 obj["key2"] = 3; 131 132 obj.keys.should.containOnly(["key1", "key2"]); 133 } 134 135 /// Json array keys 136 unittest { 137 auto obj = Json.emptyArray; 138 139 ({ 140 obj.keys.should.contain(["key1", "key2"]); 141 }).should.throwAnyException.msg.should.startWith("Invalid Json type."); 142 } 143 144 /// Get all the keys from your Json object. The levels will be separated by `.` or `[]` 145 string[] nestedKeys(Json obj) @trusted { 146 return obj.flatten.byKeyValue.map!"a.key".array; 147 } 148 149 /// Empty Json object keys 150 unittest { 151 Json.emptyObject.nestedKeys.length.should.equal(0); 152 } 153 154 /// Get all keys from nested object 155 unittest { 156 auto obj = Json.emptyObject; 157 obj["key1"] = 1; 158 obj["key2"] = 2; 159 obj["key3"] = Json.emptyObject; 160 obj["key3"]["item1"] = "3"; 161 obj["key3"]["item2"] = Json.emptyObject; 162 obj["key3"]["item2"]["item4"] = Json.emptyObject; 163 obj["key3"]["item2"]["item5"] = Json.emptyObject; 164 obj["key3"]["item2"]["item5"]["item6"] = Json.emptyObject; 165 166 obj.nestedKeys.should.containOnly(["key1", "key2", "key3.item1", "key3.item2.item4", "key3.item2.item5.item6"]); 167 } 168 169 170 /// Get all keys from nested objects inside an array 171 unittest { 172 auto obj = Json.emptyObject; 173 Json elm = Json.emptyObject; 174 elm["item5"] = Json.emptyObject; 175 elm["item5"]["item6"] = Json.emptyObject; 176 177 obj["key2"] = Json.emptyArray; 178 obj["key3"] = Json.emptyArray; 179 obj["key3"] ~= Json("3"); 180 obj["key3"] ~= Json.emptyObject; 181 obj["key3"] ~= elm; 182 obj["key3"] ~= [ Json.emptyArray ]; 183 184 obj.nestedKeys.should.containOnly(["key2", "key3[0]", "key3[1]", "key3[2].item5.item6", "key3[3]"]); 185 } 186 187 /// Takes a nested Json object and moves the values to a Json assoc array where the key 188 /// is the path from the original object to that value 189 Json[string] flatten(Json object) @trusted { 190 Json[string] elements; 191 192 auto root = tuple("", object); 193 Tuple!(string, Json)[] queue = [ root ]; 194 195 while(queue.length > 0) { 196 auto element = queue[0]; 197 198 if(element[0] != "") { 199 if(element[1].type != Json.Type.object && element[1].type != Json.Type.array) { 200 elements[element[0]] = element[1]; 201 } 202 203 if(element[1].type == Json.Type.object && element[1].length == 0) { 204 elements[element[0]] = element[1]; 205 } 206 207 if(element[1].type == Json.Type.array && element[1].length == 0) { 208 elements[element[0]] = element[1]; 209 } 210 } 211 212 if(element[1].type == Json.Type.object) { 213 foreach(string key, value; element[1].byKeyValue) { 214 string nextKey = key; 215 216 if(element[0] != "") { 217 nextKey = element[0] ~ "." ~ nextKey; 218 } 219 220 queue ~= tuple(nextKey, value); 221 } 222 } 223 224 if(element[1].type == Json.Type.array) { 225 size_t index; 226 227 foreach(value; element[1].byValue) { 228 string nextKey = element[0] ~ "[" ~ index.to!string ~ "]"; 229 230 queue ~= tuple(nextKey, value); 231 index++; 232 } 233 } 234 235 queue = queue[1..$]; 236 } 237 238 return elements; 239 } 240 241 /// Get a flatten object 242 unittest { 243 auto obj = Json.emptyObject; 244 obj["key1"] = 1; 245 obj["key2"] = 2; 246 obj["key3"] = Json.emptyObject; 247 obj["key3"]["item1"] = "3"; 248 obj["key3"]["item2"] = Json.emptyObject; 249 obj["key3"]["item2"]["item4"] = Json.emptyObject; 250 obj["key3"]["item2"]["item5"] = Json.emptyObject; 251 obj["key3"]["item2"]["item5"]["item6"] = Json.emptyObject; 252 253 auto result = obj.flatten; 254 result.byKeyValue.map!(a => a.key).should.containOnly(["key1", "key2", "key3.item1", "key3.item2.item4", "key3.item2.item5.item6"]); 255 result["key1"].should.equal(1); 256 result["key2"].should.equal(2); 257 result["key3.item1"].should.equal("3"); 258 result["key3.item2.item4"].should.equal(Json.emptyObject); 259 result["key3.item2.item5.item6"].should.equal(Json.emptyObject); 260 } 261 262 auto unpackJsonArray(T : U[], U)(Json data) if(!isArray!U && isBasicType!U) { 263 return data.byValue.map!(a => a.to!U).array.dup; 264 } 265 266 auto unpackJsonArray(T : U[], U)(Json data) if(!isArray!U && is(Unqual!U == Json)) { 267 U[] result; 268 269 foreach(element; data.byValue) { 270 result ~= element; 271 } 272 273 return result; 274 } 275 276 auto unpackJsonArray(T : U[], U)(Json data) if(isArray!(U) && !isSomeString!(U[])) { 277 U[] result; 278 279 foreach(element; data.byValue) { 280 result ~= unpackJsonArray!(U)(element); 281 } 282 283 return result; 284 } 285 286 /* 287 struct ShouldJson(T) { 288 private const T testData; 289 290 this(U)(U value) { 291 valueEvaluation = value.evaluation; 292 testData = value.value; 293 } 294 295 mixin ShouldCommons; 296 mixin ShouldThrowableCommons; 297 298 auto validateJsonType(const T someValue, const string file, const size_t line) { 299 auto haveSameType = someValue.type == testData.type; 300 301 string expected = "a Json.Type." ~ testData.type.to!string; 302 string actual = "a Json.Type." ~ someValue.type.to!string; 303 304 Message[] msg; 305 306 if(!haveSameType) { 307 msg = [ 308 Message(false, "They have incompatible types "), 309 Message(false, "`Json.Type."), 310 Message(true, testData.type.to!string), 311 Message(false, "` != `Json.Type."), 312 Message(true, someValue.type.to!string), 313 Message(false, "`.") 314 ]; 315 316 return result(haveSameType, msg, [ new ExpectedActualResult(actual, expected) ], file, line); 317 } 318 319 return result(haveSameType, msg, [ ], file, line); 320 } 321 322 auto validateJsonType(U)(const string file, const size_t line) { 323 Json.Type someType = Json.Type.undefined; 324 string expected; 325 string actual = "a Json.Type." ~ testData.type.to!string; 326 bool haveSameType; 327 328 static if(is(U == string)) { 329 someType = Json.Type.string; 330 expected = "a string or Json.Type." ~ someType.to!string; 331 haveSameType = someType == testData.type; 332 } 333 334 static if(is(U == byte) || is(U == short) || is(U == int) || is(U == long) || 335 is(U == ubyte) || is(U == ushort) || is(U == uint) || is(U == ulong)) { 336 someType = Json.Type.int_; 337 haveSameType = Json.Type.int_ == testData.type || Json.Type.float_ == testData.type; 338 339 expected = "a " ~ U.stringof ~ " or Json.Type." ~ someType.to!string; 340 } 341 342 static if(is(U == float) || is(U == double)) { 343 someType = Json.Type.float_; 344 haveSameType = Json.Type.int_ == testData.type || Json.Type.float_ == testData.type; 345 346 expected = "a " ~ U.stringof ~ " or Json.Type." ~ someType.to!string; 347 } 348 349 static if(is(U == bool)) { 350 someType = Json.Type.bool_; 351 haveSameType = someType == testData.type; 352 353 expected = "a " ~ U.stringof ~ " or Json.Type." ~ someType.to!string; 354 } 355 356 static if(isArray!U && !isSomeString!U) { 357 someType = Json.Type.array; 358 haveSameType = someType == testData.type; 359 360 expected = "a " ~ U.stringof ~ "[] or Json.Type." ~ someType.to!string; 361 } 362 363 if(expected == "") { 364 expected = "a Json.Type." ~ someType.to!string; 365 } 366 367 Message[] msg = [ 368 Message(false, "They have incompatible types "), 369 Message(false, "`Json.Type."), 370 Message(true, testData.type.to!string), 371 Message(false, "` != `"), 372 Message(true, U.stringof), 373 Message(false, "`.") 374 ]; 375 376 return result(haveSameType, msg, [ new ExpectedActualResult(expected, actual) ], file, line); 377 } 378 379 /// Check if it equals with a value 380 auto equal(U)(const U someValue, const string file = __FILE__, const size_t line = __LINE__) if(!isArray!U || isSomeString!U) { 381 addMessage(" equal `"); 382 addValue(someValue.to!string); 383 addMessage("`"); 384 385 static if(is(U == string) || std.traits.isNumeric!U || is(U == bool)) { 386 U nativeVal; 387 388 try { 389 nativeVal = testData.to!U; 390 } catch(ConvException e) { 391 addMessage(". "); 392 if(e.msg.length > 0 && e.msg[0].toUpper == e.msg[0]) { 393 addValue(e.msg); 394 } else { 395 addMessage("During conversion "); 396 auto msg = e.msg; 397 398 if(msg.endsWith(".")) { 399 msg = msg[0..$-1]; 400 } 401 402 addValue(msg); 403 } 404 } 405 validateException; 406 407 if(expectedValue) { 408 auto typeResult = validateJsonType!U(file, line); 409 410 if(typeResult.willThrow) { 411 return typeResult; 412 } 413 414 return expect(nativeVal, file, line).to.equal(someValue); 415 } else { 416 return expect(nativeVal, file, line).to.not.equal(someValue); 417 } 418 } else static if(is(U == Json)) { 419 return equalJson(someValue, file, line); 420 } else { 421 static assert(false, "You can not validate `Json` against `" ~ U.stringof ~ "`"); 422 } 423 } 424 425 /// Check if it equals to an array 426 auto equal(U)(const U[] someArray, const string file = __FILE__, const size_t line = __LINE__) { 427 addMessage(" equal `"); 428 addValue(someArray.to!string); 429 addMessage("`"); 430 431 validateException; 432 433 Unqual!U[] nativeVal; 434 435 try { 436 nativeVal = unpackJsonArray!(U[])(testData); 437 } catch(Exception e) { 438 addMessage(". "); 439 if(e.msg.length > 0 && e.msg[0].toUpper == e.msg[0]) { 440 addValue(e.msg); 441 } else { 442 addMessage("During conversion "); 443 addValue(e.msg); 444 } 445 } 446 447 static if(is(U == string) || std.traits.isNumeric!U || is(U == bool)) { 448 449 if(expectedValue) { 450 auto typeResult = validateJsonType!(U[])(file, line); 451 452 if(typeResult.willThrow) { 453 return typeResult; 454 } 455 456 return nativeVal.should.equal(someArray, file, line); 457 } else { 458 return nativeVal.should.not.equal(someArray, file, line); 459 } 460 } else static if(isArray!U) { 461 if(expectedValue) { 462 auto typeResult = validateJsonType!(U[])(file, line); 463 464 if(typeResult.willThrow) { 465 return typeResult; 466 } 467 468 return nativeVal.should.equal(someArray, file, line); 469 } else { 470 return nativeVal.should.not.equal(someArray, file, line); 471 } 472 } else static if(is(U == Json)) { 473 474 if(expectedValue) { 475 auto typeResult = validateJsonType!(U[])(file, line); 476 477 if(typeResult.willThrow) { 478 return typeResult; 479 } 480 481 return nativeVal.should.equal(someArray, file, line); 482 } else { 483 return nativeVal.should.not.equal(someArray, file, line); 484 } 485 } else { 486 static assert(false, "You can not validate `Json` against `" ~ U.stringof ~ "`"); 487 } 488 } 489 490 auto greaterThan(U)(const U someValue, const string file = __FILE__, const size_t line = __LINE__) if(isNumeric!U) { 491 auto enforceResult = enforceNumericJson("greaterThan", file, line); 492 493 if(enforceResult.willThrow) { 494 return enforceResult; 495 } 496 497 if(expectedValue) { 498 return testData.to!U.should.be.greaterThan(someValue, file, line); 499 } else { 500 return testData.to!U.should.not.be.greaterThan(someValue, file, line); 501 } 502 } 503 504 auto lessThan(U)(const U someValue, const string file = __FILE__, const size_t line = __LINE__) if(isNumeric!U) { 505 auto enforceResult = enforceNumericJson("lessThan", file, line); 506 507 if(enforceResult.willThrow) { 508 return enforceResult; 509 } 510 511 if(expectedValue) { 512 return testData.to!U.should.be.lessThan(someValue, file, line); 513 } else { 514 return testData.to!U.should.not.be.lessThan(someValue, file, line); 515 } 516 } 517 518 auto between(U)(const U limit1, const U limit2, const string file = __FILE__, const size_t line = __LINE__) if(isNumeric!U) { 519 auto enforceResult = enforceNumericJson("between", file, line); 520 521 if(enforceResult.willThrow) { 522 return enforceResult; 523 } 524 525 if(expectedValue) { 526 return testData.to!U.should.be.between(limit1, limit2, file, line); 527 } else { 528 return testData.to!U.should.not.be.between(limit1, limit2, file, line); 529 } 530 } 531 532 auto approximately(U)(const U someValue, const U delta, const string file = __FILE__, const size_t line = __LINE__) if(isNumeric!U) { 533 auto enforceResult = enforceNumericJson("approximately", file, line); 534 535 if(enforceResult.willThrow) { 536 return enforceResult; 537 } 538 539 if(expectedValue) { 540 return testData.to!U.should.be.approximately(someValue, delta, file, line); 541 } else { 542 return testData.to!U.should.not.be.approximately(someValue, delta, file, line); 543 } 544 } 545 546 private { 547 auto enforceNumericJson(string functionName, const string file, const size_t line) { 548 if(!expectedValue) { 549 return Result.success; 550 } 551 552 auto typeResult = expect( 553 testData.type == Json.Type.int_ || 554 testData.type == Json.Type.float_, 555 file, line 556 ).to.equal(true); 557 558 typeResult.message = new MessageResult(" must contain a number to use " ~ functionName ~ ". A `"); 559 typeResult.message.addValue("Json.Type." ~ testData.type.to!string); 560 typeResult.message.addText("` was found instead."); 561 562 typeResult.results = [ new ExpectedActualResult("Json.Type.int_ or Json.Type.float_", "Json.Type." ~ testData.type.to!string) ]; 563 564 return typeResult; 565 } 566 567 auto equalJson(const Json someValue, const string file, const size_t line) { 568 if(expectedValue) { 569 auto typeResult = validateJsonType(someValue, file, line); 570 571 if(typeResult.willThrow) { 572 return typeResult; 573 } 574 } 575 576 beginCheck; 577 if(testData.type == Json.Type.string) { 578 return equal(someValue.to!string, file, line); 579 } 580 581 if(testData.type == Json.Type.int_) { 582 return equal(someValue.to!long, file, line); 583 } 584 585 if(testData.type == Json.Type.float_) { 586 return equal(someValue.to!double, file, line); 587 } 588 589 if(testData.type == Json.Type.bool_) { 590 return equal(someValue.to!bool, file, line); 591 } 592 593 if(testData.type == Json.Type.array) { 594 Json[] values; 595 596 foreach(value; someValue.byValue) { 597 values ~= value; 598 } 599 600 return equal(values, file, line); 601 } 602 603 auto isSame = testData == someValue; 604 auto expected = expectedValue ? someValue.toPrettyString : "something different than the Actual data"; 605 606 IResult[] results; 607 auto message = new MessageResult(""); 608 message.addText("\n"); 609 message.addValue("Expected:"); 610 message.addText("\n" ~ expected ~ "\n\n"); 611 message.addValue("Actual:"); 612 message.addText("\n" ~ testData.toPrettyString); 613 614 results ~= message; 615 616 if(expectedValue) { 617 auto flattenTestData = testData.flatten; 618 auto flattenSomeValue = someValue.flatten; 619 620 foreach(string key, value; flattenTestData) { 621 if(key in flattenSomeValue && flattenSomeValue[key] != value) { 622 results ~= new ExpectedActualResult(key, 623 flattenSomeValue[key].to!string ~ " (Json.Type." ~ flattenSomeValue[key].type.to!string ~ ")", 624 value.to!string ~ " (Json.Type." ~ value.type.to!string ~ ")"); 625 } 626 } 627 628 auto infoResult = new ListInfoResult(); 629 auto comparison = ListComparison!string(someValue.nestedKeys, testData.nestedKeys); 630 631 infoResult.add("Extra key", "Extra keys", comparison.extra); 632 infoResult.add("Missing key", "Missing keys", comparison.missing); 633 634 results ~= infoResult; 635 } 636 637 return result(isSame, [], results, file, line); 638 } 639 } 640 }*/ 641 642 version(unittest) { 643 import std.string; 644 } 645 646 /// It should be able to compare 2 empty json objects 647 unittest { 648 Json.emptyObject.should.equal(Json.emptyObject); 649 } 650 651 /// It should be able to compare an empty object with an empty array 652 unittest { 653 auto msg = ({ 654 Json.emptyObject.should.equal(Json.emptyArray); 655 }).should.throwException!TestException.msg; 656 657 msg.split("\n")[0].strip.should.equal(`Json.emptyObject should equal []. {} is not equal to [].`); 658 msg.split("\n")[2].strip.should.equal(`[-[]][+{}]`); 659 msg.split("\n")[4].strip.should.equal("Expected:[]"); 660 msg.split("\n")[5].strip.should.equal("Actual:{}"); 661 662 ({ 663 Json.emptyObject.should.not.equal(Json.emptyArray); 664 }).should.not.throwException!TestException; 665 } 666 667 /// It should be able to compare two strings 668 unittest { 669 ({ 670 Json("test string").should.equal("test string"); 671 Json("other string").should.not.equal("test"); 672 }).should.not.throwAnyException; 673 674 ({ 675 Json("test string").should.equal(Json("test string")); 676 Json("other string").should.not.equal(Json("test")); 677 }).should.not.throwAnyException; 678 679 auto msg = ({ 680 Json("test string").should.equal("test"); 681 }).should.throwException!TestException.msg; 682 683 msg.split("\n")[0].should.equal(`Json("test string") should equal "test". "test string" is not equal to "test".`); 684 } 685 686 /// It throw on comparing a Json number with a string 687 unittest { 688 auto msg = ({ 689 Json(4).should.equal("some string"); 690 }).should.throwException!TestException.msg; 691 692 msg.split("\n")[0].strip.should.equal(`Json(4) should equal "some string". 4 is not equal to "some string".`); 693 msg.split("\n")[2].strip.should.equal(`[-"some string"][+4]`); 694 msg.split("\n")[4].strip.should.equal(`Expected:"some string"`); 695 msg.split("\n")[5].strip.should.equal(`Actual:4`); 696 } 697 698 /// It throws when you compare a Json string with integer values 699 unittest { 700 auto msg = ({ 701 byte val = 4; 702 Json("some string").should.equal(val); 703 }).should.throwException!TestException.msg; 704 705 msg.split("\n")[0].strip.should.equal(`Json("some string") should equal 4. "some string" is not equal to 4.`); 706 msg.split("\n")[2].strip.should.equal(`[-4][+"some string"]`); 707 msg.split("\n")[4].strip.should.equal("Expected:4"); 708 msg.split("\n")[5].strip.should.equal(`Actual:"some string"`); 709 710 msg = ({ 711 short val = 4; 712 Json("some string").should.equal(val); 713 }).should.throwException!TestException.msg; 714 715 msg.split("\n")[0].strip.should.equal(`Json("some string") should equal 4. "some string" is not equal to 4.`); 716 717 msg = ({ 718 int val = 4; 719 Json("some string").should.equal(val); 720 }).should.throwException!TestException.msg; 721 722 msg.split("\n")[0].strip.should.equal(`Json("some string") should equal 4. "some string" is not equal to 4.`); 723 724 msg = ({ 725 long val = 4; 726 Json("some string").should.equal(val); 727 }).should.throwException!TestException.msg; 728 729 msg.split("\n")[0].strip.should.equal(`Json("some string") should equal 4. "some string" is not equal to 4.`); 730 } 731 732 /// It throws when you compare a Json string with unsigned integer values 733 unittest { 734 auto msg = ({ 735 ubyte val = 4; 736 Json("some string").should.equal(val); 737 }).should.throwException!TestException.msg; 738 739 msg.split("\n")[0].strip.should.equal(`Json("some string") should equal 4. "some string" is not equal to 4.`); 740 741 msg = ({ 742 ushort val = 4; 743 Json("some string").should.equal(val); 744 }).should.throwException!TestException.msg; 745 746 msg.split("\n")[0].strip.should.equal(`Json("some string") should equal 4. "some string" is not equal to 4.`); 747 748 msg = ({ 749 uint val = 4; 750 Json("some string").should.equal(val); 751 }).should.throwException!TestException.msg; 752 753 msg.split("\n")[0].strip.should.equal(`Json("some string") should equal 4. "some string" is not equal to 4.`); 754 755 msg = ({ 756 ulong val = 4; 757 Json("some string").should.equal(val); 758 }).should.throwException!TestException.msg; 759 760 msg.split("\n")[0].strip.should.equal(`Json("some string") should equal 4. "some string" is not equal to 4.`); 761 } 762 763 /// It throws when you compare a Json string with floating point values 764 unittest { 765 auto msg = ({ 766 float val = 3.14; 767 Json("some string").should.equal(val); 768 }).should.throwException!TestException.msg; 769 770 msg.split("\n")[0].strip.should.equal(`Json("some string") should equal 3.14. "some string" is not equal to 3.14.`); 771 msg.split("\n")[2].strip.should.equal(`[-3.14][+"some string"]`); 772 msg.split("\n")[4].strip.should.equal("Expected:3.14"); 773 msg.split("\n")[5].strip.should.equal(`Actual:"some string"`); 774 775 msg = ({ 776 double val = 3.14; 777 Json("some string").should.equal(val); 778 }).should.throwException!TestException.msg; 779 780 msg.split("\n")[0].strip.should.equal(`Json("some string") should equal 3.14. "some string" is not equal to 3.14.`); 781 } 782 783 /// It throws when you compare a Json string with bool values 784 unittest { 785 auto msg = ({ 786 Json("some string").should.equal(false); 787 }).should.throwException!TestException.msg; 788 789 msg.split("\n")[0].strip.should.equal(`Json("some string") should equal false. "some string" is not equal to false.`); 790 } 791 792 /// It should be able to compare two integers 793 unittest { 794 Json(4L).should.equal(4f); 795 Json(4).should.equal(4); 796 Json(4).should.not.equal(5); 797 798 Json(4).should.equal(Json(4)); 799 Json(4).should.not.equal(Json(5)); 800 Json(4L).should.not.equal(Json(5f)); 801 802 auto msg = ({ 803 Json(4).should.equal(5); 804 }).should.throwException!TestException.msg; 805 806 msg.split("\n")[0].should.equal(`Json(4) should equal 5. 4 is not equal to 5.`); 807 808 msg = ({ 809 Json(4).should.equal(Json(5)); 810 }).should.throwException!TestException.msg; 811 812 msg.split("\n")[0].should.equal(`Json(4) should equal 5. 4 is not equal to 5.`); 813 } 814 815 /// It throws on comparing an integer Json with a string 816 unittest { 817 auto msg = ({ 818 Json(4).should.equal("5"); 819 }).should.throwException!TestException.msg; 820 821 msg.split("\n")[0].should.equal(`Json(4) should equal "5". 4 is not equal to "5".`); 822 823 msg = ({ 824 Json(4).should.equal(Json("5")); 825 }).should.throwException!TestException.msg; 826 827 msg.split("\n")[0].should.equal(`Json(4) should equal "5". 4 is not equal to "5".`); 828 } 829 830 /// It should be able to compare two floating point numbers 831 unittest { 832 Json(4f).should.equal(4L); 833 Json(4.3).should.equal(4.3); 834 Json(4.3).should.not.equal(5.3); 835 836 Json(4.3).should.equal(Json(4.3)); 837 Json(4.3).should.not.equal(Json(5.3)); 838 839 auto msg = ({ 840 Json(4.3).should.equal(5.3); 841 }).should.throwException!TestException.msg; 842 843 msg.split("\n")[0].should.equal("Json(4.3) should equal 5.3. 4.3 is not equal to 5.3."); 844 845 msg = ({ 846 Json(4.3).should.equal(Json(5.3)); 847 }).should.throwException!TestException.msg; 848 849 msg.split("\n")[0].should.equal("Json(4.3) should equal 5.3. 4.3 is not equal to 5.3."); 850 } 851 852 /// It throws on comparing an floating point Json with a string 853 unittest { 854 auto msg = ({ 855 Json(4f).should.equal("5"); 856 }).should.throwException!TestException.msg; 857 858 msg.split("\n")[0].should.equal(`Json(4f) should equal "5". 4 is not equal to "5".`); 859 860 msg = ({ 861 Json(4f).should.equal(Json("5")); 862 }).should.throwException!TestException.msg; 863 864 msg.split("\n")[0].should.equal(`Json(4f) should equal "5". 4 is not equal to "5".`); 865 } 866 867 /// It should be able to compare two booleans 868 unittest { 869 Json(true).should.equal(true); 870 Json(true).should.not.equal(false); 871 872 Json(true).should.equal(Json(true)); 873 Json(true).should.not.equal(Json(false)); 874 875 auto msg = ({ 876 Json(true).should.equal(false); 877 }).should.throwException!TestException.msg; 878 879 msg.split("\n")[0].should.equal("Json(true) should equal false. true is not equal to false."); 880 881 msg = ({ 882 Json(true).should.equal(Json(false)); 883 }).should.throwException!TestException.msg; 884 885 msg.split("\n")[0].should.equal("Json(true) should equal false. true is not equal to false."); 886 } 887 888 /// It throws on comparing a bool Json with a string 889 unittest { 890 auto msg = ({ 891 Json(true).should.equal("5"); 892 }).should.throwException!TestException.msg; 893 894 msg.split("\n")[0].should.equal(`Json(true) should equal "5". true is not equal to "5".`); 895 896 msg = ({ 897 Json(true).should.equal(Json("5")); 898 }).should.throwException!TestException.msg; 899 900 msg.split("\n")[0].should.equal(`Json(true) should equal "5". true is not equal to "5".`); 901 msg.split("\n")[2].should.equal(`[-"5"][+true]`); 902 msg.split("\n")[4].should.equal(` Expected:"5"`); 903 msg.split("\n")[5].should.equal(` Actual:true`); 904 } 905 906 /// It should be able to compare two arrays 907 unittest { 908 Json[] elements = [Json(1), Json(2)]; 909 Json[] otherElements = [Json(1), Json(2), Json(3)]; 910 911 Json(elements).should.equal([1, 2]); 912 Json(elements).should.not.equal([1, 2, 3]); 913 914 Json(elements).should.equal(Json(elements)); 915 Json(elements).should.not.equal(Json(otherElements)); 916 917 auto msg = ({ 918 Json(elements).should.equal(otherElements); 919 }).should.throwException!TestException.msg; 920 921 msg.split("\n")[0].should.equal("Json(elements) should equal [1, 2, 3]. [1, 2] is not equal to [1, 2, 3]."); 922 923 msg = ({ 924 Json(elements).should.equal(otherElements); 925 }).should.throwException!TestException.msg; 926 927 msg.split("\n")[0].should.equal("Json(elements) should equal [1, 2, 3]. [1, 2] is not equal to [1, 2, 3]."); 928 } 929 930 /// It throws on comparing a Json array with a string 931 unittest { 932 Json[] elements = [Json(1), Json(2)]; 933 auto msg = ({ 934 Json(elements).should.equal("5"); 935 }).should.throwException!TestException.msg; 936 937 msg.split("\n")[0].should.equal(`Json(elements) should equal "5". [1, 2] is not equal to "5".`); 938 939 msg = ({ 940 Json(elements).should.equal(Json("5")); 941 }).should.throwException!TestException.msg; 942 943 msg.split("\n")[0].should.equal(`Json(elements) should equal "5". [1, 2] is not equal to "5".`); 944 } 945 946 /// It should be able to compare two nested arrays 947 unittest { 948 Json[] element1 = [Json(1), Json(2)]; 949 Json[] element2 = [Json(10), Json(20)]; 950 951 Json[] elements = [Json(element1), Json(element2)]; 952 Json[] otherElements = [Json(element1), Json(element2), Json(element1)]; 953 954 Json(elements).should.equal([element1, element2]); 955 Json(elements).should.not.equal([element1, element2, element1]); 956 957 Json(elements).should.equal(Json(elements)); 958 Json(elements).should.not.equal(Json(otherElements)); 959 960 auto msg = ({ 961 Json(elements).should.equal(otherElements); 962 }).should.throwException!TestException.msg; 963 964 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]]."); 965 966 msg = ({ 967 Json(elements).should.equal(Json(otherElements)); 968 }).should.throwException!TestException.msg; 969 970 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]]."); 971 } 972 973 /// It should be able to compare two nested arrays with different levels 974 unittest { 975 Json nestedElement = Json([Json(1), Json(2)]); 976 977 Json[] elements = [nestedElement, Json(1)]; 978 Json[] otherElements = [nestedElement, Json(1), nestedElement]; 979 980 Json(elements).should.equal([nestedElement, Json(1)]); 981 Json(elements).should.not.equal([nestedElement, Json(1), nestedElement]); 982 983 Json(elements).should.equal(Json(elements)); 984 Json(elements).should.not.equal(Json(otherElements)); 985 986 auto msg = ({ 987 Json(elements).should.equal(otherElements); 988 }).should.throwException!TestException.msg; 989 990 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]]."); 991 992 msg = ({ 993 Json(elements).should.equal(Json(otherElements)); 994 }).should.throwException!TestException.msg; 995 996 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]]."); 997 } 998 999 /// It should find the key differences inside a Json object 1000 unittest { 1001 Json expectedObject = Json.emptyObject; 1002 Json testObject = Json.emptyObject; 1003 testObject["key"] = "some value"; 1004 testObject["nested"] = Json.emptyObject; 1005 testObject["nested"]["item1"] = "hello"; 1006 testObject["nested"]["item2"] = Json.emptyObject; 1007 testObject["nested"]["item2"]["value"] = "world"; 1008 1009 expectedObject["other"] = "other value"; 1010 1011 auto msg = ({ 1012 testObject.should.equal(expectedObject); 1013 }).should.throwException!TestException.msg; 1014 1015 msg.should.startWith(`testObject should equal {`); 1016 } 1017 1018 /// It should find the value differences inside a Json object 1019 unittest { 1020 Json expectedObject = Json.emptyObject; 1021 Json testObject = Json.emptyObject; 1022 testObject["key1"] = "some value"; 1023 testObject["key2"] = 1; 1024 1025 expectedObject["key1"] = "other value"; 1026 expectedObject["key2"] = 2; 1027 1028 auto msg = ({ 1029 testObject.should.equal(expectedObject); 1030 }).should.throwException!TestException.msg; 1031 1032 msg.should.startWith("testObject should equal {"); 1033 } 1034 1035 /// greaterThan support for Json Objects 1036 unittest { 1037 Json(5).should.be.greaterThan(4); 1038 Json(4).should.not.be.greaterThan(5); 1039 1040 Json(5f).should.be.greaterThan(4f); 1041 Json(4f).should.not.be.greaterThan(5f); 1042 1043 auto msg = ({ 1044 Json("").should.greaterThan(3); 1045 }).should.throwException!TestException.msg; 1046 1047 msg.split("\n")[0].should.equal(`Json("") should greater than 3.`); 1048 msg.split("\n")[1].should.equal("Can't convert the values to int"); 1049 1050 msg = ({ 1051 Json(false).should.greaterThan(3f); 1052 }).should.throwException!TestException.msg; 1053 1054 msg.split("\n")[0].should.equal(`Json(false) should greater than 3.`); 1055 msg.split("\n")[1].should.equal("Can't convert the values to float"); 1056 } 1057 1058 /// lessThan support for Json Objects 1059 unittest { 1060 Json(4).should.be.lessThan(5); 1061 Json(5).should.not.be.lessThan(4); 1062 1063 Json(4f).should.be.lessThan(5f); 1064 Json(5f).should.not.be.lessThan(4f); 1065 1066 auto msg = ({ 1067 Json("").should.lessThan(3); 1068 }).should.throwException!TestException.msg; 1069 1070 msg.split("\n")[0].should.equal(`Json("") should less than 3.`); 1071 msg.split("\n")[1].should.equal(`Can't convert the values to int`); 1072 1073 msg = ({ 1074 Json(false).should.lessThan(3f); 1075 }).should.throwException!TestException.msg; 1076 1077 msg.split("\n")[0].should.equal(`Json(false) should less than 3.`); 1078 msg.split("\n")[1].should.equal(`Can't convert the values to float`); 1079 } 1080 1081 /// between support for Json Objects 1082 unittest { 1083 Json(5).should.be.between(6, 4); 1084 Json(5).should.not.be.between(5, 6); 1085 1086 Json(5f).should.be.between(6f, 4f); 1087 Json(5f).should.not.be.between(5f, 6f); 1088 1089 auto msg = ({ 1090 Json(true).should.be.between(6f, 4f); 1091 }).should.throwException!TestException.msg; 1092 1093 msg.split("\n")[0].should.equal("Json(true) should be between 6 and 4. "); 1094 msg.split("\n")[1].should.equal("Can't convert the values to float"); 1095 } 1096 1097 /// should be able to use approximately for jsons 1098 unittest { 1099 Json(10f/3f).should.be.approximately(3, 0.34); 1100 Json(10f/3f).should.not.be.approximately(3, 0.24); 1101 1102 auto msg = ({ 1103 Json("").should.be.approximately(3, 0.34); 1104 }).should.throwException!TestException.msg; 1105 1106 msg.should.contain(`Can't parse the provided arguments!`); 1107 }