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