github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/roachpb/api_test.go (about)

     1  // Copyright 2014 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package roachpb
    12  
    13  import (
    14  	"reflect"
    15  	"testing"
    16  
    17  	"github.com/stretchr/testify/require"
    18  )
    19  
    20  // TestCombineResponses tests the behavior of the CombineResponses function,
    21  // which attempts to combine two provided responses.
    22  func TestCombineResponses(t *testing.T) {
    23  	t.Run("both combinable", func(t *testing.T) {
    24  		left := &ScanResponse{
    25  			Rows: []KeyValue{
    26  				{Key: Key("A"), Value: MakeValueFromString("V")},
    27  			},
    28  			IntentRows: []KeyValue{
    29  				{Key: Key("Ai"), Value: MakeValueFromString("X")},
    30  			},
    31  		}
    32  		right := &ScanResponse{
    33  			Rows: []KeyValue{
    34  				{Key: Key("B"), Value: MakeValueFromString("W")},
    35  			},
    36  			IntentRows: []KeyValue{
    37  				{Key: Key("Bi"), Value: MakeValueFromString("Z")},
    38  			},
    39  		}
    40  		expCombined := &ScanResponse{
    41  			Rows:       append(append([]KeyValue(nil), left.Rows...), right.Rows...),
    42  			IntentRows: append(append([]KeyValue(nil), left.IntentRows...), right.IntentRows...),
    43  		}
    44  
    45  		err := CombineResponses(left, right)
    46  		require.NoError(t, err)
    47  		require.Equal(t, expCombined, left)
    48  	})
    49  
    50  	t.Run("neither combinable", func(t *testing.T) {
    51  		left := &GetResponse{
    52  			Value: &Value{RawBytes: []byte("V")},
    53  		}
    54  		right := &GetResponse{
    55  			Value: &Value{RawBytes: []byte("W")},
    56  		}
    57  		expCombined := &GetResponse{
    58  			Value: left.Value.ShallowClone(),
    59  		}
    60  
    61  		err := CombineResponses(left, right)
    62  		require.NoError(t, err)
    63  		require.Equal(t, expCombined, left)
    64  	})
    65  
    66  	t.Run("left combinable", func(t *testing.T) {
    67  		left := &ScanResponse{
    68  			Rows: []KeyValue{
    69  				{Key: Key("A"), Value: MakeValueFromString("V")},
    70  			},
    71  			IntentRows: []KeyValue{
    72  				{Key: Key("Ai"), Value: MakeValueFromString("X")},
    73  			},
    74  		}
    75  		right := &GetResponse{
    76  			Value: &Value{RawBytes: []byte("W")},
    77  		}
    78  
    79  		err := CombineResponses(left, right)
    80  		require.Error(t, err)
    81  		require.Regexp(t, "can not combine", err)
    82  	})
    83  
    84  	t.Run("right combinable", func(t *testing.T) {
    85  		left := &GetResponse{
    86  			Value: &Value{RawBytes: []byte("V")},
    87  		}
    88  		right := &ScanResponse{
    89  			Rows: []KeyValue{
    90  				{Key: Key("B"), Value: MakeValueFromString("W")},
    91  			},
    92  			IntentRows: []KeyValue{
    93  				{Key: Key("Bi"), Value: MakeValueFromString("Z")},
    94  			},
    95  		}
    96  
    97  		err := CombineResponses(left, right)
    98  		require.Error(t, err)
    99  		require.Regexp(t, "can not combine", err)
   100  	})
   101  }
   102  
   103  // TestCombinable tests the correct behavior of some types that implement
   104  // the combinable interface, notably {Scan,DeleteRange}Response and
   105  // ResponseHeader.
   106  func TestCombinable(t *testing.T) {
   107  	t.Run("Get", func(t *testing.T) {
   108  		// Test that GetResponse doesn't have anything to do with combinable.
   109  		if _, ok := interface{}(&GetResponse{}).(combinable); ok {
   110  			t.Fatalf("GetResponse implements combinable, so presumably all Response types will")
   111  		}
   112  	})
   113  
   114  	t.Run("Scan", func(t *testing.T) {
   115  
   116  		// Test that {Scan,DeleteRange}Response properly implement it.
   117  		sr1 := &ScanResponse{
   118  			Rows: []KeyValue{
   119  				{Key: Key("A"), Value: MakeValueFromString("V")},
   120  			},
   121  			IntentRows: []KeyValue{
   122  				{Key: Key("Ai"), Value: MakeValueFromString("X")},
   123  			},
   124  		}
   125  
   126  		if _, ok := interface{}(sr1).(combinable); !ok {
   127  			t.Fatalf("ScanResponse does not implement combinable")
   128  		}
   129  
   130  		sr2 := &ScanResponse{
   131  			Rows: []KeyValue{
   132  				{Key: Key("B"), Value: MakeValueFromString("W")},
   133  			},
   134  			IntentRows: []KeyValue{
   135  				{Key: Key("Bi"), Value: MakeValueFromString("Z")},
   136  			},
   137  		}
   138  
   139  		wantedSR := &ScanResponse{
   140  			Rows:       append(append([]KeyValue(nil), sr1.Rows...), sr2.Rows...),
   141  			IntentRows: append(append([]KeyValue(nil), sr1.IntentRows...), sr2.IntentRows...),
   142  		}
   143  
   144  		if err := sr1.combine(sr2); err != nil {
   145  			t.Fatal(err)
   146  		}
   147  		if err := sr1.combine(&ScanResponse{}); err != nil {
   148  			t.Fatal(err)
   149  		}
   150  
   151  		if !reflect.DeepEqual(sr1, wantedSR) {
   152  			t.Errorf("wanted %v, got %v", wantedSR, sr1)
   153  		}
   154  	})
   155  
   156  	t.Run("DeleteRange", func(t *testing.T) {
   157  		dr1 := &DeleteRangeResponse{
   158  			Keys: []Key{[]byte("1")},
   159  		}
   160  		if _, ok := interface{}(dr1).(combinable); !ok {
   161  			t.Fatalf("DeleteRangeResponse does not implement combinable")
   162  		}
   163  		dr2 := &DeleteRangeResponse{
   164  			Keys: []Key{[]byte("2")},
   165  		}
   166  		dr3 := &DeleteRangeResponse{
   167  			Keys: nil,
   168  		}
   169  		wantedDR := &DeleteRangeResponse{
   170  			Keys: []Key{[]byte("1"), []byte("2")},
   171  		}
   172  		if err := dr2.combine(dr3); err != nil {
   173  			t.Fatal(err)
   174  		}
   175  		if err := dr1.combine(dr2); err != nil {
   176  			t.Fatal(err)
   177  		}
   178  
   179  		if !reflect.DeepEqual(dr1, wantedDR) {
   180  			t.Errorf("wanted %v, got %v", wantedDR, dr1)
   181  		}
   182  	})
   183  
   184  	t.Run("AdminVerifyProtectedTimestamp", func(t *testing.T) {
   185  		v1 := &AdminVerifyProtectedTimestampResponse{
   186  			ResponseHeader: ResponseHeader{},
   187  			Verified:       false,
   188  			FailedRanges: []RangeDescriptor{
   189  				{RangeID: 1},
   190  			},
   191  		}
   192  
   193  		if _, ok := interface{}(v1).(combinable); !ok {
   194  			t.Fatal("AdminVerifyProtectedTimestampResponse unexpectedly does not implement combinable")
   195  		}
   196  		v2 := &AdminVerifyProtectedTimestampResponse{
   197  			ResponseHeader: ResponseHeader{},
   198  			Verified:       true,
   199  			FailedRanges:   nil,
   200  		}
   201  		v3 := &AdminVerifyProtectedTimestampResponse{
   202  			ResponseHeader: ResponseHeader{},
   203  			Verified:       false,
   204  			FailedRanges: []RangeDescriptor{
   205  				{RangeID: 2},
   206  			},
   207  		}
   208  		require.NoError(t, v1.combine(v2))
   209  		require.NoError(t, v1.combine(v3))
   210  		require.EqualValues(t, &AdminVerifyProtectedTimestampResponse{
   211  			Verified: false,
   212  			FailedRanges: []RangeDescriptor{
   213  				{RangeID: 1},
   214  				{RangeID: 2},
   215  			},
   216  		}, v1)
   217  
   218  	})
   219  }
   220  
   221  // TestMustSetInner makes sure that calls to MustSetInner correctly reset the
   222  // union before repopulating to avoid having more than one value set.
   223  func TestMustSetInner(t *testing.T) {
   224  	req := RequestUnion{}
   225  	res := ResponseUnion{}
   226  
   227  	// GetRequest is checked first in the generated code for SetInner.
   228  	req.MustSetInner(&GetRequest{})
   229  	res.MustSetInner(&GetResponse{})
   230  	req.MustSetInner(&EndTxnRequest{})
   231  	res.MustSetInner(&EndTxnResponse{})
   232  
   233  	if m := req.GetInner().Method(); m != EndTxn {
   234  		t.Fatalf("unexpected request: %s in %+v", m, req)
   235  	}
   236  	if _, isET := res.GetInner().(*EndTxnResponse); !isET {
   237  		t.Fatalf("unexpected response union: %+v", res)
   238  	}
   239  }