register_test.go raw
1 package btcjson_test
2
3 import (
4 "reflect"
5 "sort"
6 "testing"
7
8 "github.com/p9c/p9/pkg/btcjson"
9 )
10
11 // TestUsageFlagStringer tests the stringized output for the UsageFlag type.
12 func TestUsageFlagStringer(t *testing.T) {
13 t.Parallel()
14 tests := []struct {
15 in btcjson.UsageFlag
16 want string
17 }{
18 {0, "0x0"},
19 {btcjson.UFWalletOnly, "UFWalletOnly"},
20 {btcjson.UFWebsocketOnly, "UFWebsocketOnly"},
21 {btcjson.UFNotification, "UFNotification"},
22 {btcjson.UFWalletOnly | btcjson.UFWebsocketOnly,
23 "UFWalletOnly|UFWebsocketOnly",
24 },
25 {btcjson.UFWalletOnly | btcjson.UFWebsocketOnly | (1 << 31),
26 "UFWalletOnly|UFWebsocketOnly|0x80000000",
27 },
28 }
29 // Detect additional usage flags that don't have the stringer added.
30 numUsageFlags := 0
31 highestUsageFlagBit := btcjson.TstHighestUsageFlagBit
32 for highestUsageFlagBit > 1 {
33 numUsageFlags++
34 highestUsageFlagBit >>= 1
35 }
36 if len(tests)-3 != numUsageFlags {
37 t.Errorf("It appears a usage flag was added without adding " +
38 "an associated stringer test",
39 )
40 }
41 t.Logf("Running %d tests", len(tests))
42 for i, test := range tests {
43 result := test.in.String()
44 if result != test.want {
45 t.Errorf("String #%d\n got: %s want: %s", i, result,
46 test.want,
47 )
48 continue
49 }
50 }
51 }
52
53 // TestRegisterCmdErrors ensures the RegisterCmd function returns the expected error when provided with invalid types.
54 func TestRegisterCmdErrors(t *testing.T) {
55 t.Parallel()
56 tests := []struct {
57 name string
58 method string
59 cmdFunc func() interface{}
60 flags btcjson.UsageFlag
61 e btcjson.GeneralError
62 }{
63 {
64 name: "duplicate method",
65 method: "getblock",
66 cmdFunc: func() interface{} {
67 return struct{}{}
68 },
69 e: btcjson.GeneralError{ErrorCode: btcjson.ErrDuplicateMethod},
70 },
71 {
72 name: "invalid usage flags",
73 method: "registertestcmd",
74 cmdFunc: func() interface{} {
75 return 0
76 },
77 flags: btcjson.TstHighestUsageFlagBit,
78 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidUsageFlags},
79 },
80 {
81 name: "invalid type",
82 method: "registertestcmd",
83 cmdFunc: func() interface{} {
84 return 0
85 },
86 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
87 },
88 {
89 name: "invalid type 2",
90 method: "registertestcmd",
91 cmdFunc: func() interface{} {
92 return &[]string{}
93 },
94 e: btcjson.GeneralError{ErrorCode: btcjson.ErrInvalidType},
95 },
96 {
97 name: "embedded field",
98 method: "registertestcmd",
99 cmdFunc: func() interface{} {
100 type test struct{ int }
101 return (*test)(nil)
102 },
103 e: btcjson.GeneralError{ErrorCode: btcjson.ErrEmbeddedType},
104 },
105 {
106 name: "unexported field",
107 method: "registertestcmd",
108 cmdFunc: func() interface{} {
109 type test struct{ a int }
110 return (*test)(nil)
111 },
112 e: btcjson.GeneralError{ErrorCode: btcjson.ErrUnexportedField},
113 },
114 {
115 name: "unsupported field type 1",
116 method: "registertestcmd",
117 cmdFunc: func() interface{} {
118 type test struct{ A **int }
119 return (*test)(nil)
120 },
121 e: btcjson.GeneralError{ErrorCode: btcjson.ErrUnsupportedFieldType},
122 },
123 {
124 name: "unsupported field type 2",
125 method: "registertestcmd",
126 cmdFunc: func() interface{} {
127 type test struct{ A chan int }
128 return (*test)(nil)
129 },
130 e: btcjson.GeneralError{ErrorCode: btcjson.ErrUnsupportedFieldType},
131 },
132 {
133 name: "unsupported field type 3",
134 method: "registertestcmd",
135 cmdFunc: func() interface{} {
136 type test struct{ A complex64 }
137 return (*test)(nil)
138 },
139 e: btcjson.GeneralError{ErrorCode: btcjson.ErrUnsupportedFieldType},
140 },
141 {
142 name: "unsupported field type 4",
143 method: "registertestcmd",
144 cmdFunc: func() interface{} {
145 type test struct{ A complex128 }
146 return (*test)(nil)
147 },
148 e: btcjson.GeneralError{ErrorCode: btcjson.ErrUnsupportedFieldType},
149 },
150 {
151 name: "unsupported field type 5",
152 method: "registertestcmd",
153 cmdFunc: func() interface{} {
154 type test struct{ A func() }
155 return (*test)(nil)
156 },
157 e: btcjson.GeneralError{ErrorCode: btcjson.ErrUnsupportedFieldType},
158 },
159 {
160 name: "unsupported field type 6",
161 method: "registertestcmd",
162 cmdFunc: func() interface{} {
163 type test struct{ A interface{} }
164 return (*test)(nil)
165 },
166 e: btcjson.GeneralError{ErrorCode: btcjson.ErrUnsupportedFieldType},
167 },
168 {
169 name: "required after optional",
170 method: "registertestcmd",
171 cmdFunc: func() interface{} {
172 type test struct {
173 A *int
174 B int
175 }
176 return (*test)(nil)
177 },
178 e: btcjson.GeneralError{ErrorCode: btcjson.ErrNonOptionalField},
179 },
180 {
181 name: "non-optional with default",
182 method: "registertestcmd",
183 cmdFunc: func() interface{} {
184 type test struct {
185 A int `jsonrpcdefault:"1"`
186 }
187 return (*test)(nil)
188 },
189 e: btcjson.GeneralError{ErrorCode: btcjson.ErrNonOptionalDefault},
190 },
191 {
192 name: "mismatched default",
193 method: "registertestcmd",
194 cmdFunc: func() interface{} {
195 type test struct {
196 A *int `jsonrpcdefault:"1.7"`
197 }
198 return (*test)(nil)
199 },
200 e: btcjson.GeneralError{ErrorCode: btcjson.ErrMismatchedDefault},
201 },
202 }
203 t.Logf("Running %d tests", len(tests))
204 for i, test := range tests {
205 e := btcjson.RegisterCmd(test.method, test.cmdFunc(),
206 test.flags,
207 )
208 if reflect.TypeOf(e) != reflect.TypeOf(test.e) {
209 t.Errorf("Test #%d (%s) wrong error - got %T, "+
210 "want %T", i, test.name, e, test.e,
211 )
212 continue
213 }
214 gotErrorCode := e.(btcjson.GeneralError).ErrorCode
215 if gotErrorCode != test.e.ErrorCode {
216 t.Errorf("Test #%d (%s) mismatched error code - got "+
217 "%v, want %v", i, test.name, gotErrorCode,
218 test.e.ErrorCode,
219 )
220 continue
221 }
222 }
223 }
224
225 // TestMustRegisterCmdPanic ensures the MustRegisterCmd function panics when used to register an invalid type.
226 func TestMustRegisterCmdPanic(t *testing.T) {
227 t.Parallel()
228 // Setup a defer to catch the expected panic to ensure it actually panicked.
229 defer func() {
230 if e := recover(); e == nil {
231 t.Error("MustRegisterCmd did not panic as expected")
232 }
233 }()
234 // Intentionally try to register an invalid type to force a panic.
235 btcjson.MustRegisterCmd("panicme", 0, 0)
236 }
237
238 // TestRegisteredCmdMethods tests the RegisteredCmdMethods function ensure it works as expected.
239 func TestRegisteredCmdMethods(t *testing.T) {
240 t.Parallel()
241 // Ensure the registered methods are returned.
242 methods := btcjson.RegisteredCmdMethods()
243 if len(methods) == 0 {
244 t.Fatal("RegisteredCmdMethods: no methods")
245 }
246 // Ensure the returned methods are sorted.
247 sortedMethods := make([]string, len(methods))
248 copy(sortedMethods, methods)
249 sort.Strings(sortedMethods)
250 if !reflect.DeepEqual(sortedMethods, methods) {
251 t.Fatal("RegisteredCmdMethods: methods are not sorted")
252 }
253 }
254