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  }