cmdparse_test.go raw
1 package btcjson_test
2
3 import (
4 "encoding/json"
5 "math"
6 "reflect"
7 "testing"
8
9 "github.com/p9c/p9/pkg/btcjson"
10 )
11
12 // TestAssignField tests the assignField function handles supported combinations properly.
13 func TestAssignField(t *testing.T) {
14 t.Parallel()
15 tests := []struct {
16 name string
17 dest interface{}
18 src interface{}
19 expected interface{}
20 }{
21 {
22 name: "same types",
23 dest: int8(0),
24 src: int8(100),
25 expected: int8(100),
26 },
27 {
28 name: "same types - more source pointers",
29 dest: int8(0),
30 src: func() interface{} {
31 i := int8(100)
32 return &i
33 }(),
34 expected: int8(100),
35 },
36 {
37 name: "same types - more dest pointers",
38 dest: func() interface{} {
39 i := int8(0)
40 return &i
41 }(),
42 src: int8(100),
43 expected: int8(100),
44 },
45 {
46 name: "convertible types - more source pointers",
47 dest: int16(0),
48 src: func() interface{} {
49 i := int8(100)
50 return &i
51 }(),
52 expected: int16(100),
53 },
54 {
55 name: "convertible types - both pointers",
56 dest: func() interface{} {
57 i := int8(0)
58 return &i
59 }(),
60 src: func() interface{} {
61 i := int16(100)
62 return &i
63 }(),
64 expected: int8(100),
65 },
66 {
67 name: "convertible types - int16 -> int8",
68 dest: int8(0),
69 src: int16(100),
70 expected: int8(100),
71 },
72 {
73 name: "convertible types - int16 -> uint8",
74 dest: uint8(0),
75 src: int16(100),
76 expected: uint8(100),
77 },
78 {
79 name: "convertible types - uint16 -> int8",
80 dest: int8(0),
81 src: uint16(100),
82 expected: int8(100),
83 },
84 {
85 name: "convertible types - uint16 -> uint8",
86 dest: uint8(0),
87 src: uint16(100),
88 expected: uint8(100),
89 },
90 {
91 name: "convertible types - float32 -> float64",
92 dest: float64(0),
93 src: float32(1.5),
94 expected: float64(1.5),
95 },
96 {
97 name: "convertible types - float64 -> float32",
98 dest: float32(0),
99 src: float64(1.5),
100 expected: float32(1.5),
101 },
102 {
103 name: "convertible types - string -> bool",
104 dest: false,
105 src: "true",
106 expected: true,
107 },
108 {
109 name: "convertible types - string -> int8",
110 dest: int8(0),
111 src: "100",
112 expected: int8(100),
113 },
114 {
115 name: "convertible types - string -> uint8",
116 dest: uint8(0),
117 src: "100",
118 expected: uint8(100),
119 },
120 {
121 name: "convertible types - string -> float32",
122 dest: float32(0),
123 src: "1.5",
124 expected: float32(1.5),
125 },
126 {
127 name: "convertible types - typecase string -> string",
128 dest: "",
129 src: func() interface{} {
130 type foo string
131 return foo("foo")
132 }(),
133 expected: "foo",
134 },
135 {
136 name: "convertible types - string -> array",
137 dest: [2]string{},
138 src: `["test","test2"]`,
139 expected: [2]string{"test", "test2"},
140 },
141 {
142 name: "convertible types - string -> slice",
143 dest: []string{},
144 src: `["test","test2"]`,
145 expected: []string{"test", "test2"},
146 },
147 {
148 name: "convertible types - string -> struct",
149 dest: struct{ A int }{},
150 src: `{"A":100}`,
151 expected: struct{ A int }{100},
152 },
153 {
154 name: "convertible types - string -> map",
155 dest: map[string]float64{},
156 src: `{"1Address":1.5}`,
157 expected: map[string]float64{"1Address": 1.5},
158 },
159 }
160 t.Logf("Running %d tests", len(tests))
161 for i, test := range tests {
162 dst := reflect.New(reflect.TypeOf(test.dest)).Elem()
163 src := reflect.ValueOf(test.src)
164 e := btcjson.TstAssignField(1, "testField", dst, src)
165 if e != nil {
166 t.Errorf("Test #%d (%s) unexpected error: %v", i,
167 test.name, e,
168 )
169 continue
170 }
171 // Indirect through to the base types to ensure their values are the same.
172 for dst.Kind() == reflect.Ptr {
173 dst = dst.Elem()
174 }
175 if !reflect.DeepEqual(dst.Interface(), test.expected) {
176 t.Errorf("Test #%d (%s) unexpected value - got %v, "+
177 "want %v", i, test.name, dst.Interface(),
178 test.expected,
179 )
180 continue
181 }
182 }
183 }
184
185 // TestAssignFieldErrors tests the assignField function error paths.
186 func TestAssignFieldErrors(t *testing.T) {
187 t.Parallel()
188 tests := []struct {
189 name string
190 dest interface{}
191 src interface{}
192 e btcjson.GeneralError
193 }{
194 {
195 name: "general incompatible int -> string",
196 dest: string(rune(0)),
197 src: 0,
198 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
199 },
200 {
201 name: "overflow source int -> dest int",
202 dest: int8(0),
203 src: 128,
204 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
205 },
206 {
207 name: "overflow source int -> dest uint",
208 dest: uint8(0),
209 src: 256,
210 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
211 },
212 {
213 name: "int -> float",
214 dest: float32(0),
215 src: 256,
216 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
217 },
218 {
219 name: "overflow source uint64 -> dest int64",
220 dest: int64(0),
221 src: uint64(1 << 63),
222 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
223 },
224 {
225 name: "overflow source uint -> dest int",
226 dest: int8(0),
227 src: uint(128),
228 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
229 },
230 {
231 name: "overflow source uint -> dest uint",
232 dest: uint8(0),
233 src: uint(256),
234 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
235 },
236 {
237 name: "uint -> float",
238 dest: float32(0),
239 src: uint(256),
240 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
241 },
242 {
243 name: "float -> int",
244 dest: 0,
245 src: float32(1.0),
246 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
247 },
248 {
249 name: "overflow float64 -> float32",
250 dest: float32(0),
251 src: float64(math.MaxFloat64),
252 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
253 },
254 {
255 name: "invalid string -> bool",
256 dest: true,
257 src: "foo",
258 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
259 },
260 {
261 name: "invalid string -> int",
262 dest: int8(0),
263 src: "foo",
264 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
265 },
266 {
267 name: "overflow string -> int",
268 dest: int8(0),
269 src: "128",
270 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
271 },
272 {
273 name: "invalid string -> uint",
274 dest: uint8(0),
275 src: "foo",
276 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
277 },
278 {
279 name: "overflow string -> uint",
280 dest: uint8(0),
281 src: "256",
282 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
283 },
284 {
285 name: "invalid string -> float",
286 dest: float32(0),
287 src: "foo",
288 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
289 },
290 {
291 name: "overflow string -> float",
292 dest: float32(0),
293 src: "1.7976931348623157e+308",
294 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
295 },
296 {
297 name: "invalid string -> array",
298 dest: [3]int{},
299 src: "foo",
300 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
301 },
302 {
303 name: "invalid string -> slice",
304 dest: []int{},
305 src: "foo",
306 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
307 },
308 {
309 name: "invalid string -> struct",
310 dest: struct{ A int }{},
311 src: "foo",
312 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
313 },
314 {
315 name: "invalid string -> map",
316 dest: map[string]int{},
317 src: "foo",
318 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
319 },
320 }
321 t.Logf("Running %d tests", len(tests))
322 for i, test := range tests {
323 dst := reflect.New(reflect.TypeOf(test.dest)).Elem()
324 src := reflect.ValueOf(test.src)
325 e := btcjson.TstAssignField(1, "testField", dst, src)
326 if reflect.TypeOf(e) != reflect.TypeOf(test.e) {
327 t.Errorf("Test #%d (%s) wrong error - got %T (%[3]v), "+
328 "want %T", i, test.name, e, test.e,
329 )
330 continue
331 }
332 gotErrorCode := e.(btcjson.GeneralError).ErrorCode
333 if gotErrorCode != test.e.ErrorCode {
334 t.Errorf("Test #%d (%s) mismatched error code - got "+
335 "%v (%v), want %v", i, test.name, gotErrorCode,
336 e, test.e.ErrorCode,
337 )
338 continue
339 }
340 }
341 }
342
343 // TestNewCmdErrors ensures the error paths of NewCmd behave as expected.
344 func TestNewCmdErrors(t *testing.T) {
345 t.Parallel()
346 tests := []struct {
347 name string
348 method string
349 args []interface{}
350 e btcjson.GeneralError
351 }{
352 {
353 name: "unregistered command",
354 method: "boguscommand",
355 args: []interface{}{},
356 e: btcjson.GeneralError{ErrorCode: btcjson.ErrUnregisteredMethod},
357 },
358 {
359 name: "too few parameters to command with required + optional",
360 method: "getblock",
361 args: []interface{}{},
362 e: btcjson.GeneralError{ErrorCode: btcjson.ErrNumParams},
363 },
364 {
365 name: "too many parameters to command with no optional",
366 method: "getblockcount",
367 args: []interface{}{"123"},
368 e: btcjson.GeneralError{ErrorCode: btcjson.ErrNumParams},
369 },
370 {
371 name: "incorrect parameter type",
372 method: "getblock",
373 args: []interface{}{1},
374 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
375 },
376 }
377 t.Logf("Running %d tests", len(tests))
378 for i, test := range tests {
379 _, e := btcjson.NewCmd(test.method, test.args...)
380 if reflect.TypeOf(e) != reflect.TypeOf(test.e) {
381 t.Errorf("Test #%d (%s) wrong error - got %T (%v), "+
382 "want %T", i, test.name, e, e, test.e,
383 )
384 continue
385 }
386 gotErrorCode := e.(btcjson.GeneralError).ErrorCode
387 if gotErrorCode != test.e.ErrorCode {
388 t.Errorf("Test #%d (%s) mismatched error code - got "+
389 "%v (%v), want %v", i, test.name, gotErrorCode,
390 e, test.e.ErrorCode,
391 )
392 continue
393 }
394 }
395 }
396
397 // TestMarshalCmdErrors tests the error paths of the MarshalCmd function.
398 func TestMarshalCmdErrors(t *testing.T) {
399 t.Parallel()
400 tests := []struct {
401 name string
402 id interface{}
403 cmd interface{}
404 e btcjson.GeneralError
405 }{
406 {
407 name: "unregistered type",
408 id: 1,
409 cmd: (*int)(nil),
410 e: btcjson.GeneralError{ErrorCode: btcjson.ErrUnregisteredMethod},
411 },
412 {
413 name: "nil instance of registered type",
414 id: 1,
415 cmd: (*btcjson.GetBlockCmd)(nil),
416 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
417 },
418 {
419 name: "nil instance of registered type",
420 id: []int{0, 1},
421 cmd: &btcjson.GetBlockCountCmd{},
422 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
423 },
424 }
425 t.Logf("Running %d tests", len(tests))
426 for i, test := range tests {
427 _, e := btcjson.MarshalCmd(test.id, test.cmd)
428 if reflect.TypeOf(e) != reflect.TypeOf(test.e) {
429 t.Errorf("Test #%d (%s) wrong error - got %T (%v), "+
430 "want %T", i, test.name, e, e, test.e,
431 )
432 continue
433 }
434 gotErrorCode := e.(btcjson.GeneralError).ErrorCode
435 if gotErrorCode != test.e.ErrorCode {
436 t.Errorf("Test #%d (%s) mismatched error code - got "+
437 "%v (%v), want %v", i, test.name, gotErrorCode,
438 e, test.e.ErrorCode,
439 )
440 continue
441 }
442 }
443 }
444
445 // TestUnmarshalCmdErrors tests the error paths of the UnmarshalCmd function.
446 func TestUnmarshalCmdErrors(t *testing.T) {
447 t.Parallel()
448 tests := []struct {
449 name string
450 request btcjson.Request
451 e btcjson.GeneralError
452 }{
453 {
454 name: "unregistered type",
455 request: btcjson.Request{
456 Jsonrpc: "1.0",
457 Method: "bogusmethod",
458 Params: nil,
459 ID: nil,
460 },
461 e: btcjson.GeneralError{ErrorCode: btcjson.ErrUnregisteredMethod},
462 },
463 {
464 name: "incorrect number of netparams",
465 request: btcjson.Request{
466 Jsonrpc: "1.0",
467 Method: "getblockcount",
468 Params: []json.RawMessage{[]byte(`"bogusparam"`)},
469 ID: nil,
470 },
471 e: btcjson.GeneralError{ErrorCode: btcjson.ErrNumParams},
472 },
473 {
474 name: "invalid type for a parameter",
475 request: btcjson.Request{
476 Jsonrpc: "1.0",
477 Method: "getblock",
478 Params: []json.RawMessage{[]byte("1")},
479 ID: nil,
480 },
481 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
482 },
483 {
484 name: "invalid JSON for a parameter",
485 request: btcjson.Request{
486 Jsonrpc: "1.0",
487 Method: "getblock",
488 Params: []json.RawMessage{[]byte(`"1`)},
489 ID: nil,
490 },
491 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
492 },
493 }
494 t.Logf("Running %d tests", len(tests))
495 for i, test := range tests {
496 _, e := btcjson.UnmarshalCmd(&test.request)
497 if reflect.TypeOf(e) != reflect.TypeOf(test.e) {
498 t.Errorf("Test #%d (%s) wrong error - got %T (%v), "+
499 "want %T", i, test.name, e, e, test.e,
500 )
501 continue
502 }
503 gotErrorCode := e.(btcjson.GeneralError).ErrorCode
504 if gotErrorCode != test.e.ErrorCode {
505 t.Errorf("Test #%d (%s) mismatched error code - got "+
506 "%v (%v), want %v", i, test.name, gotErrorCode,
507 e, test.e.ErrorCode,
508 )
509 continue
510 }
511 }
512 }
513