github.com/datastax/go-cassandra-native-protocol@v0.0.0-20220706104457-5e8aad05cf90/message/batch_test.go (about)

     1  // Copyright 2020 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 message
    16  
    17  import (
    18  	"bytes"
    19  	"errors"
    20  	"testing"
    21  
    22  	"github.com/stretchr/testify/assert"
    23  
    24  	"github.com/datastax/go-cassandra-native-protocol/primitive"
    25  )
    26  
    27  func TestBatch_Clone(t *testing.T) {
    28  	msg := &Batch{
    29  		Type: primitive.BatchTypeLogged,
    30  		Children: []*BatchChild{{
    31  			Query: "query",
    32  			Values: []*primitive.Value{{
    33  				Type:     primitive.ValueTypeRegular,
    34  				Contents: []byte{0x0a},
    35  			}},
    36  		}},
    37  		Consistency:       primitive.ConsistencyLevelLocalOne,
    38  		SerialConsistency: consistencyLevelPtr(primitive.ConsistencyLevelSerial),
    39  		DefaultTimestamp:  int64Ptr(1),
    40  		Keyspace:          "ks1",
    41  		NowInSeconds:      int32Ptr(2),
    42  	}
    43  	cloned := msg.DeepCopy()
    44  	assert.Equal(t, msg, cloned)
    45  	cloned.Type = primitive.BatchTypeUnlogged
    46  	cloned.Children = []*BatchChild{{
    47  		Query: "query2",
    48  		Values: []*primitive.Value{{
    49  			Type:     primitive.ValueTypeNull,
    50  			Contents: []byte{0x0b},
    51  		}},
    52  	}}
    53  	cloned.Consistency = primitive.ConsistencyLevelAll
    54  	cloned.SerialConsistency = consistencyLevelPtr(primitive.ConsistencyLevelLocalSerial)
    55  	cloned.DefaultTimestamp = int64Ptr(5)
    56  	cloned.Keyspace = "ks2"
    57  	cloned.NowInSeconds = int32Ptr(9)
    58  	assert.Equal(t, "query", msg.Children[0].Query)
    59  	assert.Equal(t, "query2", cloned.Children[0].Query)
    60  	assert.Equal(t, primitive.BatchTypeLogged, msg.Type)
    61  	assert.Equal(t, primitive.BatchTypeUnlogged, cloned.Type)
    62  	assert.Equal(t, primitive.ConsistencyLevelLocalOne, msg.Consistency)
    63  	assert.Equal(t, primitive.ConsistencyLevelAll, cloned.Consistency)
    64  	assert.Equal(t, primitive.ConsistencyLevelSerial, *msg.SerialConsistency)
    65  	assert.Equal(t, primitive.ConsistencyLevelLocalSerial, *cloned.SerialConsistency)
    66  	assert.EqualValues(t, 1, *msg.DefaultTimestamp)
    67  	assert.EqualValues(t, 5, *cloned.DefaultTimestamp)
    68  	assert.Equal(t, "ks1", msg.Keyspace)
    69  	assert.Equal(t, "ks2", cloned.Keyspace)
    70  	assert.EqualValues(t, 2, *msg.NowInSeconds)
    71  	assert.EqualValues(t, 9, *cloned.NowInSeconds)
    72  	assert.NotEqual(t, msg, cloned)
    73  }
    74  
    75  func TestBatchCodec_Encode(t *testing.T) {
    76  	codec := &batchCodec{}
    77  	// version = 2
    78  	t.Run(primitive.ProtocolVersion2.String(), func(t *testing.T) {
    79  		tests := []encodeTestCase{
    80  			{
    81  				"not a batch",
    82  				&AuthChallenge{[]byte{0xca, 0xfe, 0xba, 0xbe}},
    83  				nil,
    84  				errors.New("expected *message.Batch, got *message.AuthChallenge"),
    85  			},
    86  			{
    87  				"invalid batch type",
    88  				&Batch{Type: primitive.BatchType(42)},
    89  				nil,
    90  				errors.New("invalid BATCH type: BatchType ? [0X2A]"),
    91  			},
    92  			{
    93  				"empty batch",
    94  				&Batch{},
    95  				[]byte{
    96  					byte(primitive.BatchTypeLogged),
    97  					0, 0, // children count
    98  					0, 0, // consistency level
    99  				},
   100  				nil,
   101  			},
   102  			{
   103  				"batch with 2 children",
   104  				&Batch{
   105  					Children: []*BatchChild{
   106  						{
   107  							Query:  "INSERT",
   108  							Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
   109  						},
   110  						{
   111  							Id:     []byte{0xca, 0xfe, 0xba, 0xbe},
   112  							Values: []*primitive.Value{primitive.NewValue([]byte{5, 6, 7, 8})},
   113  						},
   114  					},
   115  				},
   116  				[]byte{
   117  					byte(primitive.BatchTypeLogged),
   118  					0, 2, // children count
   119  					0,                            // child 1 kind
   120  					0, 0, 0, 6, I, N, S, E, R, T, // child 1 query
   121  					0, 1, // child 1 values count
   122  					0, 0, 0, 4, 1, 2, 3, 4, // child 1 value 1
   123  					1,                            // child 2 kind
   124  					0, 4, 0xca, 0xfe, 0xba, 0xbe, // child 2 query id
   125  					0, 1, // child 2 values count
   126  					0, 0, 0, 4, 5, 6, 7, 8, // child 2 value 1
   127  					0, 0, // consistency level
   128  				},
   129  				nil,
   130  			},
   131  		}
   132  		for _, tt := range tests {
   133  			t.Run(tt.name, func(t *testing.T) {
   134  				dest := &bytes.Buffer{}
   135  				err := codec.Encode(tt.input, dest, primitive.ProtocolVersion2)
   136  				assert.Equal(t, tt.expected, dest.Bytes())
   137  				assert.Equal(t, tt.err, err)
   138  			})
   139  		}
   140  	})
   141  	// versions = 3, 4
   142  	for _, version := range []primitive.ProtocolVersion{primitive.ProtocolVersion3, primitive.ProtocolVersion4} {
   143  		t.Run(version.String(), func(t *testing.T) {
   144  			tests := []encodeTestCase{
   145  				{
   146  					"not a batch",
   147  					&AuthChallenge{[]byte{0xca, 0xfe, 0xba, 0xbe}},
   148  					nil,
   149  					errors.New("expected *message.Batch, got *message.AuthChallenge"),
   150  				},
   151  				{
   152  					"invalid batch type",
   153  					&Batch{Type: primitive.BatchType(42)},
   154  					nil,
   155  					errors.New("invalid BATCH type: BatchType ? [0X2A]"),
   156  				},
   157  				{
   158  					"empty batch",
   159  					&Batch{},
   160  					[]byte{
   161  						byte(primitive.BatchTypeLogged),
   162  						0, 0, // children count
   163  						0, 0, // consistency level
   164  						0, // flags
   165  					},
   166  					nil,
   167  				},
   168  				{
   169  					"batch with 2 children",
   170  					&Batch{
   171  						Children: []*BatchChild{
   172  							{
   173  								Query:  "INSERT",
   174  								Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
   175  							},
   176  							{
   177  								Id:     []byte{0xca, 0xfe, 0xba, 0xbe},
   178  								Values: []*primitive.Value{primitive.NewValue([]byte{5, 6, 7, 8})},
   179  							},
   180  						},
   181  					},
   182  					[]byte{
   183  						byte(primitive.BatchTypeLogged),
   184  						0, 2, // children count
   185  						0,                            // child 1 kind
   186  						0, 0, 0, 6, I, N, S, E, R, T, // child 1 query
   187  						0, 1, // child 1 values count
   188  						0, 0, 0, 4, 1, 2, 3, 4, // child 1 value 1
   189  						1,                            // child 2 kind
   190  						0, 4, 0xca, 0xfe, 0xba, 0xbe, // child 2 query id
   191  						0, 1, // child 2 values count
   192  						0, 0, 0, 4, 5, 6, 7, 8, // child 2 value 1
   193  						0, 0, // consistency level
   194  						0, // flags
   195  					},
   196  					nil,
   197  				},
   198  				{
   199  					"batch with custom options",
   200  					&Batch{
   201  						Type: primitive.BatchTypeUnlogged,
   202  						Children: []*BatchChild{
   203  							{
   204  								Query:  "INSERT",
   205  								Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
   206  							},
   207  						},
   208  						Consistency:       primitive.ConsistencyLevelLocalQuorum,
   209  						SerialConsistency: consistencyLevelPtr(primitive.ConsistencyLevelLocalSerial),
   210  						DefaultTimestamp:  int64Ptr(123),
   211  					},
   212  					[]byte{
   213  						byte(primitive.BatchTypeUnlogged),
   214  						0, 1, // children count
   215  						0,                            // child 1 kind
   216  						0, 0, 0, 6, I, N, S, E, R, T, // child 1 query
   217  						0, 1, // child 1 values count
   218  						0, 0, 0, 4, 1, 2, 3, 4, // child 1 value 1
   219  						0, 6, // consistency
   220  						0b0011_0000, // flags
   221  						0, 9,        // serial consistency
   222  						0, 0, 0, 0, 0, 0, 0, 123, // default timestamp
   223  					},
   224  					nil,
   225  				},
   226  			}
   227  			for _, tt := range tests {
   228  				t.Run(tt.name, func(t *testing.T) {
   229  					dest := &bytes.Buffer{}
   230  					err := codec.Encode(tt.input, dest, version)
   231  					assert.Equal(t, tt.expected, dest.Bytes())
   232  					assert.Equal(t, tt.err, err)
   233  				})
   234  			}
   235  		})
   236  	}
   237  	// version = 5
   238  	t.Run(primitive.ProtocolVersion5.String(), func(t *testing.T) {
   239  		tests := []encodeTestCase{
   240  			{
   241  				"not a batch",
   242  				&AuthChallenge{[]byte{0xca, 0xfe, 0xba, 0xbe}},
   243  				nil,
   244  				errors.New("expected *message.Batch, got *message.AuthChallenge"),
   245  			},
   246  			{
   247  				"invalid batch type",
   248  				&Batch{Type: primitive.BatchType(42)},
   249  				nil,
   250  				errors.New("invalid BATCH type: BatchType ? [0X2A]"),
   251  			},
   252  			{
   253  				"empty batch",
   254  				&Batch{},
   255  				[]byte{
   256  					byte(primitive.BatchTypeLogged),
   257  					0, 0, // children count
   258  					0, 0, // consistency level
   259  					0, 0, 0, 0, // flags
   260  				},
   261  				nil,
   262  			},
   263  			{
   264  				"batch with 2 children",
   265  				&Batch{
   266  					Children: []*BatchChild{
   267  						{
   268  							Query:  "INSERT",
   269  							Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
   270  						},
   271  						{
   272  							Id:     []byte{0xca, 0xfe, 0xba, 0xbe},
   273  							Values: []*primitive.Value{primitive.NewValue([]byte{5, 6, 7, 8})},
   274  						},
   275  					},
   276  				},
   277  				[]byte{
   278  					byte(primitive.BatchTypeLogged),
   279  					0, 2, // children count
   280  					0,                            // child 1 kind
   281  					0, 0, 0, 6, I, N, S, E, R, T, // child 1 query
   282  					0, 1, // child 1 values count
   283  					0, 0, 0, 4, 1, 2, 3, 4, // child 1 value 1
   284  					1,                            // child 2 kind
   285  					0, 4, 0xca, 0xfe, 0xba, 0xbe, // child 2 query id
   286  					0, 1, // child 2 values count
   287  					0, 0, 0, 4, 5, 6, 7, 8, // child 2 value 1
   288  					0, 0, // consistency level
   289  					0, 0, 0, 0, // flags
   290  				},
   291  				nil,
   292  			},
   293  			{
   294  				"batch with custom options",
   295  				&Batch{
   296  					Type: primitive.BatchTypeUnlogged,
   297  					Children: []*BatchChild{
   298  						{
   299  							Query:  "INSERT",
   300  							Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
   301  						},
   302  					},
   303  					Consistency:       primitive.ConsistencyLevelLocalQuorum,
   304  					SerialConsistency: consistencyLevelPtr(primitive.ConsistencyLevelLocalSerial),
   305  					DefaultTimestamp:  int64Ptr(123),
   306  					Keyspace:          "ks1",
   307  					NowInSeconds:      int32Ptr(234),
   308  				},
   309  				[]byte{
   310  					byte(primitive.BatchTypeUnlogged),
   311  					0, 1, // children count
   312  					0,                            // child 1 kind
   313  					0, 0, 0, 6, I, N, S, E, R, T, // child 1 query
   314  					0, 1, // child 1 values count
   315  					0, 0, 0, 4, 1, 2, 3, 4, // child 1 value 1
   316  					0, 6, // consistency
   317  					0b0000_0000, // flags
   318  					0b0000_0000, // flags
   319  					0b0000_0001, // flags => 0x100 (now in seconds)
   320  					0b1011_0000, // flags => 0x10 (serial) | 0x20 (timestamp) | 0x80 (keyspace)
   321  					0, 9,        // serial consistency
   322  					0, 0, 0, 0, 0, 0, 0, 123, // default timestamp
   323  					0, 3, k, s, _1, // keyspace
   324  					0, 0, 0, 234, // now in seconds
   325  				},
   326  				nil,
   327  			},
   328  		}
   329  		for _, tt := range tests {
   330  			t.Run(tt.name, func(t *testing.T) {
   331  				dest := &bytes.Buffer{}
   332  				err := codec.Encode(tt.input, dest, primitive.ProtocolVersion5)
   333  				assert.Equal(t, tt.expected, dest.Bytes())
   334  				assert.Equal(t, tt.err, err)
   335  			})
   336  		}
   337  	})
   338  	// version = DSE v1
   339  	t.Run(primitive.ProtocolVersionDse1.String(), func(t *testing.T) {
   340  		tests := []encodeTestCase{
   341  			{
   342  				"not a batch",
   343  				&AuthChallenge{[]byte{0xca, 0xfe, 0xba, 0xbe}},
   344  				nil,
   345  				errors.New("expected *message.Batch, got *message.AuthChallenge"),
   346  			},
   347  			{
   348  				"invalid batch type",
   349  				&Batch{Type: primitive.BatchType(42)},
   350  				nil,
   351  				errors.New("invalid BATCH type: BatchType ? [0X2A]"),
   352  			},
   353  			{
   354  				"empty batch",
   355  				&Batch{},
   356  				[]byte{
   357  					byte(primitive.BatchTypeLogged),
   358  					0, 0, // children count
   359  					0, 0, // consistency level
   360  					0, 0, 0, 0, // flags
   361  				},
   362  				nil,
   363  			},
   364  			{
   365  				"batch with 2 children",
   366  				&Batch{
   367  					Children: []*BatchChild{
   368  						{
   369  							Query:  "INSERT",
   370  							Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
   371  						},
   372  						{
   373  							Id:     []byte{0xca, 0xfe, 0xba, 0xbe},
   374  							Values: []*primitive.Value{primitive.NewValue([]byte{5, 6, 7, 8})},
   375  						},
   376  					},
   377  				},
   378  				[]byte{
   379  					byte(primitive.BatchTypeLogged),
   380  					0, 2, // children count
   381  					0,                            // child 1 kind
   382  					0, 0, 0, 6, I, N, S, E, R, T, // child 1 query
   383  					0, 1, // child 1 values count
   384  					0, 0, 0, 4, 1, 2, 3, 4, // child 1 value 1
   385  					1,                            // child 2 kind
   386  					0, 4, 0xca, 0xfe, 0xba, 0xbe, // child 2 query id
   387  					0, 1, // child 2 values count
   388  					0, 0, 0, 4, 5, 6, 7, 8, // child 2 value 1
   389  					0, 0, // consistency level
   390  					0, 0, 0, 0, // flags
   391  				},
   392  				nil,
   393  			},
   394  			{
   395  				"batch with custom options",
   396  				&Batch{
   397  					Type: primitive.BatchTypeUnlogged,
   398  					Children: []*BatchChild{
   399  						{
   400  							Query:  "INSERT",
   401  							Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
   402  						},
   403  					},
   404  					Consistency:       primitive.ConsistencyLevelLocalQuorum,
   405  					SerialConsistency: consistencyLevelPtr(primitive.ConsistencyLevelLocalSerial),
   406  					DefaultTimestamp:  int64Ptr(123),
   407  				},
   408  				[]byte{
   409  					byte(primitive.BatchTypeUnlogged),
   410  					0, 1, // children count
   411  					0,                            // child 1 kind
   412  					0, 0, 0, 6, I, N, S, E, R, T, // child 1 query
   413  					0, 1, // child 1 values count
   414  					0, 0, 0, 4, 1, 2, 3, 4, // child 1 value 1
   415  					0, 6, // consistency
   416  					0, 0, 0, 0b0011_0000, // flags 0x10 (serial) | 0x20 (timestamp)
   417  					0, 9, // serial consistency
   418  					0, 0, 0, 0, 0, 0, 0, 123, // default timestamp
   419  				},
   420  				nil,
   421  			},
   422  		}
   423  		for _, tt := range tests {
   424  			t.Run(tt.name, func(t *testing.T) {
   425  				dest := &bytes.Buffer{}
   426  				err := codec.Encode(tt.input, dest, primitive.ProtocolVersionDse1)
   427  				assert.Equal(t, tt.expected, dest.Bytes())
   428  				assert.Equal(t, tt.err, err)
   429  			})
   430  		}
   431  	})
   432  	// version = DSE v2
   433  	t.Run(primitive.ProtocolVersionDse2.String(), func(t *testing.T) {
   434  		tests := []encodeTestCase{
   435  			{
   436  				"not a batch",
   437  				&AuthChallenge{[]byte{0xca, 0xfe, 0xba, 0xbe}},
   438  				nil,
   439  				errors.New("expected *message.Batch, got *message.AuthChallenge"),
   440  			},
   441  			{
   442  				"invalid batch type",
   443  				&Batch{Type: primitive.BatchType(42)},
   444  				nil,
   445  				errors.New("invalid BATCH type: BatchType ? [0X2A]"),
   446  			},
   447  			{
   448  				"empty batch",
   449  				&Batch{},
   450  				[]byte{
   451  					byte(primitive.BatchTypeLogged),
   452  					0, 0, // children count
   453  					0, 0, // consistency level
   454  					0, 0, 0, 0, // flags
   455  				},
   456  				nil,
   457  			},
   458  			{
   459  				"batch with 2 children",
   460  				&Batch{
   461  					Children: []*BatchChild{
   462  						{
   463  							Query:  "INSERT",
   464  							Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
   465  						},
   466  						{
   467  							Id:     []byte{0xca, 0xfe, 0xba, 0xbe},
   468  							Values: []*primitive.Value{primitive.NewValue([]byte{5, 6, 7, 8})},
   469  						},
   470  					},
   471  				},
   472  				[]byte{
   473  					byte(primitive.BatchTypeLogged),
   474  					0, 2, // children count
   475  					0,                            // child 1 kind
   476  					0, 0, 0, 6, I, N, S, E, R, T, // child 1 query
   477  					0, 1, // child 1 values count
   478  					0, 0, 0, 4, 1, 2, 3, 4, // child 1 value 1
   479  					1,                            // child 2 kind
   480  					0, 4, 0xca, 0xfe, 0xba, 0xbe, // child 2 query id
   481  					0, 1, // child 2 values count
   482  					0, 0, 0, 4, 5, 6, 7, 8, // child 2 value 1
   483  					0, 0, // consistency level
   484  					0, 0, 0, 0, // flags
   485  				},
   486  				nil,
   487  			},
   488  			{
   489  				"batch with custom options",
   490  				&Batch{
   491  					Type: primitive.BatchTypeUnlogged,
   492  					Children: []*BatchChild{
   493  						{
   494  							Query:  "INSERT",
   495  							Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
   496  						},
   497  					},
   498  					Consistency:       primitive.ConsistencyLevelLocalQuorum,
   499  					SerialConsistency: consistencyLevelPtr(primitive.ConsistencyLevelLocalSerial),
   500  					DefaultTimestamp:  int64Ptr(123),
   501  					Keyspace:          "ks1",
   502  				},
   503  				[]byte{
   504  					byte(primitive.BatchTypeUnlogged),
   505  					0, 1, // children count
   506  					0,                            // child 1 kind
   507  					0, 0, 0, 6, I, N, S, E, R, T, // child 1 query
   508  					0, 1, // child 1 values count
   509  					0, 0, 0, 4, 1, 2, 3, 4, // child 1 value 1
   510  					0, 6, // consistency
   511  					0b0000_0000, // flags
   512  					0b0000_0000, // flags
   513  					0b0000_0000, // flags
   514  					0b1011_0000, // flags => 0x10 (serial) | 0x20 (timestamp) | 0x80 (keyspace)
   515  					0, 9,        // serial consistency
   516  					0, 0, 0, 0, 0, 0, 0, 123, // default timestamp
   517  					0, 3, k, s, _1, // keyspace
   518  				},
   519  				nil,
   520  			},
   521  		}
   522  		for _, tt := range tests {
   523  			t.Run(tt.name, func(t *testing.T) {
   524  				dest := &bytes.Buffer{}
   525  				err := codec.Encode(tt.input, dest, primitive.ProtocolVersionDse2)
   526  				assert.Equal(t, tt.expected, dest.Bytes())
   527  				assert.Equal(t, tt.err, err)
   528  			})
   529  		}
   530  	})
   531  }
   532  
   533  func TestBatchCodec_EncodedLength(t *testing.T) {
   534  	codec := &batchCodec{}
   535  	// version = 2
   536  	t.Run(primitive.ProtocolVersion2.String(), func(t *testing.T) {
   537  		tests := []encodedLengthTestCase{
   538  			{
   539  				"not a batch",
   540  				&AuthChallenge{[]byte{0xca, 0xfe, 0xba, 0xbe}},
   541  				-1,
   542  				errors.New("expected *message.Batch, got *message.AuthChallenge"),
   543  			},
   544  			{
   545  				"empty batch",
   546  				&Batch{},
   547  				primitive.LengthOfByte +
   548  					primitive.LengthOfShort + // children count
   549  					primitive.LengthOfShort, // consistency
   550  				nil,
   551  			},
   552  			{
   553  				"batch with 2 children",
   554  				&Batch{
   555  					Children: []*BatchChild{
   556  						{
   557  							Query:  "INSERT",
   558  							Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
   559  						},
   560  						{
   561  							Id:     []byte{0xca, 0xfe, 0xba, 0xbe},
   562  							Values: []*primitive.Value{primitive.NewValue([]byte{5, 6, 7, 8})},
   563  						},
   564  					},
   565  				},
   566  				primitive.LengthOfByte +
   567  					primitive.LengthOfShort + // children count
   568  					primitive.LengthOfByte + // child 1 kind
   569  					primitive.LengthOfLongString("INSERT") + // child 1 query
   570  					primitive.LengthOfShort + // child values count
   571  					primitive.LengthOfInt + len([]byte{1, 2, 3, 4}) + // child 1 value 1
   572  					primitive.LengthOfByte + // child 2 kind
   573  					primitive.LengthOfShortBytes([]byte{0xca, 0xfe, 0xba, 0xbe}) + // child 1 query
   574  					primitive.LengthOfShort + // child values count
   575  					primitive.LengthOfInt + len([]byte{1, 2, 3, 4}) + // child 2 value 1
   576  					primitive.LengthOfShort, // consistency
   577  				nil,
   578  			},
   579  		}
   580  		for _, tt := range tests {
   581  			t.Run(tt.name, func(t *testing.T) {
   582  				actual, err := codec.EncodedLength(tt.input, primitive.ProtocolVersion2)
   583  				assert.Equal(t, tt.expected, actual)
   584  				assert.Equal(t, tt.err, err)
   585  			})
   586  		}
   587  	})
   588  	// versions = 3, 4
   589  	for _, version := range []primitive.ProtocolVersion{primitive.ProtocolVersion3, primitive.ProtocolVersion4} {
   590  		t.Run(version.String(), func(t *testing.T) {
   591  			tests := []encodedLengthTestCase{
   592  				{
   593  					"not a batch",
   594  					&AuthChallenge{[]byte{0xca, 0xfe, 0xba, 0xbe}},
   595  					-1,
   596  					errors.New("expected *message.Batch, got *message.AuthChallenge"),
   597  				},
   598  				{
   599  					"empty batch",
   600  					&Batch{},
   601  					primitive.LengthOfByte +
   602  						primitive.LengthOfShort + // children count
   603  						primitive.LengthOfShort + // consistency
   604  						primitive.LengthOfByte, // flags
   605  					nil,
   606  				},
   607  				{
   608  					"batch with 2 children",
   609  					&Batch{
   610  						Children: []*BatchChild{
   611  							{
   612  								Query:  "INSERT",
   613  								Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
   614  							},
   615  							{
   616  								Id:     []byte{0xca, 0xfe, 0xba, 0xbe},
   617  								Values: []*primitive.Value{primitive.NewValue([]byte{5, 6, 7, 8})},
   618  							},
   619  						},
   620  					},
   621  					primitive.LengthOfByte +
   622  						primitive.LengthOfShort + // children count
   623  						primitive.LengthOfByte + // child 1 kind
   624  						primitive.LengthOfLongString("INSERT") + // child 1 query
   625  						primitive.LengthOfShort + // child values count
   626  						primitive.LengthOfInt + len([]byte{1, 2, 3, 4}) + // child 1 value 1
   627  						primitive.LengthOfByte + // child 2 kind
   628  						primitive.LengthOfShortBytes([]byte{0xca, 0xfe, 0xba, 0xbe}) + // child 1 query
   629  						primitive.LengthOfShort + // child values count
   630  						primitive.LengthOfInt + len([]byte{1, 2, 3, 4}) + // child 2 value 1
   631  						primitive.LengthOfShort + // consistency
   632  						primitive.LengthOfByte, // flags
   633  					nil,
   634  				},
   635  				{
   636  					"batch with custom options",
   637  					&Batch{
   638  						Type: primitive.BatchTypeUnlogged,
   639  						Children: []*BatchChild{
   640  							{
   641  								Query:  "INSERT",
   642  								Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
   643  							},
   644  						},
   645  						Consistency:       primitive.ConsistencyLevelLocalQuorum,
   646  						SerialConsistency: consistencyLevelPtr(primitive.ConsistencyLevelLocalSerial),
   647  						DefaultTimestamp:  int64Ptr(123),
   648  					},
   649  					primitive.LengthOfByte +
   650  						primitive.LengthOfShort + // children count
   651  						primitive.LengthOfByte + // child 1 kind
   652  						primitive.LengthOfLongString("INSERT") + // child 1 query
   653  						primitive.LengthOfShort + // child values count
   654  						primitive.LengthOfInt + len([]byte{1, 2, 3, 4}) + // child 1 value 1
   655  						primitive.LengthOfShort + // consistency
   656  						primitive.LengthOfByte + // flags
   657  						primitive.LengthOfShort + // serial consistency
   658  						primitive.LengthOfLong, // default timestamp
   659  					nil,
   660  				},
   661  			}
   662  			for _, tt := range tests {
   663  				t.Run(tt.name, func(t *testing.T) {
   664  					actual, err := codec.EncodedLength(tt.input, version)
   665  					assert.Equal(t, tt.expected, actual)
   666  					assert.Equal(t, tt.err, err)
   667  				})
   668  			}
   669  		})
   670  	}
   671  	// version = 5
   672  	t.Run(primitive.ProtocolVersion5.String(), func(t *testing.T) {
   673  		tests := []encodedLengthTestCase{
   674  			{
   675  				"not a batch",
   676  				&AuthChallenge{[]byte{0xca, 0xfe, 0xba, 0xbe}},
   677  				-1,
   678  				errors.New("expected *message.Batch, got *message.AuthChallenge"),
   679  			},
   680  			{
   681  				"empty batch",
   682  				&Batch{},
   683  				primitive.LengthOfByte +
   684  					primitive.LengthOfShort + // children count
   685  					primitive.LengthOfShort + // consistency
   686  					primitive.LengthOfInt, // flags
   687  				nil,
   688  			},
   689  			{
   690  				"batch with 2 children",
   691  				&Batch{
   692  					Children: []*BatchChild{
   693  						{
   694  							Query:  "INSERT",
   695  							Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
   696  						},
   697  						{
   698  							Id:     []byte{0xca, 0xfe, 0xba, 0xbe},
   699  							Values: []*primitive.Value{primitive.NewValue([]byte{5, 6, 7, 8})},
   700  						},
   701  					},
   702  				},
   703  				primitive.LengthOfByte +
   704  					primitive.LengthOfShort + // children count
   705  					primitive.LengthOfByte + // child 1 kind
   706  					primitive.LengthOfLongString("INSERT") + // child 1 query
   707  					primitive.LengthOfShort + // child values count
   708  					primitive.LengthOfInt + len([]byte{1, 2, 3, 4}) + // child 1 value 1
   709  					primitive.LengthOfByte + // child 2 kind
   710  					primitive.LengthOfShortBytes([]byte{0xca, 0xfe, 0xba, 0xbe}) + // child 1 query
   711  					primitive.LengthOfShort + // child values count
   712  					primitive.LengthOfInt + len([]byte{1, 2, 3, 4}) + // child 2 value 1
   713  					primitive.LengthOfShort + // consistency
   714  					primitive.LengthOfInt, // flags
   715  				nil,
   716  			},
   717  			{
   718  				"batch with custom options",
   719  				&Batch{
   720  					Type: primitive.BatchTypeUnlogged,
   721  					Children: []*BatchChild{
   722  						{
   723  							Query:  "INSERT",
   724  							Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
   725  						},
   726  					},
   727  					Consistency:       primitive.ConsistencyLevelLocalQuorum,
   728  					SerialConsistency: consistencyLevelPtr(primitive.ConsistencyLevelLocalSerial),
   729  					DefaultTimestamp:  int64Ptr(123),
   730  					Keyspace:          "ks1",
   731  					NowInSeconds:      int32Ptr(234),
   732  				},
   733  				primitive.LengthOfByte +
   734  					primitive.LengthOfShort + // children count
   735  					primitive.LengthOfByte + // child 1 kind
   736  					primitive.LengthOfLongString("INSERT") + // child 1 query
   737  					primitive.LengthOfShort + // child values count
   738  					primitive.LengthOfInt + len([]byte{1, 2, 3, 4}) + // child 1 value 1
   739  					primitive.LengthOfShort + // consistency
   740  					primitive.LengthOfInt + // flags
   741  					primitive.LengthOfShort + // serial consistency
   742  					primitive.LengthOfLong + // default timestamp
   743  					primitive.LengthOfString("ks1") + // keyspace
   744  					primitive.LengthOfInt, // now in seconds
   745  				nil,
   746  			},
   747  		}
   748  		for _, tt := range tests {
   749  			t.Run(tt.name, func(t *testing.T) {
   750  				actual, err := codec.EncodedLength(tt.input, primitive.ProtocolVersion5)
   751  				assert.Equal(t, tt.expected, actual)
   752  				assert.Equal(t, tt.err, err)
   753  			})
   754  		}
   755  	})
   756  	// version = DSE v1
   757  	t.Run(primitive.ProtocolVersionDse1.String(), func(t *testing.T) {
   758  		tests := []encodedLengthTestCase{
   759  			{
   760  				"not a batch",
   761  				&AuthChallenge{[]byte{0xca, 0xfe, 0xba, 0xbe}},
   762  				-1,
   763  				errors.New("expected *message.Batch, got *message.AuthChallenge"),
   764  			},
   765  			{
   766  				"empty batch",
   767  				&Batch{},
   768  				primitive.LengthOfByte +
   769  					primitive.LengthOfShort + // children count
   770  					primitive.LengthOfShort + // consistency
   771  					primitive.LengthOfInt, // flags
   772  				nil,
   773  			},
   774  			{
   775  				"batch with 2 children",
   776  				&Batch{
   777  					Children: []*BatchChild{
   778  						{
   779  							Query:  "INSERT",
   780  							Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
   781  						},
   782  						{
   783  							Id:     []byte{0xca, 0xfe, 0xba, 0xbe},
   784  							Values: []*primitive.Value{primitive.NewValue([]byte{5, 6, 7, 8})},
   785  						},
   786  					},
   787  				},
   788  				primitive.LengthOfByte +
   789  					primitive.LengthOfShort + // children count
   790  					primitive.LengthOfByte + // child 1 kind
   791  					primitive.LengthOfLongString("INSERT") + // child 1 query
   792  					primitive.LengthOfShort + // child values count
   793  					primitive.LengthOfInt + len([]byte{1, 2, 3, 4}) + // child 1 value 1
   794  					primitive.LengthOfByte + // child 2 kind
   795  					primitive.LengthOfShortBytes([]byte{0xca, 0xfe, 0xba, 0xbe}) + // child 1 query
   796  					primitive.LengthOfShort + // child values count
   797  					primitive.LengthOfInt + len([]byte{1, 2, 3, 4}) + // child 2 value 1
   798  					primitive.LengthOfShort + // consistency
   799  					primitive.LengthOfInt, // flags
   800  				nil,
   801  			},
   802  			{
   803  				"batch with custom options",
   804  				&Batch{
   805  					Type: primitive.BatchTypeUnlogged,
   806  					Children: []*BatchChild{
   807  						{
   808  							Query:  "INSERT",
   809  							Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
   810  						},
   811  					},
   812  					Consistency:       primitive.ConsistencyLevelLocalQuorum,
   813  					SerialConsistency: consistencyLevelPtr(primitive.ConsistencyLevelLocalSerial),
   814  					DefaultTimestamp:  int64Ptr(123),
   815  				},
   816  				primitive.LengthOfByte +
   817  					primitive.LengthOfShort + // children count
   818  					primitive.LengthOfByte + // child 1 kind
   819  					primitive.LengthOfLongString("INSERT") + // child 1 query
   820  					primitive.LengthOfShort + // child values count
   821  					primitive.LengthOfInt + len([]byte{1, 2, 3, 4}) + // child 1 value 1
   822  					primitive.LengthOfShort + // consistency
   823  					primitive.LengthOfInt + // flags
   824  					primitive.LengthOfShort + // serial consistency
   825  					primitive.LengthOfLong, // default timestamp
   826  				nil,
   827  			},
   828  		}
   829  		for _, tt := range tests {
   830  			t.Run(tt.name, func(t *testing.T) {
   831  				actual, err := codec.EncodedLength(tt.input, primitive.ProtocolVersionDse1)
   832  				assert.Equal(t, tt.expected, actual)
   833  				assert.Equal(t, tt.err, err)
   834  			})
   835  		}
   836  	})
   837  	// version = DSE v2
   838  	t.Run(primitive.ProtocolVersionDse2.String(), func(t *testing.T) {
   839  		tests := []encodedLengthTestCase{
   840  			{
   841  				"not a batch",
   842  				&AuthChallenge{[]byte{0xca, 0xfe, 0xba, 0xbe}},
   843  				-1,
   844  				errors.New("expected *message.Batch, got *message.AuthChallenge"),
   845  			},
   846  			{
   847  				"empty batch",
   848  				&Batch{},
   849  				primitive.LengthOfByte +
   850  					primitive.LengthOfShort + // children count
   851  					primitive.LengthOfShort + // consistency
   852  					primitive.LengthOfInt, // flags
   853  				nil,
   854  			},
   855  			{
   856  				"batch with 2 children",
   857  				&Batch{
   858  					Children: []*BatchChild{
   859  						{
   860  							Query:  "INSERT",
   861  							Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
   862  						},
   863  						{
   864  							Id:     []byte{0xca, 0xfe, 0xba, 0xbe},
   865  							Values: []*primitive.Value{primitive.NewValue([]byte{5, 6, 7, 8})},
   866  						},
   867  					},
   868  				},
   869  				primitive.LengthOfByte +
   870  					primitive.LengthOfShort + // children count
   871  					primitive.LengthOfByte + // child 1 kind
   872  					primitive.LengthOfLongString("INSERT") + // child 1 query
   873  					primitive.LengthOfShort + // child values count
   874  					primitive.LengthOfInt + len([]byte{1, 2, 3, 4}) + // child 1 value 1
   875  					primitive.LengthOfByte + // child 2 kind
   876  					primitive.LengthOfShortBytes([]byte{0xca, 0xfe, 0xba, 0xbe}) + // child 1 query
   877  					primitive.LengthOfShort + // child values count
   878  					primitive.LengthOfInt + len([]byte{1, 2, 3, 4}) + // child 2 value 1
   879  					primitive.LengthOfShort + // consistency
   880  					primitive.LengthOfInt, // flags
   881  				nil,
   882  			},
   883  			{
   884  				"batch with custom options",
   885  				&Batch{
   886  					Type: primitive.BatchTypeUnlogged,
   887  					Children: []*BatchChild{
   888  						{
   889  							Query:  "INSERT",
   890  							Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
   891  						},
   892  					},
   893  					Consistency:       primitive.ConsistencyLevelLocalQuorum,
   894  					SerialConsistency: consistencyLevelPtr(primitive.ConsistencyLevelLocalSerial),
   895  					DefaultTimestamp:  int64Ptr(123),
   896  					Keyspace:          "ks1",
   897  				},
   898  				primitive.LengthOfByte +
   899  					primitive.LengthOfShort + // children count
   900  					primitive.LengthOfByte + // child 1 kind
   901  					primitive.LengthOfLongString("INSERT") + // child 1 query
   902  					primitive.LengthOfShort + // child values count
   903  					primitive.LengthOfInt + len([]byte{1, 2, 3, 4}) + // child 1 value 1
   904  					primitive.LengthOfShort + // consistency
   905  					primitive.LengthOfInt + // flags
   906  					primitive.LengthOfShort + // serial consistency
   907  					primitive.LengthOfLong + // default timestamp
   908  					primitive.LengthOfString("ks1"), // keyspace
   909  				nil,
   910  			},
   911  		}
   912  		for _, tt := range tests {
   913  			t.Run(tt.name, func(t *testing.T) {
   914  				actual, err := codec.EncodedLength(tt.input, primitive.ProtocolVersionDse2)
   915  				assert.Equal(t, tt.expected, actual)
   916  				assert.Equal(t, tt.err, err)
   917  			})
   918  		}
   919  	})
   920  }
   921  
   922  func TestBatchCodec_Decode(t *testing.T) {
   923  	codec := &batchCodec{}
   924  	// version = 2
   925  	t.Run(primitive.ProtocolVersion2.String(), func(t *testing.T) {
   926  		tests := []decodeTestCase{
   927  			{
   928  				"invalid batch type",
   929  				[]byte{
   930  					42,   // bach type
   931  					0, 0, // children count
   932  					0, 0, // consistency level
   933  					0, // flags
   934  				},
   935  				nil,
   936  				errors.New("invalid BATCH type: BatchType ? [0X2A]"),
   937  			},
   938  			{
   939  				"empty batch",
   940  				[]byte{
   941  					byte(primitive.BatchTypeLogged),
   942  					0, 0, // children count
   943  					0, 0, // consistency level
   944  				},
   945  				&Batch{Children: []*BatchChild{}},
   946  				nil,
   947  			},
   948  			{
   949  				"batch with 2 children",
   950  				[]byte{
   951  					byte(primitive.BatchTypeLogged),
   952  					0, 2, // children count
   953  					0,                            // child 1 kind
   954  					0, 0, 0, 6, I, N, S, E, R, T, // child 1 query
   955  					0, 1, // child 1 values count
   956  					0, 0, 0, 4, 1, 2, 3, 4, // child 1 value 1
   957  					1,                            // child 2 kind
   958  					0, 4, 0xca, 0xfe, 0xba, 0xbe, // child 2 query id
   959  					0, 1, // child 2 values count
   960  					0, 0, 0, 4, 5, 6, 7, 8, // child 2 value 1
   961  					0, 0, // consistency level
   962  				},
   963  				&Batch{
   964  					Children: []*BatchChild{
   965  						{
   966  							Query:  "INSERT",
   967  							Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
   968  						},
   969  						{
   970  							Id:     []byte{0xca, 0xfe, 0xba, 0xbe},
   971  							Values: []*primitive.Value{primitive.NewValue([]byte{5, 6, 7, 8})},
   972  						},
   973  					},
   974  				},
   975  				nil,
   976  			},
   977  		}
   978  		for _, tt := range tests {
   979  			t.Run(tt.name, func(t *testing.T) {
   980  				source := bytes.NewBuffer(tt.input)
   981  				actual, err := codec.Decode(source, primitive.ProtocolVersion2)
   982  				assert.Equal(t, tt.expected, actual)
   983  				assert.Equal(t, tt.err, err)
   984  			})
   985  		}
   986  	})
   987  	// versions = 3, 4
   988  	for _, version := range []primitive.ProtocolVersion{primitive.ProtocolVersion3, primitive.ProtocolVersion4} {
   989  		t.Run(version.String(), func(t *testing.T) {
   990  			tests := []decodeTestCase{
   991  				{
   992  					"invalid batch type",
   993  					[]byte{
   994  						42,   // bach type
   995  						0, 0, // children count
   996  						0, 0, // consistency level
   997  						0, // flags
   998  					},
   999  					nil,
  1000  					errors.New("invalid BATCH type: BatchType ? [0X2A]"),
  1001  				},
  1002  				{
  1003  					"empty batch",
  1004  					[]byte{
  1005  						byte(primitive.BatchTypeLogged),
  1006  						0, 0, // children count
  1007  						0, 0, // consistency level
  1008  						0, // flags
  1009  					},
  1010  					&Batch{Children: []*BatchChild{}},
  1011  					nil,
  1012  				},
  1013  				{
  1014  					"batch with 2 children",
  1015  					[]byte{
  1016  						byte(primitive.BatchTypeLogged),
  1017  						0, 2, // children count
  1018  						0,                            // child 1 kind
  1019  						0, 0, 0, 6, I, N, S, E, R, T, // child 1 query
  1020  						0, 1, // child 1 values count
  1021  						0, 0, 0, 4, 1, 2, 3, 4, // child 1 value 1
  1022  						1,                            // child 2 kind
  1023  						0, 4, 0xca, 0xfe, 0xba, 0xbe, // child 2 query id
  1024  						0, 1, // child 2 values count
  1025  						0, 0, 0, 4, 5, 6, 7, 8, // child 2 value 1
  1026  						0, 0, // consistency level
  1027  						0, // flags
  1028  					},
  1029  					&Batch{
  1030  						Children: []*BatchChild{
  1031  							{
  1032  								Query:  "INSERT",
  1033  								Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
  1034  							},
  1035  							{
  1036  								Id:     []byte{0xca, 0xfe, 0xba, 0xbe},
  1037  								Values: []*primitive.Value{primitive.NewValue([]byte{5, 6, 7, 8})},
  1038  							},
  1039  						},
  1040  					},
  1041  					nil,
  1042  				},
  1043  				{
  1044  					"batch with custom options",
  1045  					[]byte{
  1046  						byte(primitive.BatchTypeUnlogged),
  1047  						0, 1, // children count
  1048  						0,                            // child 1 kind
  1049  						0, 0, 0, 6, I, N, S, E, R, T, // child 1 query
  1050  						0, 1, // child 1 values count
  1051  						0, 0, 0, 4, 1, 2, 3, 4, // child 1 value 1
  1052  						0, 6, // consistency
  1053  						0b0011_0000, // flags
  1054  						0, 9,        // serial consistency
  1055  						0, 0, 0, 0, 0, 0, 0, 123, // default timestamp
  1056  					},
  1057  					&Batch{
  1058  						Type: primitive.BatchTypeUnlogged,
  1059  						Children: []*BatchChild{
  1060  							{
  1061  								Query:  "INSERT",
  1062  								Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
  1063  							},
  1064  						},
  1065  						Consistency:       primitive.ConsistencyLevelLocalQuorum,
  1066  						SerialConsistency: consistencyLevelPtr(primitive.ConsistencyLevelLocalSerial),
  1067  						DefaultTimestamp:  int64Ptr(123),
  1068  					},
  1069  					nil,
  1070  				},
  1071  			}
  1072  			for _, tt := range tests {
  1073  				t.Run(tt.name, func(t *testing.T) {
  1074  					source := bytes.NewBuffer(tt.input)
  1075  					actual, err := codec.Decode(source, version)
  1076  					assert.Equal(t, tt.expected, actual)
  1077  					assert.Equal(t, tt.err, err)
  1078  				})
  1079  			}
  1080  		})
  1081  	}
  1082  	// version = 5
  1083  	t.Run(primitive.ProtocolVersion5.String(), func(t *testing.T) {
  1084  		tests := []decodeTestCase{
  1085  			{
  1086  				"invalid batch type",
  1087  				[]byte{
  1088  					42,   // bach type
  1089  					0, 0, // children count
  1090  					0, 0, // consistency level
  1091  					0, 0, 0, 0, // flags
  1092  				},
  1093  				nil,
  1094  				errors.New("invalid BATCH type: BatchType ? [0X2A]"),
  1095  			},
  1096  			{
  1097  				"empty batch",
  1098  				[]byte{
  1099  					byte(primitive.BatchTypeLogged),
  1100  					0, 0, // children count
  1101  					0, 0, // consistency level
  1102  					0, 0, 0, 0, // flags
  1103  				},
  1104  				&Batch{Children: []*BatchChild{}},
  1105  				nil,
  1106  			},
  1107  			{
  1108  				"batch with 2 children",
  1109  				[]byte{
  1110  					byte(primitive.BatchTypeLogged),
  1111  					0, 2, // children count
  1112  					0,                            // child 1 kind
  1113  					0, 0, 0, 6, I, N, S, E, R, T, // child 1 query
  1114  					0, 1, // child 1 values count
  1115  					0, 0, 0, 4, 1, 2, 3, 4, // child 1 value 1
  1116  					1,                            // child 2 kind
  1117  					0, 4, 0xca, 0xfe, 0xba, 0xbe, // child 2 query id
  1118  					0, 1, // child 2 values count
  1119  					0, 0, 0, 4, 5, 6, 7, 8, // child 2 value 1
  1120  					0, 0, // consistency level
  1121  					0, 0, 0, 0, // flags
  1122  				},
  1123  				&Batch{
  1124  					Children: []*BatchChild{
  1125  						{
  1126  							Query:  "INSERT",
  1127  							Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
  1128  						},
  1129  						{
  1130  							Id:     []byte{0xca, 0xfe, 0xba, 0xbe},
  1131  							Values: []*primitive.Value{primitive.NewValue([]byte{5, 6, 7, 8})},
  1132  						},
  1133  					},
  1134  				},
  1135  				nil,
  1136  			},
  1137  			{
  1138  				"batch with custom options",
  1139  				[]byte{
  1140  					byte(primitive.BatchTypeUnlogged),
  1141  					0, 1, // children count
  1142  					0,                            // child 1 kind
  1143  					0, 0, 0, 6, I, N, S, E, R, T, // child 1 query
  1144  					0, 1, // child 1 values count
  1145  					0, 0, 0, 4, 1, 2, 3, 4, // child 1 value 1
  1146  					0, 6, // consistency
  1147  					0b0000_0000, // flags
  1148  					0b0000_0000, // flags
  1149  					0b0000_0001, // flags => 0x100 (now in seconds)
  1150  					0b1011_0000, // flags => 0x10 (serial) | 0x20 (timestamp) | 0x80 (keyspace)
  1151  					0, 9,        // serial consistency
  1152  					0, 0, 0, 0, 0, 0, 0, 123, // default timestamp
  1153  					0, 3, k, s, _1, // keyspace
  1154  					0, 0, 0, 234, // now in seconds
  1155  				},
  1156  				&Batch{
  1157  					Type: primitive.BatchTypeUnlogged,
  1158  					Children: []*BatchChild{
  1159  						{
  1160  							Query:  "INSERT",
  1161  							Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
  1162  						},
  1163  					},
  1164  					Consistency:       primitive.ConsistencyLevelLocalQuorum,
  1165  					SerialConsistency: consistencyLevelPtr(primitive.ConsistencyLevelLocalSerial),
  1166  					DefaultTimestamp:  int64Ptr(123),
  1167  					Keyspace:          "ks1",
  1168  					NowInSeconds:      int32Ptr(234),
  1169  				},
  1170  				nil,
  1171  			},
  1172  		}
  1173  		for _, tt := range tests {
  1174  			t.Run(tt.name, func(t *testing.T) {
  1175  				source := bytes.NewBuffer(tt.input)
  1176  				actual, err := codec.Decode(source, primitive.ProtocolVersion5)
  1177  				assert.Equal(t, tt.expected, actual)
  1178  				assert.Equal(t, tt.err, err)
  1179  			})
  1180  		}
  1181  	})
  1182  	// version = DSE v1
  1183  	t.Run(primitive.ProtocolVersionDse1.String(), func(t *testing.T) {
  1184  		tests := []decodeTestCase{
  1185  			{
  1186  				"invalid batch type",
  1187  				[]byte{
  1188  					42,   // bach type
  1189  					0, 0, // children count
  1190  					0, 0, // consistency level
  1191  					0, 0, 0, 0, // flags
  1192  				},
  1193  				nil,
  1194  				errors.New("invalid BATCH type: BatchType ? [0X2A]"),
  1195  			},
  1196  			{
  1197  				"empty batch",
  1198  				[]byte{
  1199  					byte(primitive.BatchTypeLogged),
  1200  					0, 0, // children count
  1201  					0, 0, // consistency level
  1202  					0, 0, 0, 0, // flags
  1203  				},
  1204  				&Batch{Children: []*BatchChild{}},
  1205  				nil,
  1206  			},
  1207  			{
  1208  				"batch with 2 children",
  1209  				[]byte{
  1210  					byte(primitive.BatchTypeLogged),
  1211  					0, 2, // children count
  1212  					0,                            // child 1 kind
  1213  					0, 0, 0, 6, I, N, S, E, R, T, // child 1 query
  1214  					0, 1, // child 1 values count
  1215  					0, 0, 0, 4, 1, 2, 3, 4, // child 1 value 1
  1216  					1,                            // child 2 kind
  1217  					0, 4, 0xca, 0xfe, 0xba, 0xbe, // child 2 query id
  1218  					0, 1, // child 2 values count
  1219  					0, 0, 0, 4, 5, 6, 7, 8, // child 2 value 1
  1220  					0, 0, // consistency level
  1221  					0, 0, 0, 0, // flags
  1222  				},
  1223  				&Batch{
  1224  					Children: []*BatchChild{
  1225  						{
  1226  							Query:  "INSERT",
  1227  							Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
  1228  						},
  1229  						{
  1230  							Id:     []byte{0xca, 0xfe, 0xba, 0xbe},
  1231  							Values: []*primitive.Value{primitive.NewValue([]byte{5, 6, 7, 8})},
  1232  						},
  1233  					},
  1234  				},
  1235  				nil,
  1236  			},
  1237  			{
  1238  				"batch with custom options",
  1239  				[]byte{
  1240  					byte(primitive.BatchTypeUnlogged),
  1241  					0, 1, // children count
  1242  					0,                            // child 1 kind
  1243  					0, 0, 0, 6, I, N, S, E, R, T, // child 1 query
  1244  					0, 1, // child 1 values count
  1245  					0, 0, 0, 4, 1, 2, 3, 4, // child 1 value 1
  1246  					0, 6, // consistency
  1247  					0, 0, 0, 0b0011_0000, // flags
  1248  					0, 9, // serial consistency
  1249  					0, 0, 0, 0, 0, 0, 0, 123, // default timestamp
  1250  				},
  1251  				&Batch{
  1252  					Type: primitive.BatchTypeUnlogged,
  1253  					Children: []*BatchChild{
  1254  						{
  1255  							Query:  "INSERT",
  1256  							Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
  1257  						},
  1258  					},
  1259  					Consistency:       primitive.ConsistencyLevelLocalQuorum,
  1260  					SerialConsistency: consistencyLevelPtr(primitive.ConsistencyLevelLocalSerial),
  1261  					DefaultTimestamp:  int64Ptr(123),
  1262  				},
  1263  				nil,
  1264  			},
  1265  		}
  1266  		for _, tt := range tests {
  1267  			t.Run(tt.name, func(t *testing.T) {
  1268  				source := bytes.NewBuffer(tt.input)
  1269  				actual, err := codec.Decode(source, primitive.ProtocolVersionDse1)
  1270  				assert.Equal(t, tt.expected, actual)
  1271  				assert.Equal(t, tt.err, err)
  1272  			})
  1273  		}
  1274  	})
  1275  	// version = DSE v2
  1276  	t.Run(primitive.ProtocolVersionDse2.String(), func(t *testing.T) {
  1277  		tests := []decodeTestCase{
  1278  			{
  1279  				"invalid batch type",
  1280  				[]byte{
  1281  					42,   // bach type
  1282  					0, 0, // children count
  1283  					0, 0, // consistency level
  1284  					0, 0, 0, 0, // flags
  1285  				},
  1286  				nil,
  1287  				errors.New("invalid BATCH type: BatchType ? [0X2A]"),
  1288  			},
  1289  			{
  1290  				"empty batch",
  1291  				[]byte{
  1292  					byte(primitive.BatchTypeLogged),
  1293  					0, 0, // children count
  1294  					0, 0, // consistency level
  1295  					0, 0, 0, 0, // flags
  1296  				},
  1297  				&Batch{Children: []*BatchChild{}},
  1298  				nil,
  1299  			},
  1300  			{
  1301  				"batch with 2 children",
  1302  				[]byte{
  1303  					byte(primitive.BatchTypeLogged),
  1304  					0, 2, // children count
  1305  					0,                            // child 1 kind
  1306  					0, 0, 0, 6, I, N, S, E, R, T, // child 1 query
  1307  					0, 1, // child 1 values count
  1308  					0, 0, 0, 4, 1, 2, 3, 4, // child 1 value 1
  1309  					1,                            // child 2 kind
  1310  					0, 4, 0xca, 0xfe, 0xba, 0xbe, // child 2 query id
  1311  					0, 1, // child 2 values count
  1312  					0, 0, 0, 4, 5, 6, 7, 8, // child 2 value 1
  1313  					0, 0, // consistency level
  1314  					0, 0, 0, 0, // flags
  1315  				},
  1316  				&Batch{
  1317  					Children: []*BatchChild{
  1318  						{
  1319  							Query:  "INSERT",
  1320  							Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
  1321  						},
  1322  						{
  1323  							Id:     []byte{0xca, 0xfe, 0xba, 0xbe},
  1324  							Values: []*primitive.Value{primitive.NewValue([]byte{5, 6, 7, 8})},
  1325  						},
  1326  					},
  1327  				},
  1328  				nil,
  1329  			},
  1330  			{
  1331  				"batch with custom options",
  1332  				[]byte{
  1333  					byte(primitive.BatchTypeUnlogged),
  1334  					0, 1, // children count
  1335  					0,                            // child 1 kind
  1336  					0, 0, 0, 6, I, N, S, E, R, T, // child 1 query
  1337  					0, 1, // child 1 values count
  1338  					0, 0, 0, 4, 1, 2, 3, 4, // child 1 value 1
  1339  					0, 6, // consistency
  1340  					0b0000_0000, // flags
  1341  					0b0000_0000, // flags
  1342  					0b0000_0000, // flags
  1343  					0b1011_0000, // flags => 0x10 (serial) | 0x20 (timestamp) | 0x80 (keyspace)
  1344  					0, 9,        // serial consistency
  1345  					0, 0, 0, 0, 0, 0, 0, 123, // default timestamp
  1346  					0, 3, k, s, _1, // keyspace
  1347  				},
  1348  				&Batch{
  1349  					Type: primitive.BatchTypeUnlogged,
  1350  					Children: []*BatchChild{
  1351  						{
  1352  							Query:  "INSERT",
  1353  							Values: []*primitive.Value{primitive.NewValue([]byte{1, 2, 3, 4})},
  1354  						},
  1355  					},
  1356  					Consistency:       primitive.ConsistencyLevelLocalQuorum,
  1357  					SerialConsistency: consistencyLevelPtr(primitive.ConsistencyLevelLocalSerial),
  1358  					DefaultTimestamp:  int64Ptr(123),
  1359  					Keyspace:          "ks1",
  1360  				},
  1361  				nil,
  1362  			},
  1363  		}
  1364  		for _, tt := range tests {
  1365  			t.Run(tt.name, func(t *testing.T) {
  1366  				source := bytes.NewBuffer(tt.input)
  1367  				actual, err := codec.Decode(source, primitive.ProtocolVersionDse2)
  1368  				assert.Equal(t, tt.expected, actual)
  1369  				assert.Equal(t, tt.err, err)
  1370  			})
  1371  		}
  1372  	})
  1373  }