v2 / vlib / x / json2 / tests / decode_struct_test.v
304 lines · 261 sloc · 6.92 KB · 6c46fdd6a72b46f10ec3d15f72c3c9153c1c4ff5
Raw
1import x.json2 as json
2import time
3import math
4
5const fixed_time = time.new(
6 year: 2022
7 month: 3
8 day: 11
9 hour: 13
10 minute: 54
11 second: 25
12)
13
14type StringAlias = string
15type BoolAlias = bool
16type IntAlias = int
17type TimeAlias = time.Time
18type StructAlias = StructType[int]
19type EnumAlias = Enumerates
20
21type SumTypes = StructType[string] | []SumTypes | []string | bool | string | time.Time | u32
22
23enum Enumerates {
24 a
25 b
26 c
27 d
28 e = 99
29 f
30}
31
32struct StructType[T] {
33mut:
34 val T
35}
36
37struct StructTypeOption[T] {
38mut:
39 val ?T
40}
41
42struct StructTypePointer[T] {
43mut:
44 val &T
45}
46
47fn test_types() {
48 assert json.decode[StructType[string]]('{"val": ""}')!.val == ''
49
50 assert json.decode[StructType[string]]('{"val": "2"}')!.val == '2'
51
52 assert json.decode[StructType[int]]('{"val": 2}')!.val == 2
53
54 assert json.decode[StructType[map[string]string]]('{"val": {"val1": "test"}}')!.val['val1'] == 'test'
55
56 assert json.decode[StructType[Enumerates]]('{"val": 0}')!.val == Enumerates.a
57 assert json.decode[StructType[Enumerates]]('{"val": 1}')!.val == Enumerates.b
58
59 assert json.decode[StructType[time.Time]]('{"val": "2022-03-11T13:54:25.000Z"}')!.val == fixed_time
60 assert json.decode[StructType[time.Time]]('{"val": "2022-03-11T13:54:25.000Z"}')!.val.unix() == fixed_time.unix()
61}
62
63fn test_option_types() {
64 if x := json.decode[StructTypeOption[string]]('{}')!.val {
65 assert false, 'Should return none'
66 } else {
67 assert err.msg() == ''
68 assert true
69 }
70
71 if x := json.decode[StructTypeOption[string]]('{"val": "2"}')!.val {
72 assert x == '2'
73 } else {
74 assert false, 'Should not return none'
75 }
76}
77
78// Test structure for basic number types
79struct JsonNumbers {
80 val_i8 i8
81 val_i16 i16
82 val_i32 i32
83 val_i64 i64
84 val_u8 u8
85 val_u16 u16
86 val_u32 u32
87 val_u64 u64
88 val_int int
89 val_f32 f32
90 val_f64 f64
91}
92
93// Test structure for float precision
94struct JsonFloats {
95 val_f32 f32
96 val_f64 f64
97}
98
99// Test basic functionality
100struct JsonU8 {
101 val1 u8
102 val2 u8
103}
104
105fn test_u8_values() {
106 x := JsonU8{
107 val1: 58
108 val2: 58
109 }
110
111 str := json.encode(x)
112 y := json.decode[JsonU8](str) or { panic(err) }
113 assert x == y
114 println('✓ Basic u8 test passed')
115}
116
117fn test_all_number_types() {
118 // Test normal range values
119 x := JsonNumbers{
120 val_i8: -100
121 val_i16: -1000
122 val_i32: -100000
123 val_i64: -1000000000
124 val_u8: 58
125 val_u16: 2000
126 val_u32: 200000
127 val_u64: 2000000000
128 val_int: 300000
129 val_f32: 3.14
130 val_f64: 2.71828
131 }
132
133 str := json.encode(x)
134 y := json.decode[JsonNumbers](str) or { panic('Decoding failed: ${err}') }
135
136 assert x.val_i8 == y.val_i8
137 assert x.val_i16 == y.val_i16
138 assert x.val_i32 == y.val_i32
139 assert x.val_i64 == y.val_i64
140 assert x.val_u8 == y.val_u8
141 assert x.val_u16 == y.val_u16
142 assert x.val_u32 == y.val_u32
143 assert x.val_u64 == y.val_u64
144 assert x.val_int == y.val_int
145 assert math.alike(x.val_f32, y.val_f32)
146 assert math.alike(x.val_f64, y.val_f64)
147
148 println('✓ All number types test passed')
149}
150
151fn test_number_boundaries() {
152 // Test minimum and maximum values for various types
153 test_cases := [
154 json.encode(JsonU8{ val1: 0, val2: 255 }),
155 json.encode(JsonU8{ val1: u8(128), val2: u8(255) }),
156 '{"min_i8": -128, "max_i8": 127}',
157 '{"min_u8": 0, "max_u8": 255}',
158 '{"zero": 0}',
159 ]!
160
161 for case in test_cases {
162 result := json.decode[JsonU8](case)!
163 assert result.val1 >= 0 && result.val1 <= 255
164 assert result.val2 >= 0 && result.val2 <= 255
165 }
166 println('✓ Boundary values test passed')
167}
168
169fn test_quoted_numbers_in_strict_mode() {
170 quoted_number_cases := [
171 '{"val1": "123", "val2": "45"}',
172 '{"val1": 123, "val2": "45"}',
173 '{"val1": "255", "val2": 0}',
174 ]!
175
176 for case in quoted_number_cases {
177 json.decode[JsonU8](case, strict: true) or { continue }
178 panic('Expected decoding to fail for quoted number in strict mode but succeeded: ${case}')
179 }
180 println('✓ Quoted numbers correctly rejected in strict mode test passed')
181}
182
183fn test_quoted_numbers_in_default_mode() {
184 assert json.decode[JsonU8]('{"val1": "123", "val2": "45"}')! == JsonU8{
185 val1: 123
186 val2: 45
187 }
188 assert json.decode[JsonU8]('{"val1": 123, "val2": "45"}')! == JsonU8{
189 val1: 123
190 val2: 45
191 }
192 println('✓ Quoted numbers accepted in default mode test passed')
193}
194
195fn test_error_conditions() {
196 // Test invalid inputs that should trigger errors
197 invalid_cases := [
198 '{"val1": "abc", "val2": 58}', // Non-numeric string
199 '{"val1": "", "val2": 58}', // Empty string
200 '{"val1": 300, "val2": 58}', // Out of u8 range (300 > 255)
201 '{"val1": -1, "val2": 58}', // Negative value for u8
202 '{"val1": 123.45, "val2": 58}', // Float for integer type
203 ]!
204
205 mut error_count := 0
206 for case in invalid_cases {
207 result := json.decode[JsonU8](case) or {
208 error_count++
209 continue // Expected failure, error handling works correctly
210 }
211 // If we reach here, it means decoding succeeded when it should have failed
212 panic('Expected decoding to fail but succeeded: ${case}')
213 }
214 assert error_count > 0 // Ensure we caught some errors
215 println('✓ Error handling test passed')
216}
217
218fn test_float_precision() {
219 // Test floating point precision and scientific notation
220 test_cases := [
221 JsonFloats{
222 val_f32: 3.14159265
223 val_f64: 2.718281828459045
224 },
225 JsonFloats{
226 val_f32: 1.0e10
227 val_f64: 1.0e-10
228 },
229 JsonFloats{
230 val_f32: -123.456
231 val_f64: -789.012
232 },
233 JsonFloats{
234 val_f32: 0.0
235 val_f64: 0.0
236 },
237 ]!
238
239 for x in test_cases {
240 str := json.encode(x)
241 y := json.decode[JsonFloats](str) or { panic('Float decoding failed: ${err}') }
242
243 // Use relative error for assertions
244 assert math.alike(y.val_f32, x.val_f32)
245 assert math.alike(y.val_f64, x.val_f64)
246 }
247 println('✓ Float precision test passed')
248}
249
250fn test_performance_large_scale() {
251 // Performance test: decode large numbers of values
252 mut total := u64(0)
253
254 for i in 0 .. 1000 {
255 x := JsonU8{
256 val1: u8(i % 256)
257 val2: u8((i * 7) % 256)
258 }
259 str := json.encode(x)
260 y := json.decode[JsonU8](str) or { panic('Performance test decoding failed: ${err}') }
261 total += y.val1 + y.val2
262 }
263
264 assert total > 0 // Ensure the loop executed
265 println('✓ Performance test passed')
266}
267
268fn test_special_float_values() {
269 // Test special float values
270
271 // Json does not support nan, +-inf yet
272 assert json.encode(math.nan()) == 'nan'
273 assert json.encode(math.inf(1)) == '+inf'
274 assert json.encode(math.inf(-1)) == '-inf'
275
276 println('✓ Special float values test passed')
277}
278
279fn test_json_format_consistency() {
280 // Test that encoding and decoding preserves data integrity
281 original := JsonNumbers{
282 val_i8: 100
283 val_i16: 1000
284 val_i32: 100000
285 val_i64: 1000000000
286 val_u8: 33
287 val_u16: 2000
288 val_u32: 200000
289 val_u64: 2000000000
290 val_int: 300000
291 val_f32: 3.14
292 val_f64: 2.71828
293 }
294
295 // Encode and decode multiple times
296 str1 := json.encode(original)
297 decoded1 := json.decode[JsonNumbers](str1) or { panic('First decode failed: ${err}') }
298 str2 := json.encode(decoded1)
299 decoded2 := json.decode[JsonNumbers](str2) or { panic('Second decode failed: ${err}') }
300
301 // All versions should be equivalent
302 assert decoded1 == decoded2
303 println('✓ JSON format consistency test passed')
304}
305