v2 / vlib / x / json2 / tests / json_module_compatibility_test / json_test.v
451 lines · 387 sloc · 8.77 KB · e3d4433d5df208c82b14168a45ab4ab5eeebde0b
Raw
1import x.json2 as json
2import time
3
4enum JobTitle {
5 manager
6 executive
7 worker
8}
9
10struct Employee {
11pub mut:
12 name string
13 age int
14 salary f32
15 title JobTitle
16 sub_employee SubEmployee //! FIXME - decode
17}
18
19pub struct SubEmployee {
20pub mut:
21 name string
22 age int
23 salary f32
24 title JobTitle
25}
26
27fn test_simple() {
28 sub_employee := SubEmployee{
29 name: 'João'
30 }
31 x := Employee{'Peter', 28, 95000.5, .worker, sub_employee}
32 s := json.encode[Employee](x, enum_as_int: true)
33 assert s == '{"name":"Peter","age":28,"salary":95000.5,"title":2,"sub_employee":{"name":"João","age":0,"salary":0,"title":0}}'
34
35 y := json.decode[Employee](s) or {
36 println(err)
37 assert false
38 return
39 }
40 assert y.name == 'Peter'
41 assert y.age == 28
42 assert y.salary == 95000.5
43 assert y.title == .worker
44 // assert y.sub_employee.name == 'João'
45 assert y.sub_employee.age == 0
46 assert y.sub_employee.salary == 0.0
47 // assert y.sub_employee.title == .worker //! FIXME
48}
49
50// const currency_id = 'cconst'
51
52// struct Price {
53// net f64
54// currency_id string @[json: currencyId] = currency_id
55// }
56
57struct User2 {
58mut:
59 age int
60 nums []int
61 reg_date time.Time
62}
63
64// User struct needs to be `pub mut` for now in order to access and manipulate values
65pub struct User {
66pub mut:
67 age int
68 nums []int
69 last_name string @[json: lastName]
70 is_registered bool @[json: IsRegistered]
71 typ int @[json: 'type']
72 pets string @[json: 'pet_animals'; raw]
73}
74
75fn test_parse_user() {
76 s := '{"age": 10, "nums": [1,2,3], "type": 1, "lastName": "Johnson", "IsRegistered": true, "pet_animals": {"name": "Bob", "animal": "Dog"}}'
77
78 u := json.decode[User](s)!
79
80 assert u.age == 10
81 assert u.last_name == 'Johnson'
82 assert u.is_registered == true
83 // assert u.nums.len == 3
84 // assert u.nums[0] == 1
85 // assert u.nums[1] == 2
86 // assert u.nums[2] == 3
87 assert u.typ == 1
88 assert u.pets == '{"name": "Bob", "animal": "Dog"}'
89}
90
91fn test_encode_decode_time() {
92 user := User2{
93 age: 25
94 reg_date: time.new(year: 2020, month: 12, day: 22, hour: 7, minute: 23)
95 }
96 s := json.encode(user)
97 assert s == '{"age":25,"nums":[],"reg_date":"2020-12-22T07:23:00.000Z"}'
98
99 assert s.contains('"reg_date":"2020-12-22T07:23:00.000Z"')
100 user2 := json.decode[User2](s)!
101 assert user2.reg_date.str() == '2020-12-22 07:23:00'
102}
103
104fn (mut u User) foo() string {
105 return json.encode(u)
106}
107
108fn test_encode_user() {
109 mut usr := User{
110 age: 10
111 nums: [1, 2, 3]
112 last_name: 'Johnson'
113 is_registered: true
114 typ: 0
115 pets: 'foo'
116 }
117 expected := '{"age":10,"nums":[1,2,3],"lastName":"Johnson","IsRegistered":true,"type":0,"pet_animals":"foo"}'
118 out := json.encode[User](usr)
119 // println(out)
120 assert out == expected
121 // Test json.encode on mutable pointers
122 assert usr.foo() == expected
123}
124
125struct Color {
126pub mut:
127 space string
128 point string @[raw]
129}
130
131fn test_raw_json_field() {
132 color := json.decode[Color]('{"space": "YCbCr", "point": {"Y": 123}}') or {
133 assert false
134 Color{}
135 }
136 assert color.point == '{"Y": 123}'
137 assert color.space == 'YCbCr'
138}
139
140fn test_bad_raw_json_field() {
141 color := json.decode[Color]('{"space": "YCbCr"}') or { return }
142 assert color.point == ''
143 assert color.space == 'YCbCr'
144}
145
146fn test_encode_map() {
147 expected := '{"one":1,"two":2,"three":3,"four":4}'
148 numbers := {
149 'one': json.Any(1)
150 'two': json.Any(2)
151 'three': json.Any(3)
152 'four': json.Any(4)
153 }
154 // numbers := {
155 // 'one': 1
156 // 'two': 2
157 // 'three': 3
158 // 'four': 4
159 // }
160 assert json.encode(numbers) == expected
161 assert numbers.str() == expected
162}
163
164type ID = string
165type GG = int
166
167struct Message {
168 id ID
169 ij GG
170}
171
172fn test_encode_alias_struct() {
173 expected := '{"id":"118499178790780929","ij":999998888}'
174 msg := Message{'118499178790780929', 999998888}
175 out := json.encode[Message](msg)
176 assert out == expected
177}
178
179struct StByteArray {
180 ba []u8
181}
182
183fn test_byte_array() {
184 assert json.encode(StByteArray{ ba: [u8(1), 2, 3, 4, 5] }) == '{"ba":[1,2,3,4,5]}'
185}
186
187struct Bar {
188 x string
189}
190
191fn bar[T](payload string) !Bar { // ?T doesn't work currently
192 result := json.decode[T](payload)!
193 return result
194}
195
196fn test_generic() {
197 result := bar[Bar]('{"x":"test"}') or { Bar{} }
198 assert result.x == 'test'
199}
200
201struct Foo[T] {
202pub:
203 name string
204 data T
205}
206
207fn test_generic_struct() {
208 foo_int := Foo[int]{'bar', 12}
209 foo_enc := json.encode(foo_int)
210 assert foo_enc == '{"name":"bar","data":12}'
211 foo_dec := json.decode[Foo[int]](foo_enc)!
212 assert foo_dec.name == 'bar'
213 assert foo_dec.data == 12
214}
215
216type StringAlias = string
217
218// TODO: encode_pretty array, sum types, struct, alias of struct and others...
219struct Foo2 {
220 ux8 u8
221 ux16 u16
222 ux32 u32
223 ux64 u64
224 sx8 i8
225 sx16 i16
226 sx32 int
227 sx64 i64
228 a bool
229 b string
230 c StringAlias
231}
232
233fn test_pretty() {
234 foo := Foo2{1, 2, 3, 4, -1, -2, -3, -4, true, 'abc', 'aliens'}
235 assert json.encode(foo, prettify: true, indent_string: ' ') == '{
236 "ux8": 1,
237 "ux16": 2,
238 "ux32": 3,
239 "ux64": 4,
240 "sx8": -1,
241 "sx16": -2,
242 "sx32": -3,
243 "sx64": -4,
244 "a": true,
245 "b": "abc",
246 "c": "aliens"
247}'
248}
249
250struct Aa {
251 sub AliasType
252}
253
254struct Bb {
255 a int
256}
257
258type AliasType = Bb
259
260fn test_encode_alias_field() {
261 s := json.encode(Aa{
262 sub: Bb{
263 a: 1
264 }
265 })
266 assert s == '{"sub":{"a":1}}'
267}
268
269struct APrice {}
270
271pub struct Association {
272 association &Association = unsafe { nil }
273 price APrice
274}
275
276fn test_encoding_struct_with_pointers() {
277 value := Association{
278 association: &Association{
279 price: APrice{}
280 }
281 price: APrice{}
282 }
283 // println(value)
284 assert json.encode(value) == '{"association":{"price":{}},"price":{}}'
285}
286
287pub struct City {
288mut:
289 name string
290}
291
292pub struct Country {
293mut:
294 cities []City
295 name string
296}
297
298struct Data {
299mut:
300 countries []Country
301 users map[string]User
302 extra map[string]map[string]int
303}
304
305fn test_nested_type() {
306 data_expected := '{"countries":[{"cities":[{"name":"London"},{"name":"Manchester"}],"name":"UK"},{"cities":[{"name":"Donlon"},{"name":"Termanches"}],"name":"KU"}],"users":{"Foo":{"age":10,"nums":[1,2,3],"lastName":"Johnson","IsRegistered":true,"type":0,"pet_animals":"little foo"},"Boo":{"age":20,"nums":[5,3,1],"lastName":"Smith","IsRegistered":false,"type":4,"pet_animals":"little boo"}},"extra":{"2":{"n1":2,"n2":4,"n3":8,"n4":16},"3":{"n1":3,"n2":9,"n3":27,"n4":81}}}'
307 data := Data{
308 countries: [
309 Country{
310 name: 'UK'
311 cities: [City{'London'}, City{'Manchester'}]
312 },
313 Country{
314 name: 'KU'
315 cities: [City{'Donlon'}, City{'Termanches'}]
316 },
317 ]
318 users: {
319 'Foo': User{
320 age: 10
321 nums: [1, 2, 3]
322 last_name: 'Johnson'
323 is_registered: true
324 typ: 0
325 pets: 'little foo'
326 }
327 'Boo': User{
328 age: 20
329 nums: [5, 3, 1]
330 last_name: 'Smith'
331 is_registered: false
332 typ: 4
333 pets: 'little boo'
334 }
335 }
336 extra: {
337 '2': {
338 'n1': 2
339 'n2': 4
340 'n3': 8
341 'n4': 16
342 }
343 '3': {
344 'n1': 3
345 'n2': 9
346 'n3': 27
347 'n4': 81
348 }
349 }
350 }
351 out := json.encode(data)
352 assert out == data_expected
353}
354
355struct Human {
356 name string
357}
358
359struct Item {
360 tag string
361}
362
363enum Animal {
364 dog // Will be encoded as `0`
365 cat
366}
367
368type Entity = Animal | Human | Item | string | time.Time
369
370struct SomeGame {
371 title string
372 player Entity
373 other []Entity
374}
375
376fn test_encode_decode_sumtype() {
377 t := time.now()
378 game := SomeGame{
379 title: 'Super Mega Game'
380 player: Human{'Monke'}
381 other: [
382 Entity(Item{'Pen'}),
383 Item{'Cookie'},
384 Animal.cat,
385 'Stool',
386 t,
387 ]
388 }
389
390 enc := json.encode(game, enum_as_int: true)
391
392 assert enc == '{"title":"Super Mega Game","player":{"name":"Monke","_type":"Human"},"other":[{"tag":"Pen","_type":"Item"},{"tag":"Cookie","_type":"Item"},1,"Stool",{"_type":"${typeof(time.Time{}).name.all_after_last('.')}","value":${t.unix()}}]}'
393
394 dec := json.decode[SomeGame](enc)!
395
396 assert game.title == dec.title
397 assert game.player == dec.player
398 assert (game.other[2] as Animal) == .cat
399 assert dec.other[2] == Entity(Animal.cat)
400 assert (game.other[4] as time.Time).unix() == (dec.other[4] as time.Time).unix()
401}
402
403struct Foo3 {
404 name string
405 age int @[omitempty]
406}
407
408// fn test_omit_empty() {
409// foo := Foo3{'Bob', 0}
410// assert json.encode(foo) == '{"name":"Bob"}'
411// assert json.encode_pretty(foo) == '{
412// "name": "Bob"
413// }'
414// }
415
416struct Foo31 {
417 name string
418 age ?int
419}
420
421fn test_option_instead_of_omit_empty() {
422 foo := Foo31{
423 name: 'Bob'
424 }
425 assert json.encode(foo, prettify: true, indent_string: ' ') == '{
426 "name": "Bob"
427}'
428}
429
430struct Asdasd {
431 data GamePacketData
432}
433
434type GamePacketData = GPEquipItem | GPScale
435
436struct GPScale {
437 value f32
438}
439
440struct GPEquipItem {
441 name string
442}
443
444fn create_game_packet(data &GamePacketData) string {
445 return json.encode(data)
446}
447
448fn test_encode_sumtype_defined_ahead() {
449 ret := create_game_packet(&GamePacketData(GPScale{}))
450 assert ret == '{"value":0,"_type":"GPScale"}'
451}
452