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 }