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