github.com/datastax/go-cassandra-native-protocol@v0.0.0-20220706104457-5e8aad05cf90/datacodec/varint_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  	"testing"
    22  
    23  	"github.com/stretchr/testify/assert"
    24  
    25  	"github.com/datastax/go-cassandra-native-protocol/datatype"
    26  	"github.com/datastax/go-cassandra-native-protocol/primitive"
    27  )
    28  
    29  var (
    30  	hugeBigIntPos = new(big.Int).Add(new(big.Int).SetUint64(math.MaxUint64), oneBigInt)
    31  	hugeBigIntNeg = new(big.Int).Sub(big.NewInt(math.MinInt64), oneBigInt)
    32  )
    33  
    34  func Test_varintCodec_DataType(t *testing.T) {
    35  	assert.Equal(t, datatype.Varint, Varint.DataType())
    36  }
    37  
    38  func Test_varintCodec_Encode(t *testing.T) {
    39  	for _, version := range primitive.SupportedProtocolVersions() {
    40  		t.Run(version.String(), func(t *testing.T) {
    41  			tests := []struct {
    42  				name     string
    43  				source   interface{}
    44  				expected []byte
    45  				err      string
    46  			}{
    47  				{"nil", nil, nil, ""},
    48  				{"nil pointer", bigIntNilPtr(), nil, ""},
    49  				{"non nil", oneBigInt, []byte{1}, ""},
    50  				{"conversion failed", float64(0), nil, fmt.Sprintf("cannot encode float64 as CQL varint with %v: cannot convert from float64 to *big.Int: conversion not supported", version)},
    51  			}
    52  			for _, tt := range tests {
    53  				t.Run(tt.name, func(t *testing.T) {
    54  					actual, err := Varint.Encode(tt.source, version)
    55  					assert.Equal(t, tt.expected, actual)
    56  					assertErrorMessage(t, tt.err, err)
    57  				})
    58  			}
    59  		})
    60  	}
    61  }
    62  
    63  func Test_varintCodec_Decode(t *testing.T) {
    64  	for _, version := range primitive.SupportedProtocolVersions() {
    65  		t.Run(version.String(), func(t *testing.T) {
    66  			tests := []struct {
    67  				name     string
    68  				source   []byte
    69  				dest     interface{}
    70  				expected interface{}
    71  				wasNull  bool
    72  				err      string
    73  			}{
    74  				{"null", nil, new(big.Int), new(big.Int), true, ""},
    75  				{"non null", []byte{1}, new(big.Int), oneBigInt, false, ""},
    76  				{"non null interface", []byte{1}, new(interface{}), interfacePtr(oneBigInt), false, ""},
    77  				{"conversion failed", []byte{1}, new(float64), new(float64), false, fmt.Sprintf("cannot decode CQL varint as *float64 with %v: cannot convert from *big.Int to *float64: conversion not supported", version)},
    78  			}
    79  			for _, tt := range tests {
    80  				t.Run(tt.name, func(t *testing.T) {
    81  					wasNull, err := Varint.Decode(tt.source, tt.dest, version)
    82  					assert.Equal(t, tt.expected, tt.dest)
    83  					assert.Equal(t, tt.wasNull, wasNull)
    84  					assertErrorMessage(t, tt.err, err)
    85  				})
    86  			}
    87  		})
    88  	}
    89  }
    90  
    91  func Test_convertToBigInt(t *testing.T) {
    92  	tests := []struct {
    93  		name     string
    94  		input    interface{}
    95  		expected *big.Int
    96  		err      string
    97  	}{
    98  		{"from *big.Int non nil", oneBigInt, oneBigInt, ""},
    99  		{"from *big.Int nil", bigIntNilPtr(), nil, ""},
   100  		{"from int", int(1), oneBigInt, ""},
   101  		{"from *int non nil", intPtr(1), oneBigInt, ""},
   102  		{"from *int nil", intNilPtr(), nil, ""},
   103  		{"from int64", int64(1), oneBigInt, ""},
   104  		{"from *int64 non nil", int64Ptr(1), oneBigInt, ""},
   105  		{"from *int64 nil", int64NilPtr(), nil, ""},
   106  		{"from int32", int32(1), oneBigInt, ""},
   107  		{"from *int32 non nil", int32Ptr(1), oneBigInt, ""},
   108  		{"from *int32 nil", int32NilPtr(), nil, ""},
   109  		{"from int16", int16(1), oneBigInt, ""},
   110  		{"from *int16 non nil", int16Ptr(1), oneBigInt, ""},
   111  		{"from *int16 nil", int16NilPtr(), nil, ""},
   112  		{"from int8", int8(1), oneBigInt, ""},
   113  		{"from *int8 non nil", int8Ptr(1), oneBigInt, ""},
   114  		{"from *int8 nil", int8NilPtr(), nil, ""},
   115  		{"from uint", uint(1), oneBigInt, ""},
   116  		{"from *uint non nil", uintPtr(1), oneBigInt, ""},
   117  		{"from *uint nil", uintNilPtr(), nil, ""},
   118  		{"from uint64", uint64(1), oneBigInt, ""},
   119  		{"from *uint64 non nil", uint64Ptr(1), oneBigInt, ""},
   120  		{"from *uint64 nil", uint64NilPtr(), nil, ""},
   121  		{"from uint32", uint32(1), oneBigInt, ""},
   122  		{"from *uint32 non nil", uint32Ptr(1), oneBigInt, ""},
   123  		{"from *uint32 nil", uint32NilPtr(), nil, ""},
   124  		{"from uint16", uint16(1), oneBigInt, ""},
   125  		{"from *uint16 non nil", uint16Ptr(1), oneBigInt, ""},
   126  		{"from *uint16 nil", uint16NilPtr(), nil, ""},
   127  		{"from uint8", uint8(1), oneBigInt, ""},
   128  		{"from *uint8 non nil", uint8Ptr(1), oneBigInt, ""},
   129  		{"from *uint8 nil", uint8NilPtr(), nil, ""},
   130  		{"from string", "1", oneBigInt, ""},
   131  		{"from string malformed", "not a number", nil, "cannot convert from string to *big.Int: cannot parse 'not a number'"},
   132  		{"from *string non nil", stringPtr("1"), oneBigInt, ""},
   133  		{"from *string malformed", stringPtr("not a number"), nil, "cannot convert from *string to *big.Int: cannot parse 'not a number'"},
   134  		{"from *string nil", stringNilPtr(), nil, ""},
   135  		{"from untyped nil", nil, nil, ""},
   136  		{"from unsupported value type", 42.0, nil, "cannot convert from float64 to *big.Int: conversion not supported"},
   137  		{"from unsupported pointer type", float64Ptr(42.0), nil, "cannot convert from *float64 to *big.Int: conversion not supported"},
   138  	}
   139  	for _, tt := range tests {
   140  		t.Run(tt.name, func(t *testing.T) {
   141  			dest, err := convertToBigInt(tt.input)
   142  			assert.Equal(t, tt.expected, dest)
   143  			assertErrorMessage(t, tt.err, err)
   144  		})
   145  	}
   146  }
   147  
   148  func Test_convertFromBigInt(t *testing.T) {
   149  	tests := []struct {
   150  		name     string
   151  		val      *big.Int
   152  		wasNull  bool
   153  		dest     interface{}
   154  		expected interface{}
   155  		err      string
   156  	}{
   157  		{"to *interface{} nil dest", oneBigInt, false, interfaceNilPtr(), interfaceNilPtr(), "cannot convert from *big.Int to *interface {}: destination is nil"},
   158  		{"to *interface{} nil source", nil, true, new(interface{}), new(interface{}), ""},
   159  		{"to *interface{} non nil", oneBigInt, false, new(interface{}), interfacePtr(oneBigInt), ""},
   160  		{"to *big.Int nil dest", oneBigInt, false, bigIntNilPtr(), bigIntNilPtr(), "cannot convert from *big.Int to *big.Int: destination is nil"},
   161  		{"to *big.Int nil source", nil, true, new(big.Int), new(big.Int), ""},
   162  		{"to *big.Int non nil", oneBigInt, false, big.NewInt(1), big.NewInt(1), ""},
   163  		{"to *int nil dest", oneBigInt, false, intNilPtr(), intNilPtr(), "cannot convert from *big.Int to *int: destination is nil"},
   164  		{"to *int nil source", nil, true, new(int), intPtr(0), ""},
   165  		{"to *int non nil", oneBigInt, false, new(int), intPtr(1), ""},
   166  		{"to *int out of range pos", hugeBigIntPos, false, new(int), new(int), "cannot convert from *big.Int to *int: value out of range: 18446744073709551616"},
   167  		{"to *int out of range neg", hugeBigIntNeg, false, new(int), new(int), "cannot convert from *big.Int to *int: value out of range: -9223372036854775809"},
   168  		{"to *int64 nil dest", oneBigInt, false, int64NilPtr(), int64NilPtr(), "cannot convert from *big.Int to *int64: destination is nil"},
   169  		{"to *int64 nil source", nil, true, new(int64), int64Ptr(0), ""},
   170  		{"to *int64 non nil", oneBigInt, false, new(int64), int64Ptr(1), ""},
   171  		{"to *int64 out of range pos", hugeBigIntPos, false, new(int64), new(int64), "cannot convert from *big.Int to *int64: value out of range: 18446744073709551616"},
   172  		{"to *int64 out of range neg", hugeBigIntNeg, false, new(int64), new(int64), "cannot convert from *big.Int to *int64: value out of range: -9223372036854775809"},
   173  		{"to *int32 nil dest", oneBigInt, false, int32NilPtr(), int32NilPtr(), "cannot convert from *big.Int to *int32: destination is nil"},
   174  		{"to *int32 nil source", nil, true, new(int32), int32Ptr(0), ""},
   175  		{"to *int32 non nil", oneBigInt, false, new(int32), int32Ptr(1), ""},
   176  		{"to *int32 out of range pos", hugeBigIntPos, false, new(int32), new(int32), "cannot convert from *big.Int to *int32: value out of range: 18446744073709551616"},
   177  		{"to *int32 out of range neg", hugeBigIntNeg, false, new(int32), new(int32), "cannot convert from *big.Int to *int32: value out of range: -9223372036854775809"},
   178  		{"to *int16 nil dest", oneBigInt, false, int16NilPtr(), int16NilPtr(), "cannot convert from *big.Int to *int16: destination is nil"},
   179  		{"to *int16 nil source", nil, true, new(int16), int16Ptr(0), ""},
   180  		{"to *int16 non nil", oneBigInt, false, new(int16), int16Ptr(1), ""},
   181  		{"to *int16 out of range pos", hugeBigIntPos, false, new(int16), new(int16), "cannot convert from *big.Int to *int16: value out of range: 18446744073709551616"},
   182  		{"to *int16 out of range neg", hugeBigIntNeg, false, new(int16), new(int16), "cannot convert from *big.Int to *int16: value out of range: -9223372036854775809"},
   183  		{"to *int8 nil dest", oneBigInt, false, int8NilPtr(), int8NilPtr(), "cannot convert from *big.Int to *int8: destination is nil"},
   184  		{"to *int8 nil source", nil, true, new(int8), int8Ptr(0), ""},
   185  		{"to *int8 non nil", oneBigInt, false, new(int8), int8Ptr(1), ""},
   186  		{"to *int8 out of range pos", hugeBigIntPos, false, new(int8), new(int8), "cannot convert from *big.Int to *int8: value out of range: 18446744073709551616"},
   187  		{"to *int8 out of range neg", hugeBigIntNeg, false, new(int8), new(int8), "cannot convert from *big.Int to *int8: value out of range: -9223372036854775809"},
   188  		{"to *uint nil dest", oneBigInt, false, uintNilPtr(), uintNilPtr(), "cannot convert from *big.Int to *uint: destination is nil"},
   189  		{"to *uint nil source", nil, true, new(uint), uintPtr(0), ""},
   190  		{"to *uint non nil", oneBigInt, false, new(uint), uintPtr(1), ""},
   191  		{"to *uint out of range pos", hugeBigIntPos, false, new(uint), new(uint), "cannot convert from *big.Int to *uint: value out of range: 18446744073709551616"},
   192  		{"to *uint out of range neg", hugeBigIntNeg, false, new(uint), new(uint), "cannot convert from *big.Int to *uint: value out of range: -9223372036854775809"},
   193  		{"to *uint64 nil dest", oneBigInt, false, uint64NilPtr(), uint64NilPtr(), "cannot convert from *big.Int to *uint64: destination is nil"},
   194  		{"to *uint64 nil source", nil, true, new(uint64), uint64Ptr(0), ""},
   195  		{"to *uint64 non nil", oneBigInt, false, new(uint64), uint64Ptr(1), ""},
   196  		{"to *uint64 out of range pos", hugeBigIntPos, false, new(uint64), new(uint64), "cannot convert from *big.Int to *uint64: value out of range: 18446744073709551616"},
   197  		{"to *uint64 out of range neg", hugeBigIntNeg, false, new(uint64), new(uint64), "cannot convert from *big.Int to *uint64: value out of range: -9223372036854775809"},
   198  		{"to *uint32 nil dest", oneBigInt, false, uint32NilPtr(), uint32NilPtr(), "cannot convert from *big.Int to *uint32: destination is nil"},
   199  		{"to *uint32 nil source", nil, true, new(uint32), uint32Ptr(0), ""},
   200  		{"to *uint32 non nil", oneBigInt, false, new(uint32), uint32Ptr(1), ""},
   201  		{"to *uint32 out of range pos", hugeBigIntPos, false, new(uint32), new(uint32), "cannot convert from *big.Int to *uint32: value out of range: 18446744073709551616"},
   202  		{"to *uint32 out of range neg", hugeBigIntNeg, false, new(uint32), new(uint32), "cannot convert from *big.Int to *uint32: value out of range: -9223372036854775809"},
   203  		{"to *uint16 nil dest", oneBigInt, false, uint16NilPtr(), uint16NilPtr(), "cannot convert from *big.Int to *uint16: destination is nil"},
   204  		{"to *uint16 nil source", nil, true, new(uint16), uint16Ptr(0), ""},
   205  		{"to *uint16 non nil", oneBigInt, false, new(uint16), uint16Ptr(1), ""},
   206  		{"to *uint16 out of range pos", hugeBigIntPos, false, new(uint16), new(uint16), "cannot convert from *big.Int to *uint16: value out of range: 18446744073709551616"},
   207  		{"to *uint16 out of range neg", hugeBigIntNeg, false, new(uint16), new(uint16), "cannot convert from *big.Int to *uint16: value out of range: -9223372036854775809"},
   208  		{"to *uint8 nil dest", oneBigInt, false, uint8NilPtr(), uint8NilPtr(), "cannot convert from *big.Int to *uint8: destination is nil"},
   209  		{"to *uint8 nil source", nil, true, new(uint8), uint8Ptr(0), ""},
   210  		{"to *uint8 non nil", oneBigInt, false, new(uint8), uint8Ptr(1), ""},
   211  		{"to *uint8 out of range pos", hugeBigIntPos, false, new(uint8), new(uint8), "cannot convert from *big.Int to *uint8: value out of range: 18446744073709551616"},
   212  		{"to *uint8 out of range neg", hugeBigIntNeg, false, new(uint8), new(uint8), "cannot convert from *big.Int to *uint8: value out of range: -9223372036854775809"},
   213  		{"to *string nil dest", oneBigInt, false, stringNilPtr(), stringNilPtr(), "cannot convert from *big.Int to *string: destination is nil"},
   214  		{"to *string nil source", nil, true, new(string), new(string), ""},
   215  		{"to *string non nil", oneBigInt, false, new(string), stringPtr("1"), ""},
   216  		{"to untyped nil", oneBigInt, false, nil, nil, "cannot convert from *big.Int to <nil>: destination is nil"},
   217  		{"to non pointer", oneBigInt, false, int64(0), int64(0), "cannot convert from *big.Int to int64: destination is not pointer"},
   218  		{"to unsupported pointer type", oneBigInt, false, new(float64), new(float64), "cannot convert from *big.Int to *float64: conversion not supported"},
   219  	}
   220  	for _, tt := range tests {
   221  		t.Run(tt.name, func(t *testing.T) {
   222  			err := convertFromBigInt(tt.val, tt.wasNull, tt.dest)
   223  			assert.Equal(t, tt.expected, tt.dest)
   224  			assertErrorMessage(t, tt.err, err)
   225  		})
   226  	}
   227  }
   228  
   229  func Test_writeBigInt(t *testing.T) {
   230  	hugeNeg, ok := new(big.Int).SetString("-1042342234234123423435647768234", 10)
   231  	assert.True(t, ok)
   232  	tests := []struct {
   233  		name     string
   234  		val      *big.Int
   235  		expected []byte
   236  	}{
   237  		{"nil", nil, nil},
   238  		{"zero", zeroBigInt, []byte{0}},
   239  		{"1", oneBigInt, []byte{1}},
   240  		{"-1", big.NewInt(-1), []byte{0xff}},
   241  		{"100", big.NewInt(100), []byte{0x64}},
   242  		{"-100", big.NewInt(-100), []byte{0x9c}},
   243  		{"128", big.NewInt(128), []byte{0x00, 0x80}},
   244  		{"255", big.NewInt(255), []byte{0x00, 0xff}},
   245  		{"MinInt32", big.NewInt(math.MinInt32), []byte{0x80, 0x00, 0x00, 0x00}},
   246  		{"MaxInt32", big.NewInt(math.MaxInt32), []byte{0x7f, 0xff, 0xff, 0xff}},
   247  		{"huge neg", hugeNeg, []byte{0xf2, 0xd8, 0x02, 0xb6, 0x52, 0x7f, 0x99, 0xee, 0x98, 0x23, 0x99, 0xa9, 0x56}},
   248  		{"huge pos", new(big.Int).SetUint64(math.MaxUint64), []byte{0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}},
   249  	}
   250  	for _, tt := range tests {
   251  		t.Run(tt.name, func(t *testing.T) {
   252  			actual := writeBigInt(tt.val)
   253  			assert.Equal(t, tt.expected, actual)
   254  		})
   255  	}
   256  }
   257  
   258  func Test_readBigInt(t *testing.T) {
   259  	hugeNeg, ok := new(big.Int).SetString("-1042342234234123423435647768234", 10)
   260  	assert.True(t, ok)
   261  	tests := []struct {
   262  		name     string
   263  		source   []byte
   264  		expected *big.Int
   265  	}{
   266  		{"nil", nil, nil},
   267  		{"empty", []byte{}, nil},
   268  		{"zero", []byte{0}, zeroBigInt},
   269  		{"-1", []byte{0xff}, big.NewInt(-1)},
   270  		{"100", []byte{0x64}, big.NewInt(100)},
   271  		{"-100", []byte{0x9c}, big.NewInt(-100)},
   272  		{"128", []byte{0x00, 0x80}, big.NewInt(128)},
   273  		{"255", []byte{0x00, 0xff}, big.NewInt(255)},
   274  		{"huge neg", []byte{0xf2, 0xd8, 0x02, 0xb6, 0x52, 0x7f, 0x99, 0xee, 0x98, 0x23, 0x99, 0xa9, 0x56}, hugeNeg},
   275  		{"huge pos", []byte{0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, new(big.Int).SetUint64(math.MaxUint64)},
   276  	}
   277  	for _, tt := range tests {
   278  		t.Run(tt.name, func(t *testing.T) {
   279  			actual := readBigInt(tt.source)
   280  			assert.Zero(t, tt.expected.Cmp(actual))
   281  		})
   282  	}
   283  }