1 module text.json.EncodeTest; 2 3 import boilerplate; 4 import dshould; 5 import std.datetime; 6 import std.json; 7 import text.json.Encode; 8 import text.json.Json; 9 10 @("aggregate types are encoded to JSON text") 11 unittest 12 { 13 const expected = ` 14 { 15 "IntValueElement": 23, 16 "StringValueElement": "FOO", 17 "BoolValueElement": true, 18 "NestedElement": { 19 "Element": "Bar" 20 }, 21 "ArrayElement": [1, 2, 3], 22 "AssocArrayElement": { 23 "baz": { "Element": "whee" }, 24 "foo": { "Element": "bar" } 25 }, 26 "NestedArray": [ 27 { "Element": "Foo" }, 28 { "Element": "Bar" } 29 ], 30 "DateElement": "2000-01-02", 31 "SysTimeElement": "2000-01-02T10:00:00Z" 32 } 33 `; 34 35 // given 36 const value = (){ 37 import text.time.Convert : Convert; 38 39 with (Value.Builder()) 40 { 41 intValue = 23; 42 stringValue = "FOO"; 43 boolValue = true; 44 nestedValue = NestedValue("Bar"); 45 arrayValue = [1, 2, 3]; 46 assocArray = ["foo": NestedValue("bar"), "baz": NestedValue("whee")]; 47 nestedArray = [NestedValue("Foo"), NestedValue("Bar")]; 48 dateValue = Date(2000, 1, 2); 49 sysTimeValue = SysTime.fromISOExtString("2000-01-02T10:00:00Z"); 50 return value; 51 } 52 }(); 53 54 // when 55 const actualJson = encode(value).parseJSON; 56 57 // then 58 const expectedJson = expected.parseJSON; 59 60 actualJson.should.equal(expectedJson); 61 } 62 63 @("custom encoders are used on fields") 64 unittest 65 { 66 // given 67 const value = ValueWithEncoders("bla", "bla"); 68 69 // when 70 auto text = encode(value); 71 72 // then 73 const expected = `{ "asFoo": "foo", "asBar": "bar" }`; 74 75 text.parseJSON.should.equal(expected.parseJSON); 76 } 77 78 @("custom encoders are used on a type") 79 unittest 80 { 81 // given 82 struct Value 83 { 84 TypeWithEncoder field; 85 86 mixin(GenerateAll); 87 } 88 89 const value = Value(TypeWithEncoder()); 90 91 // when 92 auto text = encode(value); 93 94 // then 95 const expected = `{ "field": "123" }`; 96 97 text.parseJSON.should.equal(expected.parseJSON); 98 } 99 100 @("enums are encoded as strings") 101 unittest 102 { 103 enum Enum 104 { 105 A 106 } 107 108 struct Value 109 { 110 Enum field; 111 112 mixin(GenerateAll); 113 } 114 115 // given 116 const value = Value(Enum.A); 117 118 // when 119 const text = encode(value); 120 121 // then 122 const expected = `{ "field": "A" }`; 123 124 text.parseJSON.should.equal(expected.parseJSON); 125 } 126 127 @("alias-this is encoded inline") 128 unittest 129 { 130 struct A 131 { 132 int value2; 133 134 mixin(GenerateAll); 135 } 136 137 struct B 138 { 139 int value1; 140 141 A a; 142 143 alias a this; 144 145 mixin(GenerateAll); 146 } 147 148 // given 149 const value = B(3, A(5)); 150 151 // when 152 const actual = encode(value); 153 154 // then 155 const expected = `{ "value1": 3, "value2": 5 }`; 156 157 actual.parseJSON.should.equal(expected.parseJSON); 158 } 159 160 @("alias-this is encoded inline for aliased methods") 161 unittest 162 { 163 struct A 164 { 165 int value2; 166 167 mixin(GenerateAll); 168 } 169 170 struct B 171 { 172 int value1; 173 174 @ConstRead 175 A a_; 176 177 mixin(GenerateAll); 178 179 alias a this; 180 } 181 182 // given 183 const value = B(3, A(5)); 184 185 // when 186 const actual = encode(value); 187 188 // then 189 const expected = `{ "value1": 3, "value2": 5 }`; 190 191 actual.parseJSON.should.equal(expected.parseJSON); 192 } 193 194 @("arrays of enums are encoded as strings") 195 unittest 196 { 197 enum Enum 198 { 199 A, 200 } 201 202 struct Value 203 { 204 Enum[] value; 205 206 mixin(GenerateAll); 207 } 208 209 // given 210 const value = Value([Enum.A]); 211 212 // when 213 auto text = encode(value); 214 215 // then 216 const expected = `{ "value": ["A"] }`; 217 218 text.parseJSON.should.equal(expected.parseJSON); 219 } 220 221 struct NestedValue 222 { 223 @(Json("Element")) 224 public string value; 225 226 mixin (GenerateAll); 227 } 228 229 struct Value 230 { 231 @(Json("IntValueElement")) 232 public int intValue; 233 234 @(Json("StringValueElement")) 235 public string stringValue; 236 237 @(Json("BoolValueElement")) 238 public bool boolValue; 239 240 @(Json("NestedElement")) 241 public NestedValue nestedValue; 242 243 @(Json("ArrayElement")) 244 public const int[] arrayValue; 245 246 @(Json("AssocArrayElement")) 247 public NestedValue[string] assocArray; 248 249 @(Json("NestedArray")) 250 public NestedValue[] nestedArray; 251 252 @(Json("DateElement")) 253 public Date dateValue; 254 255 @(Json("SysTimeElement")) 256 public SysTime sysTimeValue; 257 258 mixin (GenerateAll); 259 } 260 261 struct ValueWithEncoders 262 { 263 @(Json("asFoo")) 264 @(Json.Encode!asFoo) 265 public string foo; 266 267 @(Json("asBar")) 268 @(Json.Encode!asBar) 269 public string bar; 270 271 static JSONValue asFoo(string field) 272 { 273 field.should.equal("bla"); 274 275 return JSONValue("foo"); 276 } 277 278 static JSONValue asBar(string field) 279 { 280 field.should.equal("bla"); 281 282 return JSONValue("bar"); 283 } 284 285 mixin(GenerateThis); 286 } 287 288 @(Json.Encode!encodeTypeWithEncoder) 289 struct TypeWithEncoder 290 { 291 } 292 293 JSONValue encodeTypeWithEncoder(TypeWithEncoder) 294 { 295 return JSONValue("123"); 296 } 297 298 @("transform functions may modify the values that are encoded") 299 unittest 300 { 301 import std.conv : to; 302 303 struct Inner 304 { 305 int value; 306 307 mixin(GenerateThis); 308 } 309 310 struct InnerDto 311 { 312 string encodedValue; 313 314 mixin(GenerateThis); 315 } 316 317 struct Struct 318 { 319 Inner inner; 320 321 mixin(GenerateThis); 322 } 323 324 InnerDto transform(Inner inner) 325 { 326 return InnerDto(inner.value.to!string); 327 } 328 329 // given 330 const value = Struct(Inner(5)); 331 332 // when 333 const actual = encode!(Struct, transform)(value); 334 335 // then 336 const expected = `{ "inner": { "encodedValue": "5" } }`; 337 338 actual.parseJSON.should.equal(expected.parseJSON); 339 } 340 341 @("transform functions returning JSONValue") 342 unittest 343 { 344 import std.conv : to; 345 346 struct Inner 347 { 348 int value; 349 350 mixin(GenerateThis); 351 } 352 353 struct Struct 354 { 355 Inner inner; 356 357 mixin(GenerateThis); 358 } 359 360 JSONValue transform(Inner inner) 361 { 362 return JSONValue(inner.value.to!string); 363 } 364 365 // given 366 const value = Struct(Inner(5)); 367 368 // when 369 const actual = encode!(Struct, transform)(value); 370 371 // then 372 const expected = `{ "inner": "5" }`; 373 374 actual.parseJSON.should.equal(expected.parseJSON); 375 } 376 377 @("struct with version_ field") 378 unittest 379 { 380 // given 381 struct Value 382 { 383 int version_; 384 385 mixin(GenerateAll); 386 } 387 388 const value = Value(1); 389 390 // when 391 auto text = encode(value); 392 393 // then 394 const expected = `{ "version": 1 }`; 395 396 text.parseJSON.should.equal(expected.parseJSON); 397 }