fluentasserts.vibe.json 467/480(97%) line coverage

      
10
20
30
40
50
60
70
80
90
100
110
120
130
140
150
163
170
183
191
200
210
220
231
240
250
260
2714
282
290
300
312
320
330
340
350
360
370
380
390
402
410
420
430
440
451
461
471
480
492
500
510
520
530
541
550
563
572
581
590
600
610
620
6313
640
650
660
670
682
690
700
710
720
731
741
751
761
771
781
791
801
811
820
832
840
850
860
870
880
891
901
911
921
930
941
951
961
971
981
991
1000
1012
1020
1030
1040
1050
1060
10724
1080
10924
11024
1110
11292
11368
1140
11568
11670
11746
1180
1190
12062
12112
1220
1230
12447
1254
1260
1270
1280
12968
130284
13140
1320
13340
13418
1350
1360
13740
1380
1390
1400
14168
1423
1430
14418
1454
1460
1474
1484
1490
1500
1510
15268
1530
1540
15524
1560
1570
1580
1590
1600
1610
1621
1631
1641
1651
1661
1671
1681
1691
1701
1710
1721
17312
1742
1752
1762
1772
1782
1790
1800
1810
1826
1830
1840
1850
18618
1870
188144
18936
1900
1910
19218
1930
1940
1950
1962
1970
19816
1994
2000
2010
2022
2030
2040
2050
2060
2070
20895
20995
21095
2110
2120
2130
2140
2150
2160
21723
2180
21923
22023
2210
22223
2230
22423
2255
2260
2270
2280
2290
2300
2310
2320
2330
2345
2350
2360
23718
2380
2390
2400
24149
24249
24349
24449
2450
2460
24710
24810
24910
2500
2510
2520
2530
25415
25524
2560
25715
2580
2590
2600
2617
26213
2630
2647
2650
2660
2670
2685
2695
2700
2715
2720
2730
2740
27512
27612
2770
27812
2790
2800
28149
2820
2830
2840
28549
2860
2870
2880
2890
2900
2910
2920
2930
29449
2950
2960
2970
29878
29978
30078
3010
3020
30346
3040
3050
30646
3070
30810
30920
3108
3110
3122
3132
3140
3150
31646
3170
31846
31974
3200
32137
32216
3230
3240
32542
3260
32718
3280
3290
33032
3310
3320
3330
3340
3350
3360
33718
33818
33918
3400
34118
3420
34318
3440
3450
34618
3470
3480
3490
3500
3510
3520
3530
3540
3550
3560
3570
3580
3592
3602
3610
3621
3630
3640
3650
3662
3670
3682
3690
3700
3712
3722
3730
3741
3750
3760
3770
3782
3790
3802
3810
3820
3830
38414
38520
3860
38710
3880
3890
3900
39120
3920
3938
3940
3950
3960
3970
3980
3990
4000
40110
4020
4034
4040
4050
4060
4074
4084
4090
4104
4110
4120
4130
4140
41510
4160
4174
4180
4190
4200
4214
4224
4230
4244
4250
4260
4270
4280
4299
4300
4314
4320
4330
4340
4354
4364
4370
4384
4390
4400
4410
4420
4435
4440
4452
4460
4470
4480
4492
4502
4510
4522
4530
4540
4550
4560
4570
45820
4597
4600
4610
46226
46313
46410
4650
4660
46713
46813
46913
4700
47113
4720
47313
4740
4750
4760
47732
47846
4790
48023
4815
4820
4830
4840
48527
48627
4873
4880
4890
49024
4914
4920
4930
49420
4953
4960
4970
49817
4993
5000
5010
50214
5038
5040
50579
50621
5070
5080
5098
5100
5110
5126
51312
5140
5156
5166
5176
5186
5196
5206
5216
5220
5236
5240
5256
5265
5275
5280
5295
5307
5312
5320
5330
5340
5350
5360
5375
5385
5390
5405
5415
5420
5435
5440
5450
5466
5470
5480
5490
5500
5510
5520
5530
5540
5550
5560
5572
5580
5590
5600
5610
5623
5632
5640
5650
5662
5672
5682
5690
5702
5712
5720
5730
5740
5750
5760
5772
5782
5792
5800
5810
5822
5832
5842
5850
5860
5873
5882
5890
5900
5912
5920
5933
5942
5950
5960
5972
5980
5990
6000
6010
6023
6032
6040
6050
6062
6072
6082
6090
6100
6110
6120
6133
6141
6152
6160
6170
6182
6192
6202
6210
6223
6231
6242
6250
6260
6272
6280
6293
6301
6312
6320
6330
6342
6350
6363
6371
6382
6390
6400
6412
6420
6430
6440
6450
6463
6471
6482
6490
6500
6512
6520
6533
6541
6552
6560
6570
6582
6590
6603
6611
6622
6630
6640
6652
6660
6673
6681
6692
6700
6710
6722
6730
6740
6750
6760
6773
6781
6792
6800
6810
6822
6832
6842
6850
6863
6871
6882
6890
6900
6912
6920
6930
6940
6950
6963
6972
6980
6990
7002
7012
7022
7030
7040
7050
7060
7072
7082
7092
7100
7112
7122
7132
7140
7153
7162
7170
7180
7192
7200
7213
7222
7230
7240
7252
7260
7270
7280
7290
7303
7312
7320
7330
7342
7350
7363
7372
7380
7390
7402
7410
7420
7430
7440
7452
7462
7472
7480
7492
7502
7510
7523
7532
7540
7550
7562
7570
7583
7592
7600
7610
7622
7630
7640
7650
7660
7673
7682
7690
7700
7712
7720
7733
7742
7750
7760
7772
7780
7790
7800
7810
7822
7832
7840
7852
7862
7870
7883
7892
7900
7910
7922
7930
7943
7952
7960
7970
7982
7990
8000
8010
8020
8033
8042
8050
8060
8072
8080
8093
8102
8110
8120
8132
8140
8150
8160
8170
8181
8191
8200
8212
8222
8230
8242
8252
8260
8273
8282
8290
8300
8312
8320
8333
8342
8350
8360
8372
8380
8390
8400
8410
8421
8433
8442
8450
8460
8472
8480
8493
8502
8510
8520
8532
8540
8550
8560
8570
8581
8591
8600
8611
8621
8630
8642
8652
8660
8672
8682
8690
8703
8712
8720
8730
8742
8750
8763
8772
8780
8790
8802
8810
8820
8830
8840
8851
8860
8871
8881
8890
8902
8912
8920
8932
8942
8950
8963
8972
8980
8990
9002
9010
9023
9032
9040
9050
9062
9070
9080
9090
9100
9111
9121
9131
9141
9151
9161
9171
9180
9191
9200
9213
9222
9230
9240
9252
9260
9270
9280
9290
9300
9310
9320
9330
9340
9350
9360
9370
9380
9390
9400
9410
9420
9430
9440
9450
9460
9470
9480
9491
9501
9511
9521
9530
9541
9551
9560
9573
9582
9590
9600
9612
9620
9630
9640
9650
9660
9670
9680
9690
9700
9710
9720
9730
9740
9750
9760
9770
9780
9790
9800
9810
9820
9830
9840
9850
9862
9872
9880
9892
9902
9910
9923
9932
9940
9950
9962
9972
9982
9990
10003
10012
10020
10030
10042
10052
10062
10070
10080
10090
10100
10112
10122
10130
10142
10152
10160
10173
10182
10190
10200
10212
10222
10232
10240
10253
10262
10270
10280
10292
10302
10312
10320
10330
10340
10350
10362
10372
10380
10392
10402
10410
10423
10432
10440
10450
10462
10472
10482
10490
10500
10510
10522
10532
10540
10553
10562
10570
10580
10592
10602
10612
10620
module fluentasserts.vibe.json; version(Have_vibe_d_data): import std.exception, std.conv, std.traits; import std.array, std.algorithm, std.typecons; import std.uni; import vibe.data.json; import fluentasserts.core.base; import fluentasserts.core.results; /// Get all the keys from your Json object string[] keys(Json obj, const string file = __FILE__, const size_t line = __LINE__) @trusted { string[] list; if(obj.type != Json.Type.object) { IResult[] results = [ cast(IResult) new MessageResult("Invalid Json type."), cast(IResult) new ExpectedActualResult("object", obj.type.to!string), cast(IResult) new SourceResult(file, line) ]; throw new TestException(results, file, line); } static if(typeof(obj.byKeyValue).stringof == "Rng") { foreach(string key, Json value; obj.byKeyValue) { list ~= key; } return list; } else { pragma(msg, "Json.keys is not compatible with your vibe.d version"); assert(false, "Json.keys is not compatible with your vibe.d version"); } } /// Empty Json object keys unittest { Json.emptyObject.keys.length.should.equal(0); } /// Json object keys unittest { auto obj = Json.emptyObject; obj["key1"] = 1; obj["key2"] = 3; obj.keys.should.containOnly(["key1", "key2"]); } /// Json array keys unittest { auto obj = Json.emptyArray; ({ obj.keys.should.contain(["key1", "key2"]); }).should.throwAnyException.msg.should.startWith("Invalid Json type."); } /// Get all the keys from your Json object. The levels will be sepparated by `.` or `[]` string[] nestedKeys(Json obj) @trusted { return obj.flatten.byKeyValue.map!"a.key".array; } /// Empty Json object keys unittest { Json.emptyObject.nestedKeys.length.should.equal(0); } /// Get all keys from nested object unittest { auto obj = Json.emptyObject; obj["key1"] = 1; obj["key2"] = 2; obj["key3"] = Json.emptyObject; obj["key3"]["item1"] = "3"; obj["key3"]["item2"] = Json.emptyObject; obj["key3"]["item2"]["item4"] = Json.emptyObject; obj["key3"]["item2"]["item5"] = Json.emptyObject; obj["key3"]["item2"]["item5"]["item6"] = Json.emptyObject; obj.nestedKeys.should.containOnly(["key1", "key2", "key3.item1", "key3.item2.item4", "key3.item2.item5.item6"]); } /// Get all keys from nested objects inside an array unittest { auto obj = Json.emptyObject; Json elm = Json.emptyObject; elm["item5"] = Json.emptyObject; elm["item5"]["item6"] = Json.emptyObject; obj["key2"] = Json.emptyArray; obj["key3"] = Json.emptyArray; obj["key3"] ~= Json("3"); obj["key3"] ~= Json.emptyObject; obj["key3"] ~= elm; obj["key3"] ~= [ Json.emptyArray ]; obj.nestedKeys.should.containOnly(["key2", "key3[0]", "key3[1]", "key3[2].item5.item6", "key3[3]"]); } /// Takes a nested Json object and moves the values to a Json assoc array where the key /// is the path from the original object to that value Json[string] flatten(Json object) @trusted { Json[string] elements; auto root = tuple("", object); Tuple!(string, Json)[] queue = [ root ]; while(queue.length > 0) { auto element = queue[0]; if(element[0] != "") { if(element[1].type != Json.Type.object && element[1].type != Json.Type.array) { elements[element[0]] = element[1]; } if(element[1].type == Json.Type.object && element[1].length == 0) { elements[element[0]] = element[1]; } if(element[1].type == Json.Type.array && element[1].length == 0) { elements[element[0]] = element[1]; } } if(element[1].type == Json.Type.object) { foreach(string key, value; element[1].byKeyValue) { string nextKey = key; if(element[0] != "") { nextKey = element[0] ~ "." ~ nextKey; } queue ~= tuple(nextKey, value); } } if(element[1].type == Json.Type.array) { size_t index; foreach(value; element[1].byValue) { string nextKey = element[0] ~ "[" ~ index.to!string ~ "]"; queue ~= tuple(nextKey, value); index++; } } queue = queue[1..$]; } return elements; } @trusted: /// Get a flatten object unittest { auto obj = Json.emptyObject; obj["key1"] = 1; obj["key2"] = 2; obj["key3"] = Json.emptyObject; obj["key3"]["item1"] = "3"; obj["key3"]["item2"] = Json.emptyObject; obj["key3"]["item2"]["item4"] = Json.emptyObject; obj["key3"]["item2"]["item5"] = Json.emptyObject; obj["key3"]["item2"]["item5"]["item6"] = Json.emptyObject; auto result = obj.flatten; result.byKeyValue.map!(a => a.key).should.containOnly(["key1", "key2", "key3.item1", "key3.item2.item4", "key3.item2.item5.item6"]); result["key1"].should.equal(1); result["key2"].should.equal(2); result["key3.item1"].should.equal("3"); result["key3.item2.item4"].should.equal(Json.emptyObject); result["key3.item2.item5.item6"].should.equal(Json.emptyObject); } auto unpackJsonArray(T : U[], U)(Json data) if(!isArray!U && isBasicType!U) { return data.byValue.map!(a => a.to!U).array.dup; } auto unpackJsonArray(T : U[], U)(Json data) if(!isArray!U && is(Unqual!U == Json)) { U[] result; foreach(element; data.byValue) { result ~= element; } return result; } auto unpackJsonArray(T : U[], U)(Json data) if(isArray!(U) && !isSomeString!(U[])) { U[] result; foreach(element; data.byValue) { result ~= unpackJsonArray!(U)(element); } return result; } struct ShouldJson(T) { private const T testData; this(U)(U value) { valueEvaluation = value.evaluation; testData = value.value; } mixin ShouldCommons; mixin ShouldThrowableCommons; auto validateJsonType(const T someValue, const string file, const size_t line) { auto haveSameType = someValue.type == testData.type; string expected = "a Json.Type." ~ testData.type.to!string; string actual = "a Json.Type." ~ someValue.type.to!string; Message[] msg; if(!haveSameType) { msg = [ Message(false, "They have incompatible types "), Message(false, "`Json.Type."), Message(true, testData.type.to!string), Message(false, "` != `Json.Type."), Message(true, someValue.type.to!string), Message(false, "`.") ]; return result(haveSameType, msg, [ new ExpectedActualResult(actual, expected) ], file, line); } return result(haveSameType, msg, [ ], file, line); } auto validateJsonType(U)(const string file, const size_t line) { Json.Type someType = Json.Type.undefined; string expected; string actual = "a Json.Type." ~ testData.type.to!string; bool haveSameType; static if(is(U == string)) { someType = Json.Type.string; expected = "a string or Json.Type." ~ someType.to!string; haveSameType = someType == testData.type; } static if(is(U == byte) || is(U == short) || is(U == int) || is(U == long) || is(U == ubyte) || is(U == ushort) || is(U == uint) || is(U == ulong)) { someType = Json.Type.int_; haveSameType = Json.Type.int_ == testData.type || Json.Type.float_ == testData.type; expected = "a " ~ U.stringof ~ " or Json.Type." ~ someType.to!string; } static if(is(U == float) || is(U == double)) { someType = Json.Type.float_; haveSameType = Json.Type.int_ == testData.type || Json.Type.float_ == testData.type; expected = "a " ~ U.stringof ~ " or Json.Type." ~ someType.to!string; } static if(is(U == bool)) { someType = Json.Type.bool_; haveSameType = someType == testData.type; expected = "a " ~ U.stringof ~ " or Json.Type." ~ someType.to!string; } static if(isArray!U && !isSomeString!U) { someType = Json.Type.array; haveSameType = someType == testData.type; expected = "a " ~ U.stringof ~ "[] or Json.Type." ~ someType.to!string; } if(expected == "") { expected = "a Json.Type." ~ someType.to!string; } Message[] msg = [ Message(false, "They have incompatible types "), Message(false, "`Json.Type."), Message(true, testData.type.to!string), Message(false, "` != `"), Message(true, U.stringof), Message(false, "`.") ]; return result(haveSameType, msg, [ new ExpectedActualResult(expected, actual) ], file, line); } auto equal(U)(const U someValue, const string file = __FILE__, const size_t line = __LINE__) if(!isArray!U || isSomeString!U) { addMessage(" equal `"); addValue(someValue.to!string); addMessage("`"); static if(is(U == string) || std.traits.isNumeric!U || is(U == bool)) { U nativeVal; try { nativeVal = testData.to!U; } catch(ConvException e) { addMessage(". "); if(e.msg.length > 0 && e.msg[0].toUpper == e.msg[0]) { addValue(e.msg); } else { addMessage("During conversion "); addValue(e.msg); } } validateException; if(expectedValue) { auto typeResult = validateJsonType!U(file, line); if(typeResult.willThrow) { return typeResult; } return nativeVal.should.equal(someValue, file, line); } else { return nativeVal.should.not.equal(someValue, file, line); } } else static if(is(U == Json)) { return equalJson(someValue, file, line); } else { static assert(false, "You can not validate `Json` against `" ~ U.stringof ~ "`"); } } auto equal(U)(const U[] someArray, const string file = __FILE__, const size_t line = __LINE__) { addMessage(" equal `"); addValue(someArray.to!string); addMessage("`"); validateException; Unqual!U[] nativeVal; try { nativeVal = unpackJsonArray!(U[])(testData); } catch(Exception e) { addMessage(". "); if(e.msg.length > 0 && e.msg[0].toUpper == e.msg[0]) { addValue(e.msg); } else { addMessage("During conversion "); addValue(e.msg); } } static if(is(U == string) || std.traits.isNumeric!U || is(U == bool)) { if(expectedValue) { auto typeResult = validateJsonType!(U[])(file, line); if(typeResult.willThrow) { return typeResult; } return nativeVal.should.equal(someArray, file, line); } else { return nativeVal.should.not.equal(someArray, file, line); } } else static if(isArray!U) { if(expectedValue) { auto typeResult = validateJsonType!(U[])(file, line); if(typeResult.willThrow) { return typeResult; } return nativeVal.should.equal(someArray, file, line); } else { return nativeVal.should.not.equal(someArray, file, line); } } else static if(is(U == Json)) { if(expectedValue) { auto typeResult = validateJsonType!(U[])(file, line); if(typeResult.willThrow) { return typeResult; } return nativeVal.should.equal(someArray, file, line); } else { return nativeVal.should.not.equal(someArray, file, line); } } else { static assert(false, "You can not validate `Json` against `" ~ U.stringof ~ "`"); } } auto greaterThan(U)(const U someValue, const string file = __FILE__, const size_t line = __LINE__) if(isNumeric!U) { auto enforceResult = enforceNumericJson("greaterThan", file, line); if(enforceResult.willThrow) { return enforceResult; } if(expectedValue) { return testData.to!U.should.be.greaterThan(someValue, file, line); } else { return testData.to!U.should.not.be.greaterThan(someValue, file, line); } } auto lessThan(U)(const U someValue, const string file = __FILE__, const size_t line = __LINE__) if(isNumeric!U) { auto enforceResult = enforceNumericJson("lessThan", file, line); if(enforceResult.willThrow) { return enforceResult; } if(expectedValue) { return testData.to!U.should.be.lessThan(someValue, file, line); } else { return testData.to!U.should.not.be.lessThan(someValue, file, line); } } auto between(U)(const U limit1, const U limit2, const string file = __FILE__, const size_t line = __LINE__) if(isNumeric!U) { auto enforceResult = enforceNumericJson("between", file, line); if(enforceResult.willThrow) { return enforceResult; } if(expectedValue) { return testData.to!U.should.be.between(limit1, limit2, file, line); } else { return testData.to!U.should.not.be.between(limit1, limit2, file, line); } } auto approximately(U)(const U someValue, const U delta, const string file = __FILE__, const size_t line = __LINE__) if(isNumeric!U) { auto enforceResult = enforceNumericJson("approximately", file, line); if(enforceResult.willThrow) { return enforceResult; } if(expectedValue) { return testData.to!U.should.be.approximately(someValue, delta, file, line); } else { return testData.to!U.should.not.be.approximately(someValue, delta, file, line); } } private { auto enforceNumericJson(string functionName, const string file, const size_t line) { if(!expectedValue) { return Result.success; } auto typeResult = ( testData.type == Json.Type.int_ || testData.type == Json.Type.float_ ).should.equal(true, file, line); typeResult.message = new MessageResult(" must contain a number to use " ~ functionName ~ ". A `"); typeResult.message.addValue("Json.Type." ~ testData.type.to!string); typeResult.message.addText("` was found instead."); typeResult.results = [ new ExpectedActualResult("Json.Type.int_ or Json.Type.float_", "Json.Type." ~ testData.type.to!string) ]; return typeResult; } auto equalJson(const Json someValue, const string file, const size_t line) { if(expectedValue) { auto typeResult = validateJsonType(someValue, file, line); if(typeResult.willThrow) { return typeResult; } } beginCheck; if(testData.type == Json.Type.string) { return equal(someValue.to!string, file, line); } if(testData.type == Json.Type.int_) { return equal(someValue.to!long, file, line); } if(testData.type == Json.Type.float_) { return equal(someValue.to!double, file, line); } if(testData.type == Json.Type.bool_) { return equal(someValue.to!bool, file, line); } if(testData.type == Json.Type.array) { Json[] values; foreach(value; someValue.byValue) { values ~= value; } return equal(values, file, line); } auto isSame = testData == someValue; auto expected = expectedValue ? someValue.toPrettyString : "something different than the Actual data"; IResult[] results; auto message = new MessageResult(""); message.addText("\n"); message.addValue("Expected:"); message.addText("\n" ~ expected ~ "\n\n"); message.addValue("Actual:"); message.addText("\n" ~ testData.toPrettyString); results ~= message; if(expectedValue) { auto flattenTestData = testData.flatten; auto flattenSomeValue = someValue.flatten; foreach(string key, value; flattenTestData) { if(key in flattenSomeValue && flattenSomeValue[key] != value) { results ~= new ExpectedActualResult(key, flattenSomeValue[key].to!string ~ " (Json.Type." ~ flattenSomeValue[key].type.to!string ~ ")", value.to!string ~ " (Json.Type." ~ value.type.to!string ~ ")"); } } auto infoResult = new ListInfoResult(); auto comparison = ListComparison!string(someValue.nestedKeys, testData.nestedKeys); infoResult.add("Extra key", "Extra keys", comparison.extra); infoResult.add("Missing key", "Missing keys", comparison.missing); results ~= infoResult; } return result(isSame, [], results, file, line); } } } version(unittest) { import std.string; } /// It should be able to compare 2 empty json objects unittest { Json.emptyObject.should.equal(Json.emptyObject); } /// It should be able to compare an empty object with an empty array unittest { auto msg = ({ Json.emptyObject.should.equal(Json.emptyArray); }).should.throwException!TestException.msg; msg.split("\n")[0].strip.should.equal("Json.emptyObject should equal `[]`. They have incompatible types `Json.Type.object` != `Json.Type.array`."); msg.split("\n")[2].strip.should.equal("Expected:a Json.Type.array"); msg.split("\n")[3].strip.should.equal("Actual:a Json.Type.object"); ({ Json.emptyObject.should.not.equal(Json.emptyArray); }).should.not.throwException!TestException; } /// It should be able to compare two strings unittest { ({ Json("test string").should.equal("test string"); Json("other string").should.not.equal("test"); }).should.not.throwAnyException; ({ Json("test string").should.equal(Json("test string")); Json("other string").should.not.equal(Json("test")); }).should.not.throwAnyException; auto msg = ({ Json("test string").should.equal("test"); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(\"test string\") should equal `test`. `test string` is not equal to `test`."); msg = ({ Json("test string").should.equal(Json("test")); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(\"test string\") should equal `test`. `test string` is not equal to `test`."); } /// It throw on comparing a Json number with a string unittest { auto msg = ({ Json(4).should.equal("some string"); }).should.throwException!TestException.msg; msg.split("\n")[0].strip.should.equal("Json(4) should equal `some string`. They have incompatible types `Json.Type.int_` != `string`."); msg.split("\n")[2].strip.should.equal("Expected:a string or Json.Type.string"); msg.split("\n")[3].strip.should.equal("Actual:a Json.Type.int_"); } /// It throws when you compare a Json string with integer values unittest { auto msg = ({ byte val = 4; Json("some string").should.equal(val); }).should.throwException!TestException.msg; msg.split("\n")[0].strip.should.equal("Json(\"some string\") should equal `4`. Unexpected 's' when converting from type string to type long. They have incompatible types `Json.Type.string` != `byte`."); msg.split("\n")[2].strip.should.equal("Expected:a byte or Json.Type.int_"); msg.split("\n")[3].strip.should.equal("Actual:a Json.Type.string"); msg = ({ short val = 4; Json("some string").should.equal(val); }).should.throwException!TestException.msg; msg.split("\n")[0].strip.should.equal("Json(\"some string\") should equal `4`. Unexpected 's' when converting from type string to type long. They have incompatible types `Json.Type.string` != `short`."); msg = ({ int val = 4; Json("some string").should.equal(val); }).should.throwException!TestException.msg; msg.split("\n")[0].strip.should.equal("Json(\"some string\") should equal `4`. Unexpected 's' when converting from type string to type long. They have incompatible types `Json.Type.string` != `int`."); msg = ({ long val = 4; Json("some string").should.equal(val); }).should.throwException!TestException.msg; msg.split("\n")[0].strip.should.equal("Json(\"some string\") should equal `4`. Unexpected 's' when converting from type string to type long. They have incompatible types `Json.Type.string` != `long`."); } /// It throws when you compare a Json string with unsigned integer values unittest { auto msg = ({ ubyte val = 4; Json("some string").should.equal(val); }).should.throwException!TestException.msg; msg.split("\n")[0].strip.should.equal("Json(\"some string\") should equal `4`. Unexpected 's' when converting from type string to type long. They have incompatible types `Json.Type.string` != `ubyte`."); msg = ({ ushort val = 4; Json("some string").should.equal(val); }).should.throwException!TestException.msg; msg.split("\n")[0].strip.should.equal("Json(\"some string\") should equal `4`. Unexpected 's' when converting from type string to type long. They have incompatible types `Json.Type.string` != `ushort`."); msg = ({ uint val = 4; Json("some string").should.equal(val); }).should.throwException!TestException.msg; msg.split("\n")[0].strip.should.equal("Json(\"some string\") should equal `4`. Unexpected 's' when converting from type string to type long. They have incompatible types `Json.Type.string` != `uint`."); msg = ({ ulong val = 4; Json("some string").should.equal(val); }).should.throwException!TestException.msg; msg.split("\n")[0].strip.should.equal("Json(\"some string\") should equal `4`. Unexpected 's' when converting from type string to type long. They have incompatible types `Json.Type.string` != `ulong`."); } /// It throws when you compare a Json string with floating point values unittest { auto msg = ({ float val = 3.14; Json("some string").should.equal(val); }).should.throwException!TestException.msg; msg.split("\n")[0].strip.should.equal("Json(\"some string\") should equal `3.14`. During conversion no digits seen. They have incompatible types `Json.Type.string` != `float`."); msg.split("\n")[2].strip.should.equal("Expected:a float or Json.Type.float_"); msg.split("\n")[3].strip.should.equal("Actual:a Json.Type.string"); msg = ({ double val = 3.14; Json("some string").should.equal(val); }).should.throwException!TestException.msg; msg.split("\n")[0].strip.should.equal("Json(\"some string\") should equal `3.14`. During conversion no digits seen. They have incompatible types `Json.Type.string` != `double`."); } /// It throws when you compare a Json string with bool values unittest { auto msg = ({ Json("some string").should.equal(false); }).should.throwException!TestException.msg; msg.split("\n")[0].strip.should.equal("Json(\"some string\") should equal `false`. They have incompatible types `Json.Type.string` != `bool`."); msg.split("\n")[2].strip.should.equal("Expected:a bool or Json.Type.bool_"); msg.split("\n")[3].strip.should.equal("Actual:a Json.Type.string"); } /// It should be able to compare two integers unittest { Json(4L).should.equal(4f); Json(4).should.equal(4); Json(4).should.not.equal(5); Json(4).should.equal(Json(4)); Json(4).should.not.equal(Json(5)); Json(4L).should.not.equal(Json(5f)); auto msg = ({ Json(4).should.equal(5); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(4) should equal `5`."); msg = ({ Json(4).should.equal(Json(5)); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(4) should equal `5`."); } /// It throws on comparing an integer Json with a string unittest { auto msg = ({ Json(4).should.equal("5"); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(4) should equal `5`. They have incompatible types `Json.Type.int_` != `string`."); msg = ({ Json(4).should.equal(Json("5")); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(4) should equal `5`. They have incompatible types `Json.Type.int_` != `Json.Type.string`."); } /// It should be able to compare two floating point numbers unittest { Json(4f).should.equal(4L); Json(4.3).should.equal(4.3); Json(4.3).should.not.equal(5.3); Json(4.3).should.equal(Json(4.3)); Json(4.3).should.not.equal(Json(5.3)); auto msg = ({ Json(4.3).should.equal(5.3); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(4.3) should equal `5.3`."); msg = ({ Json(4.3).should.equal(Json(5.3)); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(4.3) should equal `5.3`."); } /// It throws on comparing an floating point Json with a string unittest { auto msg = ({ Json(4f).should.equal("5"); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(4f) should equal `5`. They have incompatible types `Json.Type.float_` != `string`."); msg = ({ Json(4f).should.equal(Json("5")); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(4f) should equal `5`. They have incompatible types `Json.Type.float_` != `Json.Type.string`."); } /// It should be able to compare two booleans unittest { Json(true).should.equal(true); Json(true).should.not.equal(false); Json(true).should.equal(Json(true)); Json(true).should.not.equal(Json(false)); auto msg = ({ Json(true).should.equal(false); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(true) should equal `false`."); msg = ({ Json(true).should.equal(Json(false)); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(true) should equal `false`."); } /// It throws on comparing a bool Json with a string unittest { auto msg = ({ Json(true).should.equal("5"); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(true) should equal `5`. They have incompatible types `Json.Type.bool_` != `string`."); msg = ({ Json(true).should.equal(Json("5")); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(true) should equal `5`. They have incompatible types `Json.Type.bool_` != `Json.Type.string`."); } /// It should be able to compare two arrays unittest { Json[] elements = [Json(1), Json(2)]; Json[] otherElements = [Json(1), Json(2), Json(3)]; Json(elements).should.equal([1, 2]); Json(elements).should.not.equal([1, 2, 3]); Json(elements).should.equal(Json(elements)); Json(elements).should.not.equal(Json(otherElements)); auto msg = ({ Json(elements).should.equal(otherElements); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(elements) should equal `[1, 2, 3]`."); msg = ({ Json(elements).should.equal(otherElements); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(elements) should equal `[1, 2, 3]`."); } /// It throws on comparing a Json array with a string unittest { Json[] elements = [Json(1), Json(2)]; auto msg = ({ Json(elements).should.equal("5"); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(elements) should equal `5`. They have incompatible types `Json.Type.array` != `string`."); msg = ({ Json(elements).should.equal(Json("5")); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(elements) should equal `5`. They have incompatible types `Json.Type.array` != `Json.Type.string`."); } /// It should be able to compare two nested arrays unittest { Json[] element1 = [Json(1), Json(2)]; Json[] element2 = [Json(10), Json(20)]; Json[] elements = [Json(element1), Json(element2)]; Json[] otherElements = [Json(element1), Json(element2), Json(element1)]; Json(elements).should.equal([element1, element2]); Json(elements).should.not.equal([element1, element2, element1]); Json(elements).should.equal(Json(elements)); Json(elements).should.not.equal(Json(otherElements)); auto msg = ({ Json(elements).should.equal(otherElements); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(elements) should equal `[[1,2], [10,20], [1,2]]`."); msg = ({ Json(elements).should.equal(Json(otherElements)); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(elements) should equal `[[1,2], [10,20], [1,2]]`."); } /// It should be able to compare two nested arrays with different levels unittest { Json nestedElement = Json([Json(1), Json(2)]); Json[] elements = [nestedElement, Json(1)]; Json[] otherElements = [nestedElement, Json(1), nestedElement]; Json(elements).should.equal([nestedElement, Json(1)]); Json(elements).should.not.equal([nestedElement, Json(1), nestedElement]); Json(elements).should.equal(Json(elements)); Json(elements).should.not.equal(Json(otherElements)); auto msg = ({ Json(elements).should.equal(otherElements); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(elements) should equal `[[1,2], 1, [1,2]]`."); msg = ({ Json(elements).should.equal(Json(otherElements)); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(elements) should equal `[[1,2], 1, [1,2]]`."); } /// It should find the key differences inside a Json object unittest { Json expectedObject = Json.emptyObject; Json testObject = Json.emptyObject; testObject["key"] = "some value"; testObject["nested"] = Json.emptyObject; testObject["nested"]["item1"] = "hello"; testObject["nested"]["item2"] = Json.emptyObject; testObject["nested"]["item2"]["value"] = "world"; expectedObject["other"] = "other value"; auto msg = ({ testObject.should.equal(expectedObject); }).should.throwException!TestException.msg; msg.should.startWith("testObject should equal `{\"other\":\"other value\"}`. Expected: { \t\"other\": \"other value\" } Actual: { \t\"nested\": { \t\t\"item1\": \"hello\", \t\t\"item2\": { \t\t\t\"value\": \"world\" \t\t} \t}, \t\"key\": \"some value\" } Extra keys:nested.item2.value,nested.item1,key Missing key:other"); } /// It should find the value differences inside a Json object unittest { Json expectedObject = Json.emptyObject; Json testObject = Json.emptyObject; testObject["key1"] = "some value"; testObject["key2"] = 1; expectedObject["key1"] = "other value"; expectedObject["key2"] = 2; auto msg = ({ testObject.should.equal(expectedObject); }).should.throwException!TestException.msg; msg.should.startWith("testObject should equal `{\"key1\":\"other value\",\"key2\":2}`. Expected: { \t\"key1\": \"other value\", \t\"key2\": 2 } Actual: { \t\"key1\": \"some value\", \t\"key2\": 1 } key1 Expected:other value (Json.Type.string) Actual:some value (Json.Type.string) key2 Expected:2 (Json.Type.int_) Actual:1 (Json.Type.int_)"); } /// greaterThan support for Json Objects unittest { Json(5).should.be.greaterThan(4); Json(4).should.not.be.greaterThan(5); Json(5f).should.be.greaterThan(4f); Json(4f).should.not.be.greaterThan(5f); auto msg = ({ Json("").should.greaterThan(3); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(\"\") must contain a number to use greaterThan. A `Json.Type.string` was found instead."); msg.split("\n")[2].should.equal(" Expected:Json.Type.int_ or Json.Type.float_"); msg.split("\n")[3].should.equal(" Actual:Json.Type.string"); msg = ({ Json(false).should.greaterThan(3f); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(false) must contain a number to use greaterThan. A `Json.Type.bool_` was found instead."); msg.split("\n")[2].should.equal(" Expected:Json.Type.int_ or Json.Type.float_"); msg.split("\n")[3].should.equal(" Actual:Json.Type.bool_"); } /// lessThan support for Json Objects unittest { Json(4).should.be.lessThan(5); Json(5).should.not.be.lessThan(4); Json(4f).should.be.lessThan(5f); Json(5f).should.not.be.lessThan(4f); auto msg = ({ Json("").should.lessThan(3); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(\"\") must contain a number to use lessThan. A `Json.Type.string` was found instead."); msg.split("\n")[2].should.equal(" Expected:Json.Type.int_ or Json.Type.float_"); msg.split("\n")[3].should.equal(" Actual:Json.Type.string"); msg = ({ Json(false).should.lessThan(3f); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(false) must contain a number to use lessThan. A `Json.Type.bool_` was found instead."); msg.split("\n")[2].should.equal(" Expected:Json.Type.int_ or Json.Type.float_"); msg.split("\n")[3].should.equal(" Actual:Json.Type.bool_"); } /// between support for Json Objects unittest { Json(5).should.be.between(6, 4); Json(5).should.not.be.between(5, 6); Json(5f).should.be.between(6f, 4f); Json(5f).should.not.be.between(5f, 6f); auto msg = ({ Json(true).should.be.between(6f, 4f); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(true) must contain a number to use between. A `Json.Type.bool_` was found instead."); msg.split("\n")[2].should.equal(" Expected:Json.Type.int_ or Json.Type.float_"); msg.split("\n")[3].should.equal(" Actual:Json.Type.bool_"); } unittest { Json(10f/3f).should.be.approximately(3, 0.34); Json(10f/3f).should.not.be.approximately(3, 0.24); auto msg = ({ Json("").should.be.approximately(3, 0.34); }).should.throwException!TestException.msg; msg.split("\n")[0].should.equal("Json(\"\") must contain a number to use approximately. A `Json.Type.string` was found instead."); msg.split("\n")[2].should.equal(" Expected:Json.Type.int_ or Json.Type.float_"); msg.split("\n")[3].should.equal(" Actual:Json.Type.string"); }