github.com/datastax/go-cassandra-native-protocol@v0.0.0-20220706104457-5e8aad05cf90/datacodec/collection_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  	"bytes"
    19  	"errors"
    20  	"fmt"
    21  	"math"
    22  	"strconv"
    23  	"testing"
    24  
    25  	"github.com/stretchr/testify/assert"
    26  	"github.com/stretchr/testify/mock"
    27  
    28  	"github.com/datastax/go-cassandra-native-protocol/datatype"
    29  	"github.com/datastax/go-cassandra-native-protocol/primitive"
    30  )
    31  
    32  var (
    33  	listOfInt, _          = NewList(datatype.NewList(datatype.Int))
    34  	listOfSetOfVarchar, _ = NewList(datatype.NewList(datatype.NewSet(datatype.Varchar)))
    35  )
    36  
    37  var (
    38  	listOneBytes4 = []byte{
    39  		0, 0, 0, 1,
    40  		0, 0, 0, 4,
    41  		0, 0, 0, 1,
    42  	}
    43  	listOneBytes2 = []byte{
    44  		0, 1,
    45  		0, 0, 0, 4,
    46  		0, 0, 0, 1,
    47  	}
    48  	listOneTwoThreeBytes2 = []byte{
    49  		0, 3,
    50  		0, 0, 0, 4,
    51  		0, 0, 0, 1,
    52  		0, 0, 0, 4,
    53  		0, 0, 0, 2,
    54  		0, 0, 0, 4,
    55  		0, 0, 0, 3,
    56  	}
    57  	listOneTwoThreeBytes4 = []byte{
    58  		0, 0, 0, 3,
    59  		0, 0, 0, 4,
    60  		0, 0, 0, 1,
    61  		0, 0, 0, 4,
    62  		0, 0, 0, 2,
    63  		0, 0, 0, 4,
    64  		0, 0, 0, 3,
    65  	}
    66  	listAbcDefBytes2 = []byte{
    67  		0, 2, // length of outer collection
    68  		0, 0, 0, 9, // length of outer collection 1st element
    69  		0, 1, // length of 1st inner collection
    70  		0, 0, 0, 3, // length of 1st inner collection 1st element
    71  		a, b, c, // element
    72  		0, 0, 0, 9, // length of outer collection 2nd element
    73  		0, 1, // length of 2nd inner collection
    74  		0, 0, 0, 3, // length of 2nd inner collection 1st element
    75  		d, e, f, // element
    76  	}
    77  	listAbcDefBytes4 = []byte{
    78  		0, 0, 0, 2, // length of outer collection
    79  		0, 0, 0, 11, // length of outer collection 1st element
    80  		0, 0, 0, 1, // length of 1st inner collection
    81  		0, 0, 0, 3, // length of 1st inner collection 1st element
    82  		a, b, c, // element
    83  		0, 0, 0, 11, // length of outer collection 2nd element
    84  		0, 0, 0, 1, // length of 2nd inner collection
    85  		0, 0, 0, 3, // length of 2nd inner collection 1st element
    86  		d, e, f, // element
    87  	}
    88  	listAbcDefEmptyBytes2 = []byte{
    89  		0, 2, // length of outer collection
    90  		0, 0, 0, 16, // length of outer collection 1st element
    91  		0, 2, // length of 1st inner collection
    92  		0, 0, 0, 3, // length of 1st inner collection 1st element
    93  		a, b, c, // element
    94  		0, 0, 0, 3, // length of 1st inner collection 2nd element
    95  		d, e, f, // element
    96  		0, 0, 0, 2, // length of outer collection 2nd element
    97  		0, 0, // length of 2nd inner collection
    98  	}
    99  	listAbcDefEmptyBytes4 = []byte{
   100  		0, 0, 0, 2, // length of outer collection
   101  		0, 0, 0, 18, // length of outer collection 1st element
   102  		0, 0, 0, 2, // length of 1st inner collection
   103  		0, 0, 0, 3, // length of 1st inner collection 1st element
   104  		a, b, c, // element
   105  		0, 0, 0, 3, // length of 1st inner collection 2nd element
   106  		d, e, f, // element
   107  		0, 0, 0, 4, // length of outer collection 2nd element
   108  		0, 0, 0, 0, // length of 2nd inner collection
   109  	}
   110  	// lists with null elements can happen when retrieving the TTLs of some collection
   111  	listOneTwoNullBytes4 = []byte{
   112  		0, 0, 0, 3,
   113  		0, 0, 0, 4,
   114  		0, 0, 0, 1,
   115  		0, 0, 0, 4,
   116  		0, 0, 0, 2,
   117  		255, 255, 255, 255,
   118  	}
   119  	listOneTwoNullBytes2 = []byte{
   120  		0, 3,
   121  		0, 0, 0, 4,
   122  		0, 0, 0, 1,
   123  		0, 0, 0, 4,
   124  		0, 0, 0, 2,
   125  		255, 255, 255, 255,
   126  	}
   127  	listAbcNullNullBytes4 = []byte{
   128  		0, 0, 0, 2, // length of outer collection
   129  		0, 0, 0, 15, // length of outer collection 1st element
   130  		0, 0, 0, 2, // length of 1st inner collection
   131  		0, 0, 0, 3, // length of 1st inner collection 1st element
   132  		a, b, c, // element
   133  		255, 255, 255, 255, // inner collection 2nd element (null)
   134  		255, 255, 255, 255, // outer collection 2nd element (null)
   135  	}
   136  	listAbcNullNullBytes2 = []byte{
   137  		0, 2, // length of outer collection
   138  		0, 0, 0, 13, // length of outer collection 1st element
   139  		0, 2, // length of 1st inner collection
   140  		0, 0, 0, 3, // length of 1st inner collection 1st element
   141  		a, b, c, // element
   142  		255, 255, 255, 255, // inner collection 2nd element (null)
   143  		255, 255, 255, 255, // outer collection 2nd element (null)
   144  	}
   145  )
   146  
   147  func TestNewList(t *testing.T) {
   148  	tests := []struct {
   149  		name     string
   150  		dataType *datatype.List
   151  		want     Codec
   152  		wantErr  string
   153  	}{
   154  		{
   155  			"nil",
   156  			nil,
   157  			nil,
   158  			"data type is nil",
   159  		},
   160  		{
   161  			"simple",
   162  			datatype.NewList(datatype.Int),
   163  			&collectionCodec{
   164  				dataType:     datatype.NewList(datatype.Int),
   165  				elementCodec: &intCodec{},
   166  			},
   167  			"",
   168  		},
   169  		{
   170  			"complex",
   171  			datatype.NewList(datatype.NewList(datatype.Int)),
   172  			&collectionCodec{
   173  				dataType: datatype.NewList(datatype.NewList(datatype.Int)),
   174  				elementCodec: &collectionCodec{
   175  					dataType:     datatype.NewList(datatype.Int),
   176  					elementCodec: &intCodec{},
   177  				},
   178  			},
   179  			"",
   180  		},
   181  		{
   182  			"wrong data type",
   183  			datatype.NewList(wrongDataType{}),
   184  			nil,
   185  			"cannot create codec for list elements: cannot create data codec for CQL type 666",
   186  		},
   187  	}
   188  	for _, tt := range tests {
   189  		t.Run(tt.name, func(t *testing.T) {
   190  			got, gotErr := NewList(tt.dataType)
   191  			assert.Equal(t, tt.want, got)
   192  			assertErrorMessage(t, tt.wantErr, gotErr)
   193  		})
   194  	}
   195  }
   196  
   197  func TestNewSet(t *testing.T) {
   198  	tests := []struct {
   199  		name     string
   200  		dataType *datatype.Set
   201  		want     Codec
   202  		wantErr  string
   203  	}{
   204  		{
   205  			"nil",
   206  			nil,
   207  			nil,
   208  			"data type is nil",
   209  		},
   210  		{
   211  			"simple",
   212  			datatype.NewSet(datatype.Int),
   213  			&collectionCodec{
   214  				dataType:     datatype.NewSet(datatype.Int),
   215  				elementCodec: &intCodec{},
   216  			},
   217  			"",
   218  		},
   219  		{
   220  			"complex",
   221  			datatype.NewSet(datatype.NewSet(datatype.Int)),
   222  			&collectionCodec{
   223  				dataType: datatype.NewSet(datatype.NewSet(datatype.Int)),
   224  				elementCodec: &collectionCodec{
   225  					dataType:     datatype.NewSet(datatype.Int),
   226  					elementCodec: &intCodec{},
   227  				},
   228  			},
   229  			"",
   230  		},
   231  		{
   232  			"wrong data type",
   233  			datatype.NewSet(wrongDataType{}),
   234  			nil,
   235  			"cannot create codec for set elements: cannot create data codec for CQL type 666",
   236  		},
   237  	}
   238  	for _, tt := range tests {
   239  		t.Run(tt.name, func(t *testing.T) {
   240  			got, gotErr := NewSet(tt.dataType)
   241  			assert.Equal(t, tt.want, got)
   242  			assertErrorMessage(t, tt.wantErr, gotErr)
   243  		})
   244  	}
   245  }
   246  
   247  func Test_collectionCodec_Encode(t *testing.T) {
   248  	for _, version := range primitive.SupportedProtocolVersionsGreaterThanOrEqualTo(primitive.ProtocolVersion3) {
   249  		t.Run(version.String(), func(t *testing.T) {
   250  			tests := []struct {
   251  				name     string
   252  				codec    Codec
   253  				source   interface{}
   254  				expected []byte
   255  				err      string
   256  			}{
   257  				{"list<int> nil untyped", listOfInt, nil, nil, ""},
   258  				{"list<int> nil slice", listOfInt, new([]int), nil, ""},
   259  				{"list<int> empty", listOfInt, []int{}, []byte{0, 0, 0, 0}, ""},
   260  				{"list<int> one elem", listOfInt, []int{1}, listOneBytes4, ""},
   261  				{"list<int> one elem array", listOfInt, [1]int{1}, listOneBytes4, ""},
   262  				{"list<int> many elems", listOfInt, []int{1, 2, 3}, listOneTwoThreeBytes4, ""},
   263  				{"list<int> pointer slice", listOfInt, &[]int{1, 2, 3}, listOneTwoThreeBytes4, ""},
   264  				{"list<int> many elems pointers", listOfInt, []*int{intPtr(1), intPtr(2), intPtr(3)}, listOneTwoThreeBytes4, ""},
   265  				{"list<int> pointer slice pointer elems", listOfInt, &[]*int{intPtr(1), intPtr(2), intPtr(3)}, listOneTwoThreeBytes4, ""},
   266  				{"list<int> many elems []interface{}", listOfInt, []interface{}{1, 2, 3}, listOneTwoThreeBytes4, ""},
   267  				{"list<int> many elems interface{}", listOfInt, interface{}([]interface{}{1, 2, 3}), listOneTwoThreeBytes4, ""},
   268  				{"list<int> nil element", listOfInt, []interface{}{nil}, []byte{0x0, 0x0, 0x0, 0x1, 0xff, 0xff, 0xff, 0xff}, ""},
   269  				{"list<int> wrong source type", listOfInt, 123, nil, fmt.Sprintf("cannot encode int as CQL %s with %s: source type not supported", listOfInt.DataType(), version)},
   270  				{"list<int> wrong source type nil", listOfInt, map[string]int(nil), nil, fmt.Sprintf("cannot encode map[string]int as CQL %s with %s: source type not supported", listOfInt.DataType(), version)},
   271  				{"list<int> wrong source type nil pointer", listOfInt, new(map[string]int), nil, fmt.Sprintf("cannot encode *map[string]int as CQL %s with %s: source type not supported", listOfInt.DataType(), version)},
   272  				{"list<set<text>> nil untyped", listOfSetOfVarchar, nil, nil, ""},
   273  				{"list<set<text>> nil slice", listOfSetOfVarchar, [][]string(nil), nil, ""},
   274  				{"list<set<text>> empty", listOfSetOfVarchar, [][]string{}, []byte{0, 0, 0, 0}, ""},
   275  				{"list<set<text>> many elems", listOfSetOfVarchar, [][]string{{"abc"}, {"def"}}, listAbcDefBytes4, ""},
   276  				{"list<set<text>> pointer array", listOfSetOfVarchar, &[2][1]string{{"abc"}, {"def"}}, listAbcDefBytes4, ""},
   277  				{"list<set<text>> pointers", listOfSetOfVarchar, &[][]*string{{stringPtr("abc"), stringPtr("def")}, {}}, listAbcDefEmptyBytes4, ""},
   278  				{"list<set<text>> many elems interface{}", listOfSetOfVarchar, [][]interface{}{{"abc", "def"}, {}}, listAbcDefEmptyBytes4, ""},
   279  				{"list<set<text>> nil element", listOfSetOfVarchar, []interface{}{nil}, []byte{0x0, 0x0, 0x0, 0x1, 0xff, 0xff, 0xff, 0xff}, ""},
   280  				{"list<set<text>> nil inner element", listOfSetOfVarchar, []interface{}{[]interface{}{nil}}, []byte{0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x8, 0x0, 0x0, 0x0, 0x1, 0xff, 0xff, 0xff, 0xff}, ""},
   281  				{"list<set<text>> wrong source type", listOfSetOfVarchar, 123, nil, fmt.Sprintf("cannot encode int as CQL %s with %s: source type not supported", listOfSetOfVarchar.DataType(), version)},
   282  			}
   283  			for _, tt := range tests {
   284  				t.Run(tt.name, func(t *testing.T) {
   285  					actual, err := tt.codec.Encode(tt.source, version)
   286  					assert.Equal(t, tt.expected, actual)
   287  					assertErrorMessage(t, tt.err, err)
   288  				})
   289  			}
   290  		})
   291  	}
   292  	for _, version := range primitive.SupportedProtocolVersionsLesserThan(primitive.ProtocolVersion3) {
   293  		t.Run(version.String(), func(t *testing.T) {
   294  			tests := []struct {
   295  				name     string
   296  				codec    Codec
   297  				source   interface{}
   298  				expected []byte
   299  				err      string
   300  			}{
   301  				{"list<int> nil untyped", listOfInt, nil, nil, ""},
   302  				{"list<int> nil slice", listOfInt, []int(nil), nil, ""},
   303  				{"list<int> empty", listOfInt, []int{}, []byte{0, 0}, ""},
   304  				{"list<int> one elem", listOfInt, []int{1}, listOneBytes2, ""},
   305  				{"list<int> one elem array", listOfInt, [1]int{1}, listOneBytes2, ""},
   306  				{"list<int> many elems", listOfInt, []int{1, 2, 3}, listOneTwoThreeBytes2, ""},
   307  				{"list<int> many elems pointers", listOfInt, []*int{intPtr(1), intPtr(2), intPtr(3)}, listOneTwoThreeBytes2, ""},
   308  				{"list<int> many elems interface{}", listOfInt, []interface{}{1, 2, 3}, listOneTwoThreeBytes2, ""},
   309  				{"list<int> nil element", listOfInt, []interface{}{nil}, []byte{0x0, 0x1, 0xff, 0xff, 0xff, 0xff}, ""},
   310  				{"list<int> wrong source type", listOfInt, 123, nil, fmt.Sprintf("cannot encode int as CQL %s with %s: source type not supported", listOfInt.DataType(), version)},
   311  				{"list<int> wrong source type nil", listOfInt, map[string]int(nil), nil, fmt.Sprintf("cannot encode map[string]int as CQL %s with %s: source type not supported", listOfInt.DataType(), version)},
   312  				{"list<set<text>> nil untyped", listOfSetOfVarchar, nil, nil, ""},
   313  				{"list<set<text>> nil slice", listOfSetOfVarchar, [][]string(nil), nil, ""},
   314  				{"list<set<text>> empty", listOfSetOfVarchar, [][]string{}, []byte{0, 0}, ""},
   315  				{"list<set<text>> many elems", listOfSetOfVarchar, [][]string{{"abc"}, {"def"}}, listAbcDefBytes2, ""},
   316  				{"list<set<text>> array", listOfSetOfVarchar, [2][1]string{{"abc"}, {"def"}}, listAbcDefBytes2, ""},
   317  				{"list<set<text>> pointers", listOfSetOfVarchar, [][]*string{{stringPtr("abc"), stringPtr("def")}, {}}, listAbcDefEmptyBytes2, ""},
   318  				{"list<set<text>> many elems interface{}", listOfSetOfVarchar, [][]interface{}{{"abc", "def"}, {}}, listAbcDefEmptyBytes2, ""},
   319  				{"list<set<text>> nil element", listOfSetOfVarchar, []interface{}{nil}, []byte{0x0, 0x1, 0xff, 0xff, 0xff, 0xff}, ""},
   320  				{"list<set<text>> nil inner element", listOfSetOfVarchar, []interface{}{[]interface{}{nil}}, []byte{0x0, 0x1, 0x0, 0x0, 0x0, 0x6, 0x0, 0x1, 0xff, 0xff, 0xff, 0xff}, ""},
   321  				{"list<set<text>> wrong source type", listOfSetOfVarchar, 123, nil, fmt.Sprintf("cannot encode int as CQL %s with %s: source type not supported", listOfSetOfVarchar.DataType(), version)},
   322  			}
   323  			for _, tt := range tests {
   324  				t.Run(tt.name, func(t *testing.T) {
   325  					actual, err := tt.codec.Encode(tt.source, version)
   326  					assert.Equal(t, tt.expected, actual)
   327  					assertErrorMessage(t, tt.err, err)
   328  				})
   329  			}
   330  		})
   331  	}
   332  }
   333  
   334  func Test_collectionCodec_Decode(t *testing.T) {
   335  	for _, version := range primitive.SupportedProtocolVersionsGreaterThanOrEqualTo(primitive.ProtocolVersion3) {
   336  		t.Run(version.String(), func(t *testing.T) {
   337  			tests := []struct {
   338  				name     string
   339  				codec    Codec
   340  				source   []byte
   341  				dest     interface{}
   342  				want     interface{}
   343  				wantNull bool
   344  				err      string
   345  			}{
   346  				{"list<int> nil untyped", listOfInt, nil, nil, nil, true, fmt.Sprintf("cannot decode CQL list<int> as <nil> with %v: destination is nil", version)},
   347  				{"list<int> nil slice", listOfInt, nil, new([]int), new([]int), true, ""},
   348  				{"list<int> empty", listOfInt, []byte{0, 0, 0, 0}, new([]int), &[]int{}, false, ""},
   349  				{"list<int> one elem", listOfInt, listOneBytes4, new([]int), &[]int{1}, false, ""},
   350  				{"list<int> one elem array", listOfInt, listOneBytes4, new([1]int), &[1]int{1}, false, ""},
   351  				{"list<int> many elems", listOfInt, listOneTwoThreeBytes4, new([]int), &[]int{1, 2, 3}, false, ""},
   352  				{"list<int> many elems pointers", listOfInt, listOneTwoThreeBytes4, new([]*int), &[]*int{intPtr(1), intPtr(2), intPtr(3)}, false, ""},
   353  				{"list<int> many elems []interface{}", listOfInt, listOneTwoThreeBytes4, new([]interface{}), &[]interface{}{int32(1), int32(2), int32(3)}, false, ""},
   354  				{"list<int> many elems interface{}", listOfInt, listOneTwoThreeBytes4, new(interface{}), interfacePtr([]*int32{int32Ptr(1), int32Ptr(2), int32Ptr(3)}), false, ""},
   355  				{"list<int> nil elem", listOfInt, listOneTwoNullBytes4, new([]int), &[]int{1, 2, 0}, false, ""},
   356  				{"list<int> nil elem pointers", listOfInt, listOneTwoNullBytes4, new([]*int), &[]*int{intPtr(1), intPtr(2), nil}, false, ""},
   357  				{"list<int> nil elem []interface{}", listOfInt, listOneTwoNullBytes4, new([]interface{}), &[]interface{}{int32(1), int32(2), nil}, false, ""},
   358  				{"list<int> nil elem interface{}", listOfInt, listOneTwoNullBytes4, new(interface{}), interfacePtr([]*int32{int32Ptr(1), int32Ptr(2), nil}), false, ""},
   359  				{"list<int> pointer required", listOfInt, nil, []interface{}{}, []interface{}{}, true, fmt.Sprintf("cannot decode CQL %s as []interface {} with %s: destination is not pointer", listOfInt.DataType(), version)},
   360  				{"list<int> wrong destination type", listOfInt, nil, &map[string]int{}, new(map[string]int), true, fmt.Sprintf("cannot decode CQL %s as *map[string]int with %s: destination type not supported", listOfInt.DataType(), version)},
   361  				{"list<set<text>> nil untyped", listOfSetOfVarchar, nil, nil, nil, true, fmt.Sprintf("cannot decode CQL list<set<varchar>> as <nil> with %v: destination is nil", version)},
   362  				{"list<set<text>> nil slice", listOfSetOfVarchar, nil, new([][]string), new([][]string), true, ""},
   363  				{"list<set<text>> empty", listOfSetOfVarchar, []byte{0, 0, 0, 0}, new([][]string), &[][]string{}, false, ""},
   364  				{"list<set<text>> many elems", listOfSetOfVarchar, listAbcDefBytes4, new([][]string), &[][]string{{"abc"}, {"def"}}, false, ""},
   365  				{"list<set<text>> array", listOfSetOfVarchar, listAbcDefBytes4, new([2][1]string), &[2][1]string{{"abc"}, {"def"}}, false, ""},
   366  				{"list<set<text>> pointers", listOfSetOfVarchar, listAbcDefEmptyBytes4, new([][]*string), &[][]*string{{stringPtr("abc"), stringPtr("def")}, {}}, false, ""},
   367  				{"list<set<text>> many elems [][]interface{}", listOfSetOfVarchar, listAbcDefEmptyBytes4, new([][]interface{}), &[][]interface{}{{"abc", "def"}, {}}, false, ""},
   368  				{"list<set<text>> many elems interface{}", listOfSetOfVarchar, listAbcDefEmptyBytes4, new(interface{}), interfacePtr([][]*string{{stringPtr("abc"), stringPtr("def")}, {}}), false, ""},
   369  				{"list<set<text>> nil elem", listOfSetOfVarchar, listAbcNullNullBytes4, new([][]string), &[][]string{{"abc", ""}, nil}, false, ""},
   370  				{"list<set<text>> nil elem pointers", listOfSetOfVarchar, listAbcNullNullBytes4, new([][]*string), &[][]*string{{stringPtr("abc"), nil}, nil}, false, ""},
   371  				{"list<set<text>> nil elem [][]interface{}", listOfSetOfVarchar, listAbcNullNullBytes4, new([][]interface{}), &[][]interface{}{{"abc", nil}, nil}, false, ""},
   372  				{"list<set<text>> nil elem interface{}", listOfSetOfVarchar, listAbcNullNullBytes4, new(interface{}), interfacePtr([][]*string{{stringPtr("abc"), nil}, nil}), false, ""},
   373  				{"pointer required", listOfSetOfVarchar, nil, [][]interface{}{}, [][]interface{}{}, true, fmt.Sprintf("cannot decode CQL %s as [][]interface {} with %s: destination is not pointer", listOfSetOfVarchar.DataType(), version)},
   374  				{"list<set<text>> wrong destination type", listOfSetOfVarchar, nil, &map[string]int{}, new(map[string]int), true, fmt.Sprintf("cannot decode CQL %s as *map[string]int with %s: destination type not supported", listOfSetOfVarchar.DataType(), version)},
   375  			}
   376  			for _, tt := range tests {
   377  				t.Run(tt.name, func(t *testing.T) {
   378  					wasNull, err := tt.codec.Decode(tt.source, tt.dest, version)
   379  					assert.Equal(t, tt.want, tt.dest)
   380  					assert.Equal(t, tt.wantNull, wasNull)
   381  					assertErrorMessage(t, tt.err, err)
   382  				})
   383  			}
   384  		})
   385  	}
   386  	for _, version := range primitive.SupportedProtocolVersionsLesserThan(primitive.ProtocolVersion3) {
   387  		t.Run(version.String(), func(t *testing.T) {
   388  			tests := []struct {
   389  				name     string
   390  				codec    Codec
   391  				source   []byte
   392  				dest     interface{}
   393  				want     interface{}
   394  				wantNull bool
   395  				err      string
   396  			}{
   397  				{"list<int> nil untyped", listOfInt, nil, nil, nil, true, "cannot decode CQL list<int> as <nil> with ProtocolVersion OSS 2: destination is nil"},
   398  				{"list<int> nil slice", listOfInt, nil, new([]int), new([]int), true, ""},
   399  				{"list<int> empty", listOfInt, []byte{0, 0}, new([]int), &[]int{}, false, ""},
   400  				{"list<int> one elem", listOfInt, listOneBytes2, new([]int), &[]int{1}, false, ""},
   401  				{"list<int> one elem array", listOfInt, listOneBytes2, new([1]int), &[1]int{1}, false, ""},
   402  				{"list<int> many elems", listOfInt, listOneTwoThreeBytes2, new([]int), &[]int{1, 2, 3}, false, ""},
   403  				{"list<int> many elems pointers", listOfInt, listOneTwoThreeBytes2, new([]*int), &[]*int{intPtr(1), intPtr(2), intPtr(3)}, false, ""},
   404  				{"list<int> many elems []interface{}", listOfInt, listOneTwoThreeBytes2, new([]interface{}), &[]interface{}{int32(1), int32(2), int32(3)}, false, ""},
   405  				{"list<int> many elems interface{}", listOfInt, listOneTwoThreeBytes2, new(interface{}), interfacePtr([]*int32{int32Ptr(1), int32Ptr(2), int32Ptr(3)}), false, ""},
   406  				{"list<int> nil elem", listOfInt, listOneTwoNullBytes2, new([]int), &[]int{1, 2, 0}, false, ""},
   407  				{"list<int> nil elem pointers", listOfInt, listOneTwoNullBytes2, new([]*int), &[]*int{intPtr(1), intPtr(2), nil}, false, ""},
   408  				{"list<int> nil elem []interface{}", listOfInt, listOneTwoNullBytes2, new([]interface{}), &[]interface{}{int32(1), int32(2), nil}, false, ""},
   409  				{"list<int> nil elem interface{}", listOfInt, listOneTwoNullBytes2, new(interface{}), interfacePtr([]*int32{int32Ptr(1), int32Ptr(2), nil}), false, ""},
   410  				{"list<int> pointer required", listOfInt, nil, []int{}, []int{}, true, fmt.Sprintf("cannot decode CQL %s as []int with %v: destination is not pointer", listOfInt.DataType(), version)},
   411  				{"list<int> destination type not supported", listOfInt, nil, &map[string]int{}, new(map[string]int), true, fmt.Sprintf("cannot decode CQL %s as *map[string]int with %v: destination type not supported", listOfInt.DataType(), version)},
   412  				{"list<set<text>> nil untyped", listOfSetOfVarchar, nil, nil, nil, true, "cannot decode CQL list<set<varchar>> as <nil> with ProtocolVersion OSS 2: destination is nil"},
   413  				{"list<set<text>> nil slice", listOfSetOfVarchar, nil, new([][]string), new([][]string), true, ""},
   414  				{"list<set<text>> empty", listOfSetOfVarchar, []byte{0, 0}, new([][]string), &[][]string{}, false, ""},
   415  				{"list<set<text>> many elems", listOfSetOfVarchar, listAbcDefBytes2, new([][]string), &[][]string{{"abc"}, {"def"}}, false, ""},
   416  				{"list<set<text>> array", listOfSetOfVarchar, listAbcDefBytes2, new([2][1]string), &[2][1]string{{"abc"}, {"def"}}, false, ""},
   417  				{"list<set<text>> pointers", listOfSetOfVarchar, listAbcDefEmptyBytes2, new([][]*string), &[][]*string{{stringPtr("abc"), stringPtr("def")}, {}}, false, ""},
   418  				{"list<set<text>> many elems [][]interface{}", listOfSetOfVarchar, listAbcDefEmptyBytes2, new([][]interface{}), &[][]interface{}{{"abc", "def"}, {}}, false, ""},
   419  				{"list<set<text>> many elems interface{}", listOfSetOfVarchar, listAbcDefEmptyBytes2, new(interface{}), interfacePtr([][]*string{{stringPtr("abc"), stringPtr("def")}, {}}), false, ""},
   420  				{"list<set<text>> nil elem", listOfSetOfVarchar, listAbcNullNullBytes2, new([][]string), &[][]string{{"abc", ""}, nil}, false, ""},
   421  				{"list<set<text>> nil elem pointers", listOfSetOfVarchar, listAbcNullNullBytes2, new([][]*string), &[][]*string{{stringPtr("abc"), nil}, nil}, false, ""},
   422  				{"list<set<text>> nil elem [][]interface{}", listOfSetOfVarchar, listAbcNullNullBytes2, new([][]interface{}), &[][]interface{}{{"abc", nil}, nil}, false, ""},
   423  				{"list<set<text>> nil elem interface{}", listOfSetOfVarchar, listAbcNullNullBytes2, new(interface{}), interfacePtr([][]*string{{stringPtr("abc"), nil}, nil}), false, ""},
   424  				{"list<set<text>> pointer required", listOfSetOfVarchar, nil, [][]string{}, [][]string{}, true, fmt.Sprintf("cannot decode CQL %s as [][]string with %s: destination is not pointer", listOfSetOfVarchar.DataType(), version)},
   425  				{"list<set<text>> wrong destination type", listOfSetOfVarchar, nil, &map[string]string{}, new(map[string]string), true, fmt.Sprintf("cannot decode CQL %s as *map[string]string with %s: destination type not supported", listOfSetOfVarchar.DataType(), version)},
   426  			}
   427  			for _, tt := range tests {
   428  				t.Run(tt.name, func(t *testing.T) {
   429  					wasNull, err := tt.codec.Decode(tt.source, tt.dest, version)
   430  					assert.Equal(t, tt.want, tt.dest)
   431  					assert.Equal(t, tt.wantNull, wasNull)
   432  					assertErrorMessage(t, tt.err, err)
   433  				})
   434  			}
   435  		})
   436  	}
   437  }
   438  
   439  func Test_writeCollection(t *testing.T) {
   440  	type args struct {
   441  		ext          extractor
   442  		elementCodec Codec
   443  		size         int
   444  		version      primitive.ProtocolVersion
   445  	}
   446  	tests := []struct {
   447  		name    string
   448  		args    args
   449  		want    []byte
   450  		wantErr string
   451  	}{
   452  		{
   453  			"cannot write size",
   454  			args{nil, nil, -1, primitive.ProtocolVersion5},
   455  			nil,
   456  			"cannot write collection size: expected collection size >= 0, got: -1",
   457  		},
   458  		{
   459  			"cannot extract elem",
   460  			args{func() extractor {
   461  				ext := &mockExtractor{}
   462  				ext.On("getElem", 0, 0).Return(nil, errSliceIndexOutOfRange(true, 0))
   463  				return ext
   464  			}(), nil, 1, primitive.ProtocolVersion5},
   465  			nil,
   466  			"cannot extract element 0: slice index out of range: 0",
   467  		},
   468  		{
   469  			"cannot encode",
   470  			args{
   471  				func() extractor {
   472  					ext := &mockExtractor{}
   473  					ext.On("getElem", 0, 0).Return(1, nil)
   474  					return ext
   475  				}(),
   476  				func() Codec {
   477  					codec := &mockCodec{}
   478  					codec.On("Encode", 1, primitive.ProtocolVersion5).Return(nil, errors.New("write failed"))
   479  					return codec
   480  				}(),
   481  				1,
   482  				primitive.ProtocolVersion5,
   483  			},
   484  			nil,
   485  			"cannot encode element 0: write failed",
   486  		},
   487  		{"success", args{
   488  			func() extractor {
   489  				ext := &mockExtractor{}
   490  				ext.On("getElem", 0, 0).Return(1, nil)
   491  				ext.On("getElem", 1, 1).Return(2, nil)
   492  				ext.On("getElem", 2, 2).Return(3, nil)
   493  				return ext
   494  			}(),
   495  			func() Codec {
   496  				codec := &mockCodec{}
   497  				codec.On("Encode", 1, primitive.ProtocolVersion5).Return([]byte{1}, nil)
   498  				codec.On("Encode", 2, primitive.ProtocolVersion5).Return([]byte{2}, nil)
   499  				codec.On("Encode", 3, primitive.ProtocolVersion5).Return([]byte{3}, nil)
   500  				return codec
   501  			}(),
   502  			3,
   503  			primitive.ProtocolVersion5,
   504  		}, []byte{
   505  			0, 0, 0, 3, // size
   506  			0, 0, 0, 1, // elem 1
   507  			1,
   508  			0, 0, 0, 1, // elem 2
   509  			2,
   510  			0, 0, 0, 1, // elem 3
   511  			3,
   512  		}, ""},
   513  	}
   514  	for _, tt := range tests {
   515  		t.Run(tt.name, func(t *testing.T) {
   516  			got, gotErr := writeCollection(tt.args.ext, tt.args.elementCodec, tt.args.size, tt.args.version)
   517  			assert.Equal(t, tt.want, got)
   518  			assertErrorMessage(t, tt.wantErr, gotErr)
   519  		})
   520  	}
   521  }
   522  
   523  func Test_readCollection(t *testing.T) {
   524  	type args struct {
   525  		source       []byte
   526  		inj          func(int) (injector, error)
   527  		elementCodec Codec
   528  		version      primitive.ProtocolVersion
   529  	}
   530  	tests := []struct {
   531  		name    string
   532  		args    args
   533  		wantErr string
   534  	}{
   535  		{
   536  			"cannot read size",
   537  			args{[]byte{1}, nil, nil, primitive.ProtocolVersion5},
   538  			"cannot read collection size: cannot read [int]: unexpected EOF",
   539  		},
   540  		{
   541  			"cannot create injector",
   542  			args{
   543  				[]byte{0, 0, 0, 1},
   544  				func(int) (injector, error) { return nil, errors.New("cannot create injector") },
   545  				nil,
   546  				primitive.ProtocolVersion5,
   547  			},
   548  			"cannot create injector",
   549  		},
   550  		{
   551  			"cannot read element",
   552  			args{
   553  				[]byte{
   554  					0, 0, 0, 1, // size
   555  					0, // wrong [bytes]
   556  				},
   557  				func(int) (injector, error) { return &mockInjector{}, nil },
   558  				nil,
   559  				primitive.ProtocolVersion5,
   560  			},
   561  			"cannot read element 0: cannot read [bytes] length: cannot read [int]: unexpected EOF",
   562  		},
   563  		{
   564  			"cannot create element",
   565  			args{
   566  				[]byte{
   567  					0, 0, 0, 1, // size
   568  					0, 0, 0, 1, 1, // [bytes]
   569  				},
   570  				func(int) (injector, error) {
   571  					inj := &mockInjector{}
   572  					inj.On("zeroElem", 0, 0).Return(nil, errors.New("wrong data type"))
   573  					return inj, nil
   574  				},
   575  				func() Codec {
   576  					codec := &mockCodec{}
   577  					codec.On("DataType").Return(datatype.Int)
   578  					return codec
   579  				}(),
   580  				primitive.ProtocolVersion5,
   581  			},
   582  			"cannot create zero element 0: wrong data type",
   583  		},
   584  		{
   585  			"cannot decode element",
   586  			args{
   587  				[]byte{
   588  					0, 0, 0, 1, // size
   589  					0, 0, 0, 1, 1, // [bytes]
   590  				},
   591  				func(int) (injector, error) {
   592  					inj := &mockInjector{}
   593  					inj.On("zeroElem", 0, 0).Return(new(int), nil)
   594  					return inj, nil
   595  				},
   596  				func() Codec {
   597  					codec := &mockCodec{}
   598  					codec.On("DataType").Return(datatype.Int)
   599  					codec.On("Decode", []byte{1}, new(int), primitive.ProtocolVersion5).Return(false, errors.New("decode failed"))
   600  					return codec
   601  				}(),
   602  				primitive.ProtocolVersion5,
   603  			},
   604  			"cannot decode element 0: decode failed",
   605  		},
   606  		{
   607  			"cannot set element",
   608  			args{
   609  				[]byte{
   610  					0, 0, 0, 1, // size
   611  					0, 0, 0, 1, 1, // [bytes]
   612  				},
   613  				func(int) (injector, error) {
   614  					inj := &mockInjector{}
   615  					inj.On("zeroElem", 0, 0).Return(new(int), nil)
   616  					inj.On("setElem", 0, 0, intPtr(123), false, false).Return(errors.New("cannot set elem"))
   617  					return inj, nil
   618  				},
   619  				func() Codec {
   620  					codec := &mockCodec{}
   621  					codec.On("DataType").Return(datatype.Int)
   622  					codec.On("Decode", []byte{1}, new(int), primitive.ProtocolVersion5).Run(func(args mock.Arguments) {
   623  						decodedElement := args.Get(1).(*int)
   624  						*decodedElement = 123
   625  					}).Return(false, nil)
   626  					return codec
   627  				}(),
   628  				primitive.ProtocolVersion5,
   629  			},
   630  			"cannot inject element 0: cannot set elem",
   631  		},
   632  		{
   633  			"bytes remaining",
   634  			args{
   635  				[]byte{
   636  					0, 0, 0, 1, // size
   637  					0, 0, 0, 1, 1, // [bytes]
   638  					1, // trailing bytes
   639  				},
   640  				func(int) (injector, error) {
   641  					inj := &mockInjector{}
   642  					inj.On("zeroElem", 0, 0).Return(new(int), nil)
   643  					inj.On("setElem", 0, 0, intPtr(123), false, false).Return(nil)
   644  					return inj, nil
   645  				},
   646  				func() Codec {
   647  					codec := &mockCodec{}
   648  					codec.On("DataType").Return(datatype.Int)
   649  					codec.On("Decode", []byte{1}, new(int), primitive.ProtocolVersion5).Run(func(args mock.Arguments) {
   650  						decodedElement := args.Get(1).(*int)
   651  						*decodedElement = 123
   652  					}).Return(false, nil)
   653  					return codec
   654  				}(),
   655  				primitive.ProtocolVersion5,
   656  			},
   657  			"source was not fully read: bytes total: 10, read: 9, remaining: 1",
   658  		},
   659  		{
   660  			"success",
   661  			args{
   662  				[]byte{
   663  					0, 0, 0, 3, // size
   664  					0, 0, 0, 1, 1, // [bytes]
   665  					0, 0, 0, 1, 2, // [bytes]
   666  					0, 0, 0, 1, 3, // [bytes]
   667  				},
   668  				func(int) (injector, error) {
   669  					inj := &mockInjector{}
   670  					inj.On("zeroElem", 0, 0).Return(new(int), nil)
   671  					inj.On("zeroElem", 1, 1).Return(new(int), nil)
   672  					inj.On("zeroElem", 2, 2).Return(new(int), nil)
   673  					inj.On("setElem", 0, 0, intPtr(123), false, false).Return(nil)
   674  					inj.On("setElem", 1, 1, intPtr(456), false, false).Return(nil)
   675  					inj.On("setElem", 2, 2, intPtr(789), false, false).Return(nil)
   676  					return inj, nil
   677  				},
   678  				func() Codec {
   679  					codec := &mockCodec{}
   680  					codec.On("DataType").Return(datatype.Int)
   681  					codec.On("Decode", []byte{1}, new(int), primitive.ProtocolVersion5).Run(func(args mock.Arguments) {
   682  						decodedElement := args.Get(1).(*int)
   683  						*decodedElement = 123
   684  					}).Return(false, nil)
   685  					codec.On("Decode", []byte{2}, new(int), primitive.ProtocolVersion5).Run(func(args mock.Arguments) {
   686  						decodedElement := args.Get(1).(*int)
   687  						*decodedElement = 456
   688  					}).Return(false, nil)
   689  					codec.On("Decode", []byte{3}, new(int), primitive.ProtocolVersion5).Run(func(args mock.Arguments) {
   690  						decodedElement := args.Get(1).(*int)
   691  						*decodedElement = 789
   692  					}).Return(false, nil)
   693  					return codec
   694  				}(),
   695  				primitive.ProtocolVersion5,
   696  			},
   697  			"",
   698  		},
   699  	}
   700  	for _, tt := range tests {
   701  		t.Run(tt.name, func(t *testing.T) {
   702  			gotErr := readCollection(tt.args.source, tt.args.inj, tt.args.elementCodec, tt.args.version)
   703  			assertErrorMessage(t, tt.wantErr, gotErr)
   704  		})
   705  	}
   706  }
   707  
   708  func Test_writeCollectionSize(t *testing.T) {
   709  	for _, version := range primitive.SupportedProtocolVersionsGreaterThanOrEqualTo(primitive.ProtocolVersion3) {
   710  		t.Run(version.String(), func(t *testing.T) {
   711  			tests := []struct {
   712  				name     string
   713  				size     int64
   714  				wantDest []byte
   715  				wantErr  string
   716  			}{
   717  				{"4 bytes zero", 0, []byte{0, 0, 0, 0}, ""},
   718  				{"4 bytes max", math.MaxInt32, intMaxInt32Bytes, ""},
   719  				{"4 bytes out of range neg", -1, nil, "expected collection size >= 0, got: -1"},
   720  			}
   721  			if strconv.IntSize == 64 {
   722  				tests = append(tests, []struct {
   723  					name     string
   724  					size     int64
   725  					wantDest []byte
   726  					wantErr  string
   727  				}{
   728  					{"4 bytes out of range pos", math.MaxInt32 + 1, nil, "cannot write collection size: collection too large (2147483648 elements, max is 2147483647)"},
   729  				}...)
   730  			}
   731  			for _, tt := range tests {
   732  				t.Run(tt.name, func(t *testing.T) {
   733  					dest := &bytes.Buffer{}
   734  					gotErr := writeCollectionSize(int(tt.size), dest, version)
   735  					assert.Equal(t, tt.wantDest, dest.Bytes())
   736  					assertErrorMessage(t, tt.wantErr, gotErr)
   737  				})
   738  			}
   739  		})
   740  	}
   741  	for _, version := range primitive.SupportedProtocolVersionsLesserThan(primitive.ProtocolVersion3) {
   742  		t.Run(version.String(), func(t *testing.T) {
   743  			tests := []struct {
   744  				name     string
   745  				size     int
   746  				wantDest []byte
   747  				wantErr  string
   748  			}{
   749  				{"2 bytes zero", 0, []byte{0, 0}, ""},
   750  				{"2 bytes max", math.MaxUint16, encodeUint16(0xffff), ""},
   751  				{"2 bytes out of range pos", math.MaxUint16 + 1, nil, "cannot write collection size: collection too large (65536 elements, max is 65535)"},
   752  				{"2 bytes out of range neg", -1, nil, "expected collection size >= 0, got: -1"},
   753  			}
   754  			for _, tt := range tests {
   755  				t.Run(tt.name, func(t *testing.T) {
   756  					dest := &bytes.Buffer{}
   757  					gotErr := writeCollectionSize(tt.size, dest, version)
   758  					assert.Equal(t, tt.wantDest, dest.Bytes())
   759  					assertErrorMessage(t, tt.wantErr, gotErr)
   760  				})
   761  			}
   762  		})
   763  	}
   764  }
   765  
   766  func Test_readCollectionSize(t *testing.T) {
   767  	for _, version := range primitive.SupportedProtocolVersionsGreaterThanOrEqualTo(primitive.ProtocolVersion3) {
   768  		t.Run(version.String(), func(t *testing.T) {
   769  			tests := []struct {
   770  				name     string
   771  				source   []byte
   772  				wantSize int
   773  				wantErr  string
   774  			}{
   775  				{"4 bytes success", []byte{0, 0, 0, 3}, 3, ""},
   776  				{"4 bytes error", []byte{0, 3}, 0, "cannot read collection size: cannot read [int]: unexpected EOF"},
   777  			}
   778  			for _, tt := range tests {
   779  				t.Run(tt.name, func(t *testing.T) {
   780  					gotSize, gotErr := readCollectionSize(bytes.NewReader(tt.source), version)
   781  					assert.Equal(t, tt.wantSize, gotSize)
   782  					assertErrorMessage(t, tt.wantErr, gotErr)
   783  				})
   784  			}
   785  		})
   786  	}
   787  	for _, version := range primitive.SupportedProtocolVersionsLesserThan(primitive.ProtocolVersion3) {
   788  		t.Run(version.String(), func(t *testing.T) {
   789  			tests := []struct {
   790  				name     string
   791  				source   []byte
   792  				wantSize int
   793  				wantErr  string
   794  			}{
   795  				{"2 bytes success", []byte{0, 3}, 3, ""},
   796  				{"2 bytes error", []byte{3}, 0, "cannot read collection size: cannot read [short]: unexpected EOF"},
   797  			}
   798  			for _, tt := range tests {
   799  				t.Run(tt.name, func(t *testing.T) {
   800  					gotSize, gotErr := readCollectionSize(bytes.NewReader(tt.source), version)
   801  					assert.Equal(t, tt.wantSize, gotSize)
   802  					assertErrorMessage(t, tt.wantErr, gotErr)
   803  				})
   804  			}
   805  		})
   806  	}
   807  }