github.com/datastax/go-cassandra-native-protocol@v0.0.0-20220706104457-5e8aad05cf90/datacodec/injectors_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 "math/big" 19 "net" 20 "reflect" 21 "testing" 22 23 "github.com/stretchr/testify/assert" 24 "github.com/stretchr/testify/require" 25 26 "github.com/datastax/go-cassandra-native-protocol/datatype" 27 "github.com/datastax/go-cassandra-native-protocol/primitive" 28 ) 29 30 type testStruct struct { 31 Value int 32 Pointer *string 33 Slice []int 34 PointerSlice []*string `cassandra:"pointer_slice"` 35 Array [2]int 36 PointerArray [2]*string `cassandra:"pointer_array"` 37 Map map[string]interface{} 38 Untyped interface{} 39 unexported bool 40 } 41 42 func Test_newSliceInjector(t *testing.T) { 43 var nilSlice []int 44 tests := []struct { 45 name string 46 dest reflect.Value 47 wantValue bool 48 wantErr string 49 }{ 50 {"nil", reflect.Value{}, false, "destination type not supported"}, 51 {"nil typed", reflect.ValueOf(&nilSlice).Elem(), true, ""}, 52 {"not array nor slice", reflect.ValueOf(&map[string]int{}).Elem(), false, "expected slice or array, got: map[string]int"}, 53 {"success", reflect.ValueOf(&[]int{1}).Elem(), true, ""}, 54 } 55 for _, tt := range tests { 56 t.Run(tt.name, func(t *testing.T) { 57 gotValue, gotErr := newSliceInjector(tt.dest) 58 if tt.wantValue { 59 assert.NotNil(t, gotValue) 60 } else { 61 assert.Nil(t, gotValue) 62 } 63 assertErrorMessage(t, tt.wantErr, gotErr) 64 }) 65 } 66 } 67 func Test_newStructInjector(t *testing.T) { 68 tests := []struct { 69 name string 70 dest reflect.Value 71 wantValue bool 72 wantErr string 73 }{ 74 {"nil", reflect.Value{}, false, "destination type not supported"}, 75 {"not struct", reflect.ValueOf(&map[string]int{}).Elem(), false, "expected struct, got: map[string]int"}, 76 {"unaddressable", reflect.ValueOf(testStruct{}), false, "destination of type datacodec.testStruct is not addressable"}, 77 {"success", reflect.ValueOf(&testStruct{}).Elem(), true, ""}, 78 } 79 for _, tt := range tests { 80 t.Run(tt.name, func(t *testing.T) { 81 gotValue, gotErr := newStructInjector(tt.dest) 82 if tt.wantValue { 83 assert.NotNil(t, gotValue) 84 } else { 85 assert.Nil(t, gotValue) 86 } 87 assertErrorMessage(t, tt.wantErr, gotErr) 88 }) 89 } 90 } 91 92 func Test_newMapInjector(t *testing.T) { 93 var nilMap map[int]int 94 tests := []struct { 95 name string 96 dest reflect.Value 97 wantValue bool 98 wantErr string 99 }{ 100 {"nil", reflect.Value{}, false, "destination type not supported"}, 101 {"nil typed", reflect.ValueOf(&nilMap).Elem(), true, ""}, 102 {"not map", reflect.ValueOf(&[]int{}).Elem(), false, "expected map, got: []int"}, 103 {"success", reflect.ValueOf(&map[int]int{123: 456}).Elem(), true, ""}, 104 } 105 for _, tt := range tests { 106 t.Run(tt.name, func(t *testing.T) { 107 gotValue, gotErr := newMapInjector(tt.dest) 108 if tt.wantValue { 109 assert.NotNil(t, gotValue) 110 } else { 111 assert.Nil(t, gotValue) 112 } 113 assertErrorMessage(t, tt.wantErr, gotErr) 114 }) 115 } 116 } 117 118 func Test_sliceInjector_zeroElem(t *testing.T) { 119 tests := []struct { 120 name string 121 dest interface{} 122 wantValue interface{} 123 }{ 124 {"[]int", []int{}, intPtr(0)}, 125 {"[]string", []string{}, stringPtr("")}, 126 {"[]interface", []interface{}{}, new(interface{})}, 127 {"[1]interface", [1]interface{}{}, new(interface{})}, 128 } 129 for _, tt := range tests { 130 t.Run(tt.name, func(t *testing.T) { 131 dest := pointerTo(reflect.ValueOf(tt.dest)).Elem() 132 i, err := newSliceInjector(dest) 133 require.NoError(t, err) 134 gotValue, gotErr := i.zeroElem(-1, nil) 135 assert.Equal(t, tt.wantValue, gotValue) 136 assert.NoError(t, gotErr) 137 }) 138 } 139 } 140 141 func Test_structInjector_zeroElem(t *testing.T) { 142 tests := []struct { 143 name string 144 key interface{} 145 wantValue interface{} 146 wantErr string 147 }{ 148 {"by index value", 0, new(int), ""}, 149 {"by index pointer", 1, new(string), ""}, 150 {"by index slice", 2, new([]int), ""}, 151 {"by index pointer slice", 3, new([]*string), ""}, 152 {"by index array", 4, new([2]int), ""}, 153 {"by index pointer array", 5, new([2]*string), ""}, 154 {"by index map", 6, new(map[string]interface{}), ""}, 155 {"by index untyped", 7, new(interface{}), ""}, 156 {"by index out of range neg", -1, nil, "no accessible field with index -1 found in struct datacodec.testStruct"}, 157 {"by index out of range pos", 100, nil, "no accessible field with index 100 found in struct datacodec.testStruct"}, 158 {"by name value", "value", new(int), ""}, 159 {"by name pointer", "pointer", new(string), ""}, 160 {"by name slice", "slice", new([]int), ""}, 161 {"by name pointer slice", "pointer_slice", new([]*string), ""}, 162 {"by name array", "array", new([2]int), ""}, 163 {"by name pointer array", "pointer_array", new([2]*string), ""}, 164 {"by name map", "MAP", new(map[string]interface{}), ""}, 165 {"by name untyped", "Untyped", new(interface{}), ""}, 166 {"by name unexported", "unexported", nil, "no accessible field with name 'unexported' found in struct datacodec.testStruct"}, 167 {"by name nonexistent", "nonexistent", nil, "no accessible field with name 'nonexistent' found in struct datacodec.testStruct"}, 168 {"wrong key", true, nil, "wrong struct field key, expected int or string, got: bool"}, 169 } 170 for _, tt := range tests { 171 t.Run(tt.name, func(t *testing.T) { 172 i, err := newStructInjector(reflect.ValueOf(&testStruct{}).Elem()) 173 require.NoError(t, err) 174 gotValue, gotErr := i.zeroElem(-1, tt.key) 175 assert.Equal(t, tt.wantValue, gotValue) 176 assertErrorMessage(t, tt.wantErr, gotErr) 177 }) 178 } 179 } 180 181 func Test_structInjector_zeroKey(t *testing.T) { 182 i, _ := newStructInjector(reflect.ValueOf(&testStruct{}).Elem()) 183 got, err := i.zeroKey(0) 184 assert.Equal(t, new(string), got) 185 assert.NoError(t, err) 186 } 187 188 func Test_mapInjector_zeroKey(t *testing.T) { 189 tests := []struct { 190 name string 191 dest interface{} 192 wantValue interface{} 193 wantErr string 194 }{ 195 {"map[int]int", map[int]int{}, new(int), ""}, 196 {"map[*int]int", map[*int]int{}, new(int), ""}, 197 {"map[string]int", map[string]int{}, new(string), ""}, 198 {"map[interface{}]int", map[interface{}]int{}, interfacePtr(nil), ""}, 199 } 200 for _, tt := range tests { 201 t.Run(tt.name, func(t *testing.T) { 202 dest := pointerTo(reflect.ValueOf(tt.dest)).Elem() 203 i, err := newMapInjector(dest) 204 require.NoError(t, err) 205 gotValue, gotErr := i.zeroKey(0) 206 assert.Equal(t, tt.wantValue, gotValue) 207 assertErrorMessage(t, tt.wantErr, gotErr) 208 }) 209 } 210 } 211 212 func Test_mapInjector_zeroElem(t *testing.T) { 213 tests := []struct { 214 name string 215 dest interface{} 216 wantValue interface{} 217 }{ 218 {"map[int]int", map[int]int{}, new(int)}, 219 {"map[int]string", map[int]string{}, new(string)}, 220 {"map[int]*string", map[int]*string{}, new(string)}, 221 {"map[string]interface{}", map[string]interface{}{}, interfacePtr(nil)}, 222 {"map[string][]map[string]interface{}", map[string][]map[string]interface{}{}, new([]map[string]interface{})}, 223 } 224 for _, tt := range tests { 225 t.Run(tt.name, func(t *testing.T) { 226 dest := pointerTo(reflect.ValueOf(tt.dest)).Elem() 227 i, err := newMapInjector(dest) 228 require.NoError(t, err) 229 gotValue, gotErr := i.zeroElem(-1, "") 230 assert.Equal(t, tt.wantValue, gotValue) 231 assert.NoError(t, gotErr) 232 }) 233 } 234 } 235 236 func Test_sliceInjector_setElem(t *testing.T) { 237 tests := []struct { 238 name string 239 index int 240 value interface{} 241 wasNull bool 242 dest interface{} 243 want interface{} 244 wantErr string 245 }{ 246 {"[]int null", 0, new(int), true, []int{0}, []int{0}, ""}, 247 {"[]int non null", 0, intPtr(1), false, []int{0}, []int{1}, ""}, 248 {"[]*int null", 0, new(int), true, []*int{nil}, []*int{nil}, ""}, 249 {"[]*int non null", 0, intPtr(1), false, []*int{nil}, []*int{intPtr(1)}, ""}, 250 {"[]*int non null multi", 1, intPtr(1), false, []*int{nil, nil}, []*int{nil, intPtr(1)}, ""}, 251 {"[]int out of range neg", -1, intPtr(1), false, []int{123, 456}, []int{123, 456}, "slice index out of range: -1"}, 252 {"[]int out of range neg", 2, intPtr(1), false, []int{123, 456}, []int{123, 456}, "slice index out of range: 2"}, 253 {"[]interface{} null", 0, new(int), true, []interface{}{nil}, []interface{}{nil}, ""}, 254 {"[]interface{} non null", 0, intPtr(1), false, []interface{}{nil}, []interface{}{1}, ""}, 255 {"[]int wrong value", 0, stringPtr("abc"), false, []int{123}, []int{123}, "wrong slice element, expected int, got: string"}, 256 {"[1]int wrong value", 0, stringPtr("abc"), false, [1]int{123}, [1]int{123}, "wrong array element, expected int, got: string"}, 257 } 258 for _, tt := range tests { 259 t.Run(tt.name, func(t *testing.T) { 260 dest := pointerTo(reflect.ValueOf(tt.dest)).Elem() 261 i, err := newSliceInjector(dest) 262 require.NoError(t, err) 263 gotErr := i.setElem(tt.index, nil, tt.value, false, tt.wasNull) 264 assert.Equal(t, tt.want, tt.dest) 265 assertErrorMessage(t, tt.wantErr, gotErr) 266 }) 267 } 268 } 269 270 func Test_structInjector_setElem(t *testing.T) { 271 tests := []struct { 272 name string 273 key interface{} 274 value interface{} 275 wasNull bool 276 want testStruct 277 wantErr string 278 }{ 279 {"by index value null", 0, new(int), true, testStruct{}, ""}, 280 {"by index value non null", 0, intPtr(1), false, testStruct{Value: 1}, ""}, 281 {"by index pointer null", 1, new(string), true, testStruct{}, ""}, 282 {"by index pointer non null", 1, stringPtr("abc"), false, testStruct{Pointer: stringPtr("abc")}, ""}, 283 {"by index slice null", 2, new([]int), true, testStruct{}, ""}, 284 {"by index slice non null", 2, &[]int{123, 456}, false, testStruct{Slice: []int{123, 456}}, ""}, 285 {"by index pointer slice null", 3, new([]*string), true, testStruct{}, ""}, 286 {"by index pointer slice non null", 3, &[]*string{stringPtr("abc"), nil}, false, testStruct{PointerSlice: []*string{stringPtr("abc"), nil}}, ""}, 287 {"by index array null", 4, new([2]int), true, testStruct{}, ""}, 288 {"by index array non null", 4, &[2]int{123, 456}, false, testStruct{Array: [2]int{123, 456}}, ""}, 289 {"by index pointer array null", 5, new([2]*string), true, testStruct{}, ""}, 290 {"by index pointer array non null", 5, &[2]*string{stringPtr("abc"), nil}, false, testStruct{PointerArray: [2]*string{stringPtr("abc"), nil}}, ""}, 291 {"by index map null", 6, new(map[string]interface{}), true, testStruct{}, ""}, 292 {"by index map non null", 6, &map[string]interface{}{"abc": 123, "def": nil}, false, testStruct{Map: map[string]interface{}{"abc": 123, "def": nil}}, ""}, 293 {"by index untyped null", 7, new(interface{}), true, testStruct{}, ""}, 294 {"by index untyped non null", 7, stringPtr("abc"), false, testStruct{Untyped: "abc"}, ""}, 295 {"by index untyped wrong type", 7, stringPtr("abc"), false, testStruct{Untyped: "abc"}, ""}, 296 {"by name value null", "Value", new(int), true, testStruct{}, ""}, 297 {"by name value non null", "Value", intPtr(1), false, testStruct{Value: 1}, ""}, 298 {"by name pointer null", "Pointer", new(string), true, testStruct{}, ""}, 299 {"by name pointer non null", "Pointer", stringPtr("abc"), false, testStruct{Pointer: stringPtr("abc")}, ""}, 300 {"by name slice null", "Slice", new([]int), true, testStruct{}, ""}, 301 {"by name slice non null", "Slice", &[]int{123, 456}, false, testStruct{Slice: []int{123, 456}}, ""}, 302 {"by name pointer slice null", "pointer_slice", new([]*string), true, testStruct{}, ""}, 303 {"by name pointer slice non null", "pointer_slice", &[]*string{stringPtr("abc"), nil}, false, testStruct{PointerSlice: []*string{stringPtr("abc"), nil}}, ""}, 304 {"by name array null", "array", new([2]int), true, testStruct{}, ""}, 305 {"by name array non null", "array", &[2]int{123, 456}, false, testStruct{Array: [2]int{123, 456}}, ""}, 306 {"by name pointer array null", "pointer_array", new([2]*string), true, testStruct{}, ""}, 307 {"by name pointer array non null", "pointer_array", &[2]*string{stringPtr("abc"), nil}, false, testStruct{PointerArray: [2]*string{stringPtr("abc"), nil}}, ""}, 308 {"by name map null", "MAP", new(map[string]interface{}), true, testStruct{}, ""}, 309 {"by name map non null", "Map", &map[string]interface{}{"abc": 123, "def": nil}, false, testStruct{Map: map[string]interface{}{"abc": 123, "def": nil}}, ""}, 310 {"by name untyped null", "UNTYPED", new(interface{}), true, testStruct{}, ""}, 311 {"by name untyped non null", "untyped", stringPtr("abc"), false, testStruct{Untyped: "abc"}, ""}, 312 {"by name untyped wrong type", "untyped", stringPtr("abc"), false, testStruct{Untyped: "abc"}, ""}, 313 {"wrong key", true, nil, false, testStruct{}, "no accessible field with name 'true' found in struct datacodec.testStruct"}, 314 {"wrong value", 0, stringPtr("abc"), false, testStruct{}, "wrong struct field value, expected int, got: string"}, 315 } 316 for _, tt := range tests { 317 t.Run(tt.name, func(t *testing.T) { 318 dest := testStruct{} 319 i, err := newStructInjector(reflect.ValueOf(&dest).Elem()) 320 require.NoError(t, err) 321 // setElem expects the field to have been previously cached 322 _, err = i.(*structInjector).locateAndStoreField(tt.key) 323 if tt.wantErr == "" { 324 require.NoError(t, err) 325 } 326 gotErr := i.setElem(-1, tt.key, tt.value, false, tt.wasNull) 327 assert.Equal(t, tt.want, dest) 328 assertErrorMessage(t, tt.wantErr, gotErr) 329 }) 330 } 331 } 332 333 func Test_mapInjector_setElem(t *testing.T) { 334 tests := []struct { 335 name string 336 key interface{} 337 value interface{} 338 keyWasNull bool 339 valueWasNull bool 340 dest interface{} 341 want interface{} 342 wantErr string 343 }{ 344 {"[string]int value null", stringPtr("abc"), new(int), false, true, map[string]int{}, map[string]int{"abc": 0}, ""}, 345 {"[string]int value non null", stringPtr("abc"), intPtr(1), false, false, map[string]int{}, map[string]int{"abc": 1}, ""}, 346 {"[string]*int value null", stringPtr("abc"), new(int), false, true, map[string]*int{}, map[string]*int{"abc": nil}, ""}, 347 {"[string]*int value non null", stringPtr("abc"), intPtr(1), false, false, map[string]*int{}, map[string]*int{"abc": intPtr(1)}, ""}, 348 {"[int]interface{} value null", intPtr(123), new(int), false, true, map[int]interface{}{}, map[int]interface{}{123: nil}, ""}, 349 {"[int]interface{} value non null", intPtr(123), intPtr(456), false, false, map[int]interface{}{}, map[int]interface{}{123: 456}, ""}, 350 {"[string]int key null", new(string), intPtr(456), true, false, map[string]int{}, map[string]int{"": 456}, ""}, 351 {"[*string]int key null", new(string), intPtr(456), true, false, map[*string]int{}, map[*string]int{nil: 456}, ""}, 352 {"[interface{}]int key null", new(int), intPtr(456), true, false, map[interface{}]int{}, map[interface{}]int{nil: 456}, ""}, 353 {"[string]int key and value null", new(string), new(int), true, true, map[string]int{}, map[string]int{"": 0}, ""}, 354 {"[*string]int key and value null", new(string), new(int), true, true, map[*string]int{}, map[*string]int{nil: 0}, ""}, 355 {"[interface{}]int key and value null", new(int), new(int), true, true, map[interface{}]int{}, map[interface{}]int{nil: 0}, ""}, 356 {"wrong key", intPtr(123), stringPtr("abc"), false, false, map[string]string{}, map[string]string{}, "wrong map key, expected string, got: int"}, 357 {"wrong value", stringPtr("abc"), intPtr(456), false, false, map[string]string{}, map[string]string{}, "wrong map value, expected string, got: int"}, 358 } 359 for _, tt := range tests { 360 t.Run(tt.name, func(t *testing.T) { 361 dest := pointerTo(reflect.ValueOf(tt.dest)).Elem() 362 i, err := newMapInjector(dest) 363 require.NoError(t, err) 364 gotErr := i.setElem(-1, tt.key, tt.value, tt.keyWasNull, tt.valueWasNull) 365 assert.Equal(t, tt.want, tt.dest) 366 assertErrorMessage(t, tt.wantErr, gotErr) 367 }) 368 } 369 } 370 371 // A special test to check the sequence zero -> decode -> set on various data types. 372 func Test_injectors_zeroDecodeAndSet(t *testing.T) { 373 udt, _ := datatype.NewUserDefined("ks1", "table1", []string{"f1"}, []datatype.DataType{datatype.Int}) 374 dataTypes := map[datatype.DataType]interface{}{ 375 datatype.Ascii: "ascii", 376 datatype.Bigint: int64(123), 377 datatype.Blob: []byte{1, 2, 3}, 378 datatype.Boolean: true, 379 datatype.Counter: int64(456), 380 datatype.Date: datePos, 381 datatype.Decimal: CqlDecimal{ 382 Unscaled: big.NewInt(123456), 383 Scale: -1, 384 }, 385 datatype.Double: 123.456, 386 datatype.Duration: CqlDuration{12, 34, 5678}, 387 datatype.Float: float32(123.456), 388 datatype.Inet: net.ParseIP("fe80::aede:48ff:fe00:1122"), 389 datatype.Int: int32(42), 390 datatype.Smallint: int16(42), 391 datatype.Time: durationSimple, 392 datatype.Timestamp: timestampPosUTC, 393 datatype.Timeuuid: primitive.UUID{0xC0, 0xD1, 0xD2, 0x1E, 0xBB, 0x01, 0x41, 0x96, 0x86, 0xDB, 0xBC, 0x31, 0x7B, 0xC1, 0x79, 0x6A}, 394 datatype.Tinyint: int8(42), 395 datatype.Uuid: primitive.UUID{0xC0, 0xD1, 0xD2, 0x1E, 0xBB, 0x01, 0x41, 0x96, 0x86, 0xDB, 0xBC, 0x31, 0x7B, 0xC1, 0x79, 0x6A}, 396 datatype.Varchar: "UTF-8", 397 datatype.Varint: big.NewInt(123456), 398 datatype.NewList(datatype.Int): []*int32{int32Ptr(1), int32Ptr(2), int32Ptr(3)}, 399 datatype.NewSet(datatype.Int): []*int32{int32Ptr(1), int32Ptr(2), int32Ptr(3)}, 400 datatype.NewMap(datatype.Int, datatype.Varchar): map[*int32]*string{int32Ptr(123): stringPtr("abc")}, 401 datatype.NewTuple(datatype.Int, datatype.Varchar): []interface{}{int32(123), "abc"}, 402 udt: map[string]interface{}{"f1": int32(123)}, 403 } 404 for dataType, source := range dataTypes { 405 t.Run(dataType.AsCql()+" on interface{} receiver", func(t *testing.T) { 406 t.Run("slice element", func(t *testing.T) { 407 dest := make([]interface{}, 1) 408 inj, err := newSliceInjector(reflect.ValueOf(dest)) 409 require.NoError(t, err) 410 testInjectorAndDataType(t, source, inj, dataType, func() interface{} { return dest[0] }) 411 }) 412 t.Run("map value", func(t *testing.T) { 413 dest := make(map[int]interface{}, 1) 414 inj, err := newMapInjector(reflect.ValueOf(dest)) 415 require.NoError(t, err) 416 testInjectorAndDataType(t, source, inj, dataType, func() interface{} { return dest[0] }) 417 }) 418 t.Run("struct field", func(t *testing.T) { 419 dest := struct{ Field interface{} }{} 420 inj, err := newStructInjector(reflect.ValueOf(&dest).Elem()) 421 require.NoError(t, err) 422 testInjectorAndDataType(t, source, inj, dataType, func() interface{} { return dest.Field }) 423 }) 424 }) 425 } 426 } 427 428 func testInjectorAndDataType(t *testing.T, source interface{}, inj injector, dataType datatype.DataType, actual func() interface{}) { 429 zero, err := inj.zeroElem(0, 0) 430 require.NoError(t, err) 431 elementCodec, err := NewCodec(dataType) 432 require.NoError(t, err) 433 bytes, err := elementCodec.Encode(source, primitive.ProtocolVersion5) 434 require.NoError(t, err) 435 wasNull, err := elementCodec.Decode(bytes, zero, primitive.ProtocolVersion5) 436 require.NoError(t, err) 437 assert.False(t, wasNull) 438 err = inj.setElem(0, 0, zero, false, false) 439 require.NoError(t, err) 440 a := actual() 441 if _, ok := dataType.(*datatype.Map); ok { 442 assert.Len(t, a, 1) 443 assert.IsType(t, map[*int32]*string{}, a) 444 for k, v := range a.(map[*int32]*string) { 445 assert.Equal(t, int32(123), *k) 446 assert.Equal(t, "abc", *v) 447 } 448 assert.False(t, wasNull) 449 } else { 450 assert.Equal(t, source, a) 451 } 452 }