github.com/datastax/go-cassandra-native-protocol@v0.0.0-20220706104457-5e8aad05cf90/datacodec/bigint_test.go (about)

     1  // Copyright 2021 DataStax
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package datacodec
    16  
    17  import (
    18  	"fmt"
    19  	"math"
    20  	"math/big"
    21  	"strconv"
    22  	"testing"
    23  
    24  	"github.com/stretchr/testify/assert"
    25  
    26  	"github.com/datastax/go-cassandra-native-protocol/datatype"
    27  	"github.com/datastax/go-cassandra-native-protocol/primitive"
    28  )
    29  
    30  var (
    31  	bigIntZeroBytes     = encodeUint64(0x0000000000000000)
    32  	bigIntOneBytes      = encodeUint64(0x0000000000000001)
    33  	bigIntMinusOneBytes = encodeUint64(0xffffffffffffffff)
    34  	bigIntMaxInt64Bytes = encodeUint64(0x7fffffffffffffff)
    35  	bigIntMinInt64Bytes = encodeUint64(0x8000000000000000)
    36  )
    37  
    38  func Test_bigintCodec_DataType(t *testing.T) {
    39  	assert.Equal(t, datatype.Bigint, Bigint.DataType())
    40  	assert.Equal(t, datatype.Counter, Counter.DataType())
    41  }
    42  
    43  func Test_bigintCodec_Encode(t *testing.T) {
    44  	codecs := []Codec{Bigint, Counter}
    45  	for _, codec := range codecs {
    46  		t.Run(codec.DataType().AsCql(), func(t *testing.T) {
    47  			for _, version := range primitive.SupportedProtocolVersions() {
    48  				t.Run(version.String(), func(t *testing.T) {
    49  					tests := []struct {
    50  						name     string
    51  						source   interface{}
    52  						expected []byte
    53  						err      string
    54  					}{
    55  						{"nil", nil, nil, ""},
    56  						{"nil pointer", int64NilPtr(), nil, ""},
    57  						{"non nil", 1, bigIntOneBytes, ""},
    58  						{"conversion failed", uint64(math.MaxUint64), nil, fmt.Sprintf("cannot encode uint64 as CQL %v with %v: cannot convert from uint64 to int64: value out of range: 18446744073709551615", codec.DataType(), version)},
    59  					}
    60  					for _, tt := range tests {
    61  						t.Run(tt.name, func(t *testing.T) {
    62  							actual, err := codec.Encode(tt.source, version)
    63  							assert.Equal(t, tt.expected, actual)
    64  							assertErrorMessage(t, tt.err, err)
    65  						})
    66  					}
    67  				})
    68  			}
    69  		})
    70  	}
    71  }
    72  
    73  func Test_bigintCodec_Decode(t *testing.T) {
    74  	codecs := []Codec{Bigint, Counter}
    75  	for _, codec := range codecs {
    76  		t.Run(codec.DataType().AsCql(), func(t *testing.T) {
    77  			for _, version := range primitive.SupportedProtocolVersions() {
    78  				t.Run(version.String(), func(t *testing.T) {
    79  					tests := []struct {
    80  						name     string
    81  						source   []byte
    82  						dest     interface{}
    83  						expected interface{}
    84  						wasNull  bool
    85  						err      string
    86  					}{
    87  						{"null", nil, new(int64), new(int64), true, ""},
    88  						{"non null", bigIntOneBytes, new(int64), int64Ptr(1), false, ""},
    89  						{"non null interface", bigIntOneBytes, new(interface{}), interfacePtr(int64(1)), false, ""},
    90  						{"read failed", []byte{1}, new(int64), new(int64), false, fmt.Sprintf("cannot decode CQL %v as *int64 with %v: cannot read int64: expected 8 bytes but got: 1", codec.DataType(), version)},
    91  						{"conversion failed", bigIntOneBytes, new(float64), new(float64), false, fmt.Sprintf("cannot decode CQL %v as *float64 with %v: cannot convert from int64 to *float64: conversion not supported", codec.DataType(), version)},
    92  					}
    93  					for _, tt := range tests {
    94  						t.Run(tt.name, func(t *testing.T) {
    95  							wasNull, err := codec.Decode(tt.source, tt.dest, version)
    96  							assert.Equal(t, tt.expected, tt.dest)
    97  							assert.Equal(t, tt.wasNull, wasNull)
    98  							assertErrorMessage(t, tt.err, err)
    99  						})
   100  					}
   101  				})
   102  			}
   103  		})
   104  	}
   105  }
   106  
   107  func Test_convertToInt64(t *testing.T) {
   108  	tests := []struct {
   109  		name     string
   110  		input    interface{}
   111  		expected int64
   112  		wasNil   bool
   113  		err      string
   114  	}{
   115  		{"from int", int(1), 1, false, ""},
   116  		{"from *int non nil", intPtr(1), 1, false, ""},
   117  		{"from *int nil", intNilPtr(), 0, true, ""},
   118  		{"from int64", int64(1), 1, false, ""},
   119  		{"from *int64 non nil", int64Ptr(1), 1, false, ""},
   120  		{"from *int64 nil", int64NilPtr(), 0, true, ""},
   121  		{"from int32", int32(1), 1, false, ""},
   122  		{"from *int32 non nil", int32Ptr(1), 1, false, ""},
   123  		{"from *int32 nil", int32NilPtr(), 0, true, ""},
   124  		{"from int16", int16(1), 1, false, ""},
   125  		{"from *int16 non nil", int16Ptr(1), 1, false, ""},
   126  		{"from *int16 nil", int16NilPtr(), 0, true, ""},
   127  		{"from int8", int8(1), 1, false, ""},
   128  		{"from *int8 non nil", int8Ptr(1), 1, false, ""},
   129  		{"from *int8 nil", int8NilPtr(), 0, true, ""},
   130  		{"from uint", uint(1), 1, false, ""},
   131  		{"from *uint non nil", uintPtr(1), 1, false, ""},
   132  		{"from *uint nil", uintNilPtr(), 0, true, ""},
   133  		{"from uint64", uint64(1), 1, false, ""},
   134  		{"from uint64 out of range", uint64(math.MaxUint64), 0, false, "cannot convert from uint64 to int64: value out of range: 18446744073709551615"},
   135  		{"from *uint64 non nil", uint64Ptr(1), 1, false, ""},
   136  		{"from *uint64 out of range", uint64Ptr(math.MaxUint64), 0, false, "cannot convert from *uint64 to int64: value out of range: 18446744073709551615"},
   137  		{"from *uint64 nil", uint64NilPtr(), 0, true, ""},
   138  		{"from uint32", uint32(1), 1, false, ""},
   139  		{"from *uint32 non nil", uint32Ptr(1), 1, false, ""},
   140  		{"from *uint32 nil", uint32NilPtr(), 0, true, ""},
   141  		{"from uint16", uint16(1), 1, false, ""},
   142  		{"from *uint16 non nil", uint16Ptr(1), 1, false, ""},
   143  		{"from *uint16 nil", uint16NilPtr(), 0, true, ""},
   144  		{"from uint8", uint8(1), 1, false, ""},
   145  		{"from *uint8 non nil", uint8Ptr(1), 1, false, ""},
   146  		{"from *uint8 nil", uint8NilPtr(), 0, true, ""},
   147  		{"from *big.Int non nil", big.NewInt(1), 1, false, ""},
   148  		{"from *big.Int out of range", new(big.Int).SetUint64(math.MaxUint64), 0, false, "cannot convert from *big.Int to int64: value out of range: 18446744073709551615"},
   149  		{"from *big.Int nil", bigIntNilPtr(), 0, true, ""},
   150  		{"from string", "1", 1, false, ""},
   151  		{"from string malformed", "not a number", 0, false, "cannot convert from string to int64: cannot parse 'not a number'"},
   152  		{"from string out of range", new(big.Int).SetUint64(math.MaxUint64).String(), 0, false, "cannot convert from string to int64: cannot parse '18446744073709551615'"},
   153  		{"from *string non nil", stringPtr("1"), 1, false, ""},
   154  		{"from *string malformed", stringPtr("not a number"), 0, false, "cannot convert from *string to int64: cannot parse 'not a number'"},
   155  		{"from *string out of range", new(big.Int).SetUint64(math.MaxUint64).String(), 0, false, "cannot convert from string to int64: cannot parse '18446744073709551615'"},
   156  		{"from *string nil", stringNilPtr(), 0, true, ""},
   157  		{"from untyped nil", nil, 0, true, ""},
   158  		{"from unsupported value type", 42.0, 0, false, "cannot convert from float64 to int64: conversion not supported"},
   159  		{"from unsupported pointer type", float64Ptr(42.0), 0, false, "cannot convert from *float64 to int64: conversion not supported"},
   160  	}
   161  	if strconv.IntSize == 64 {
   162  		tests = append(tests, []struct {
   163  			name     string
   164  			input    interface{}
   165  			expected int64
   166  			wasNil   bool
   167  			err      string
   168  		}{
   169  			{"from uint out of range", uint(math.MaxUint64), 0, false, "cannot convert from uint to int64: value out of range: 18446744073709551615"},
   170  			{"from *uint out of range", uintPtr(math.MaxUint64), 0, false, "cannot convert from *uint to int64: value out of range: 18446744073709551615"},
   171  		}...)
   172  	}
   173  	for _, tt := range tests {
   174  		t.Run(tt.name, func(t *testing.T) {
   175  			dest, wasNil, err := convertToInt64(tt.input)
   176  			assert.Equal(t, tt.expected, dest)
   177  			assert.Equal(t, tt.wasNil, wasNil)
   178  			assertErrorMessage(t, tt.err, err)
   179  		})
   180  	}
   181  }
   182  
   183  func Test_convertFromInt64(t *testing.T) {
   184  	tests := []struct {
   185  		name     string
   186  		val      int64
   187  		wasNull  bool
   188  		dest     interface{}
   189  		expected interface{}
   190  		err      string
   191  	}{
   192  		{"to *interface{} nil dest", 1, false, interfaceNilPtr(), interfaceNilPtr(), "cannot convert from int64 to *interface {}: destination is nil"},
   193  		{"to *interface{} nil source", 0, true, new(interface{}), new(interface{}), ""},
   194  		{"to *interface{} non nil", 1, false, new(interface{}), interfacePtr(int64(1)), ""},
   195  		{"to *int nil dest", 1, false, intNilPtr(), intNilPtr(), "cannot convert from int64 to *int: destination is nil"},
   196  		{"to *int nil source", 0, true, new(int), intPtr(0), ""},
   197  		{"to *int non nil", 1, false, new(int), intPtr(1), ""},
   198  		{"to *int64 nil dest", 1, false, int64NilPtr(), int64NilPtr(), "cannot convert from int64 to *int64: destination is nil"},
   199  		{"to *int64 nil source", 0, true, new(int64), int64Ptr(0), ""},
   200  		{"to *int64 non nil", 1, false, new(int64), int64Ptr(1), ""},
   201  		{"to *int32 nil dest", 1, false, int32NilPtr(), int32NilPtr(), "cannot convert from int64 to *int32: destination is nil"},
   202  		{"to *int32 nil source", 0, true, new(int32), int32Ptr(0), ""},
   203  		{"to *int32 non nil", 1, false, new(int32), int32Ptr(1), ""},
   204  		{"to *int32 out of range pos", math.MaxInt32 + 1, false, new(int32), int32Ptr(0), "cannot convert from int64 to *int32: value out of range: 2147483648"},
   205  		{"to *int32 out of range neg", math.MinInt32 - 1, false, new(int32), int32Ptr(0), "cannot convert from int64 to *int32: value out of range: -2147483649"},
   206  		{"to *int16 nil dest", 1, false, int16NilPtr(), int16NilPtr(), "cannot convert from int64 to *int16: destination is nil"},
   207  		{"to *int16 nil source", 0, true, new(int16), int16Ptr(0), ""},
   208  		{"to *int16 non nil", 1, false, new(int16), int16Ptr(1), ""},
   209  		{"to *int16 out of range pos", math.MaxInt16 + 1, false, new(int16), int16Ptr(0), "cannot convert from int64 to *int16: value out of range: 32768"},
   210  		{"to *int16 out of range neg", math.MinInt16 - 1, false, new(int16), int16Ptr(0), "cannot convert from int64 to *int16: value out of range: -32769"},
   211  		{"to *int8 nil dest", 1, false, int8NilPtr(), int8NilPtr(), "cannot convert from int64 to *int8: destination is nil"},
   212  		{"to *int8 nil source", 0, true, new(int8), int8Ptr(0), ""},
   213  		{"to *int8 non nil", 1, false, new(int8), int8Ptr(1), ""},
   214  		{"to *int8 out of range pos", math.MaxInt8 + 1, false, new(int8), int8Ptr(0), "cannot convert from int64 to *int8: value out of range: 128"},
   215  		{"to *int8 out of range neg", math.MinInt8 - 1, false, new(int8), int8Ptr(0), "cannot convert from int64 to *int8: value out of range: -129"},
   216  		{"to *uint nil dest", 1, false, uintNilPtr(), uintNilPtr(), "cannot convert from int64 to *uint: destination is nil"},
   217  		{"to *uint nil source", 0, true, new(uint), uintPtr(0), ""},
   218  		{"to *uint non nil", 1, false, new(uint), uintPtr(1), ""},
   219  		{"to *uint out of range neg", -1, false, new(uint), new(uint), "cannot convert from int64 to *uint: value out of range: -1"},
   220  		{"to *uint64 nil dest", 1, false, uint64NilPtr(), uint64NilPtr(), "cannot convert from int64 to *uint64: destination is nil"},
   221  		{"to *uint64 nil source", 0, true, new(uint64), uint64Ptr(0), ""},
   222  		{"to *uint64 non nil", 1, false, new(uint64), uint64Ptr(1), ""},
   223  		{"to *uint64 out of range neg", -1, false, new(uint64), uint64Ptr(0), "cannot convert from int64 to *uint64: value out of range: -1"},
   224  		{"to *uint32 nil dest", 1, false, uint32NilPtr(), uint32NilPtr(), "cannot convert from int64 to *uint32: destination is nil"},
   225  		{"to *uint32 nil source", 0, true, new(uint32), uint32Ptr(0), ""},
   226  		{"to *uint32 non nil", 1, false, new(uint32), uint32Ptr(1), ""},
   227  		{"to *uint32 out of range pos", math.MaxUint32 + 1, false, new(uint32), uint32Ptr(0), "cannot convert from int64 to *uint32: value out of range: 4294967296"},
   228  		{"to *uint32 out of range neg", -1, false, new(uint32), uint32Ptr(0), "cannot convert from int64 to *uint32: value out of range: -1"},
   229  		{"to *uint16 nil dest", 1, false, uint16NilPtr(), uint16NilPtr(), "cannot convert from int64 to *uint16: destination is nil"},
   230  		{"to *uint16 nil source", 0, true, new(uint16), uint16Ptr(0), ""},
   231  		{"to *uint16 non nil", 1, false, new(uint16), uint16Ptr(1), ""},
   232  		{"to *uint16 out of range pos", math.MaxUint16 + 1, false, new(uint16), uint16Ptr(0), "cannot convert from int64 to *uint16: value out of range: 65536"},
   233  		{"to *uint16 out of range neg", -1, false, new(uint16), uint16Ptr(0), "cannot convert from int64 to *uint16: value out of range: -1"},
   234  		{"to *uint8 nil dest", 1, false, uint8NilPtr(), uint8NilPtr(), "cannot convert from int64 to *uint8: destination is nil"},
   235  		{"to *uint8 nil source", 0, true, new(uint8), uint8Ptr(0), ""},
   236  		{"to *uint8 non nil", 1, false, new(uint8), uint8Ptr(1), ""},
   237  		{"to *uint8 out of range pos", math.MaxUint8 + 1, false, new(uint8), uint8Ptr(0), "cannot convert from int64 to *uint8: value out of range: 256"},
   238  		{"to *uint8 out of range neg", -1, false, new(uint8), uint8Ptr(0), "cannot convert from int64 to *uint8: value out of range: -1"},
   239  		{"to *big.Int nil dest", 1, false, bigIntNilPtr(), bigIntNilPtr(), "cannot convert from int64 to *big.Int: destination is nil"},
   240  		{"to *big.Int nil source", 0, true, new(big.Int), new(big.Int), ""},
   241  		{"to *big.Int non nil", 1, false, big.NewInt(1), big.NewInt(1), ""},
   242  		{"to *string nil dest", 1, false, stringNilPtr(), stringNilPtr(), "cannot convert from int64 to *string: destination is nil"},
   243  		{"to *string nil source", 0, true, new(string), new(string), ""},
   244  		{"to *string non nil", 1, false, new(string), stringPtr("1"), ""},
   245  		{"to untyped nil", 1, false, nil, nil, "cannot convert from int64 to <nil>: destination is nil"},
   246  		{"to non pointer", 1, false, int64(0), int64(0), "cannot convert from int64 to int64: destination is not pointer"},
   247  		{"to unsupported pointer type", 1, false, new(float64), new(float64), "cannot convert from int64 to *float64: conversion not supported"},
   248  	}
   249  	if strconv.IntSize == 32 {
   250  		tests = append(tests, []struct {
   251  			name     string
   252  			val      int64
   253  			wasNull  bool
   254  			dest     interface{}
   255  			expected interface{}
   256  			err      string
   257  		}{
   258  			{"to *int out of range pos", int64(math.MaxInt32) + 1, false, new(int), new(int), "cannot convert from *int to int64: value out of range: 2147483648"},
   259  			{"to *int out of range neg", int64(math.MinInt32) - 1, false, new(int), new(int), "cannot convert from *int to int64: value out of range: -2147483649"},
   260  			{"to *uint out of range pos", int64(math.MaxUint32) + 1, false, new(uint), new(int), "cannot convert from *uint to int64: value out of range: 4294967296"},
   261  		}...)
   262  	}
   263  	for _, tt := range tests {
   264  		t.Run(tt.name, func(t *testing.T) {
   265  			err := convertFromInt64(tt.val, tt.wasNull, tt.dest)
   266  			assert.Equal(t, tt.expected, tt.dest)
   267  			assertErrorMessage(t, tt.err, err)
   268  		})
   269  	}
   270  }
   271  
   272  func Test_writeInt64(t *testing.T) {
   273  	tests := []struct {
   274  		name     string
   275  		val      int64
   276  		expected []byte
   277  	}{
   278  		{"zero", 0, bigIntZeroBytes},
   279  		{"positive", 1, bigIntOneBytes},
   280  		{"negative", -1, bigIntMinusOneBytes},
   281  		{"max", math.MaxInt64, bigIntMaxInt64Bytes},
   282  		{"min", math.MinInt64, bigIntMinInt64Bytes},
   283  	}
   284  	for _, tt := range tests {
   285  		t.Run(tt.name, func(t *testing.T) {
   286  			actual := writeInt64(tt.val)
   287  			assert.Equal(t, tt.expected, actual)
   288  		})
   289  	}
   290  }
   291  
   292  func Test_readInt64(t *testing.T) {
   293  	tests := []struct {
   294  		name     string
   295  		source   []byte
   296  		expected int64
   297  		wasNull  bool
   298  		err      string
   299  	}{
   300  		{"nil", nil, 0, true, ""},
   301  		{"empty", []byte{}, 0, true, ""},
   302  		{"wrong length", []byte{1}, 0, false, "cannot read int64: expected 8 bytes but got: 1"},
   303  		{"zero", bigIntZeroBytes, 0, false, ""},
   304  		{"positive", bigIntOneBytes, 1, false, ""},
   305  		{"negative", bigIntMinusOneBytes, -1, false, ""},
   306  		{"max", bigIntMaxInt64Bytes, math.MaxInt64, false, ""},
   307  		{"min", bigIntMinInt64Bytes, math.MinInt64, false, ""},
   308  	}
   309  	for _, tt := range tests {
   310  		t.Run(tt.name, func(t *testing.T) {
   311  			actual, wasNull, err := readInt64(tt.source)
   312  			assert.Equal(t, tt.expected, actual)
   313  			assert.Equal(t, tt.wasNull, wasNull)
   314  			assertErrorMessage(t, tt.err, err)
   315  		})
   316  	}
   317  }