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