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  }