vitess.io/vitess@v0.16.2/go/vt/binlog/binlogplayertest/player.go (about)

     1  /*
     2  Copyright 2019 The Vitess Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package binlogplayertest
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  	"strings"
    23  	"testing"
    24  
    25  	"context"
    26  
    27  	"google.golang.org/protobuf/proto"
    28  
    29  	"vitess.io/vitess/go/vt/binlog/binlogplayer"
    30  	"vitess.io/vitess/go/vt/key"
    31  
    32  	binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata"
    33  	querypb "vitess.io/vitess/go/vt/proto/query"
    34  	topodatapb "vitess.io/vitess/go/vt/proto/topodata"
    35  )
    36  
    37  // keyRangeRequest is used to make a request for StreamKeyRange.
    38  type keyRangeRequest struct {
    39  	Position string
    40  	KeyRange *topodatapb.KeyRange
    41  	Charset  *binlogdatapb.Charset
    42  }
    43  
    44  // tablesRequest is used to make a request for StreamTables.
    45  type tablesRequest struct {
    46  	Position string
    47  	Tables   []string
    48  	Charset  *binlogdatapb.Charset
    49  }
    50  
    51  // FakeBinlogStreamer is our implementation of UpdateStream
    52  type FakeBinlogStreamer struct {
    53  	t      *testing.T
    54  	panics bool
    55  }
    56  
    57  // NewFakeBinlogStreamer returns the test instance for UpdateStream
    58  func NewFakeBinlogStreamer(t *testing.T) *FakeBinlogStreamer {
    59  	return &FakeBinlogStreamer{
    60  		t:      t,
    61  		panics: false,
    62  	}
    63  }
    64  
    65  //
    66  // StreamKeyRange tests
    67  //
    68  
    69  var testKeyRangeRequest = &keyRangeRequest{
    70  	Position: "KeyRange starting position",
    71  	KeyRange: &topodatapb.KeyRange{
    72  		Start: key.Uint64Key(0x7000000000000000).Bytes(),
    73  		End:   key.Uint64Key(0x9000000000000000).Bytes(),
    74  	},
    75  	Charset: &binlogdatapb.Charset{
    76  		Client: 12,
    77  		Conn:   13,
    78  		Server: 14,
    79  	},
    80  }
    81  
    82  var testBinlogTransaction = &binlogdatapb.BinlogTransaction{
    83  	Statements: []*binlogdatapb.BinlogTransaction_Statement{
    84  		{
    85  			Category: binlogdatapb.BinlogTransaction_Statement_BL_ROLLBACK,
    86  			Charset: &binlogdatapb.Charset{
    87  				Client: 120,
    88  				Conn:   130,
    89  				Server: 140,
    90  			},
    91  			Sql: []byte("my statement"),
    92  		},
    93  	},
    94  	EventToken: &querypb.EventToken{
    95  		Timestamp: 78,
    96  		Position:  "BinlogTransaction returned position",
    97  	},
    98  }
    99  
   100  // StreamKeyRange is part of the UpdateStream interface
   101  func (fake *FakeBinlogStreamer) StreamKeyRange(ctx context.Context, position string, keyRange *topodatapb.KeyRange, charset *binlogdatapb.Charset, callback func(reply *binlogdatapb.BinlogTransaction) error) error {
   102  	if fake.panics {
   103  		panic(fmt.Errorf("test-triggered panic"))
   104  	}
   105  	req := &keyRangeRequest{
   106  		Position: position,
   107  		KeyRange: keyRange,
   108  		Charset:  charset,
   109  	}
   110  	if position != testKeyRangeRequest.Position ||
   111  		!proto.Equal(keyRange, testKeyRangeRequest.KeyRange) ||
   112  		!proto.Equal(charset, testKeyRangeRequest.Charset) {
   113  		fake.t.Errorf("wrong StreamKeyRange parameter, got %+v want %+v", req, testKeyRangeRequest)
   114  	}
   115  	if err := callback(testBinlogTransaction); err != nil {
   116  		fake.t.Logf("StreamKeyRange callback failed: %v", err)
   117  	}
   118  	return nil
   119  }
   120  
   121  func testStreamKeyRange(t *testing.T, bpc binlogplayer.Client) {
   122  	ctx := context.Background()
   123  	stream, err := bpc.StreamKeyRange(ctx, testKeyRangeRequest.Position, testKeyRangeRequest.KeyRange, testKeyRangeRequest.Charset)
   124  	if err != nil {
   125  		t.Fatalf("got error: %v", err)
   126  	}
   127  	if se, err := stream.Recv(); err != nil {
   128  		t.Fatalf("got error: %v", err)
   129  	} else {
   130  		if !proto.Equal(se, testBinlogTransaction) {
   131  			t.Errorf("got wrong result, got %v expected %v", se, testBinlogTransaction)
   132  		}
   133  	}
   134  	if se, err := stream.Recv(); err == nil {
   135  		t.Fatalf("got a response when error expected: %v", se)
   136  	}
   137  }
   138  
   139  func testStreamKeyRangePanics(t *testing.T, bpc binlogplayer.Client) {
   140  	ctx := context.Background()
   141  	stream, err := bpc.StreamKeyRange(ctx, testKeyRangeRequest.Position, testKeyRangeRequest.KeyRange, testKeyRangeRequest.Charset)
   142  	if err != nil {
   143  		t.Fatalf("got error: %v", err)
   144  	}
   145  	if se, err := stream.Recv(); err == nil {
   146  		t.Fatalf("got a response when error expected: %v", se)
   147  	} else {
   148  		if !strings.Contains(err.Error(), "test-triggered panic") {
   149  			t.Errorf("wrong error from panic: %v", err)
   150  		}
   151  	}
   152  }
   153  
   154  //
   155  // StreamTables test
   156  //
   157  
   158  var testTablesRequest = &tablesRequest{
   159  	Position: "Tables starting position",
   160  	Tables:   []string{"table1", "table2"},
   161  	Charset: &binlogdatapb.Charset{
   162  		Client: 12,
   163  		Conn:   13,
   164  		Server: 14,
   165  	},
   166  }
   167  
   168  // StreamTables is part of the UpdateStream interface
   169  func (fake *FakeBinlogStreamer) StreamTables(ctx context.Context, position string, tables []string, charset *binlogdatapb.Charset, callback func(reply *binlogdatapb.BinlogTransaction) error) error {
   170  	if fake.panics {
   171  		panic(fmt.Errorf("test-triggered panic"))
   172  	}
   173  	req := &tablesRequest{
   174  		Position: position,
   175  		Tables:   tables,
   176  		Charset:  charset,
   177  	}
   178  	if position != testTablesRequest.Position ||
   179  		!reflect.DeepEqual(tables, testTablesRequest.Tables) ||
   180  		!proto.Equal(charset, testTablesRequest.Charset) {
   181  		fake.t.Errorf("wrong StreamTables parameter, got %+v want %+v", req, testTablesRequest)
   182  	}
   183  	if err := callback(testBinlogTransaction); err != nil {
   184  		fake.t.Logf("StreamTables callback failed: %v", err)
   185  	}
   186  	return nil
   187  }
   188  
   189  func testStreamTables(t *testing.T, bpc binlogplayer.Client) {
   190  	ctx := context.Background()
   191  	stream, err := bpc.StreamTables(ctx, testTablesRequest.Position, testTablesRequest.Tables, testTablesRequest.Charset)
   192  	if err != nil {
   193  		t.Fatalf("got error: %v", err)
   194  	}
   195  	if se, err := stream.Recv(); err != nil {
   196  		t.Fatalf("got error: %v", err)
   197  	} else {
   198  		if !proto.Equal(se, testBinlogTransaction) {
   199  			t.Errorf("got wrong result, got %v expected %v", se, testBinlogTransaction)
   200  		}
   201  	}
   202  	if se, err := stream.Recv(); err == nil {
   203  		t.Fatalf("got a response when error expected: %v", se)
   204  	}
   205  }
   206  
   207  func testStreamTablesPanics(t *testing.T, bpc binlogplayer.Client) {
   208  	ctx := context.Background()
   209  	stream, err := bpc.StreamTables(ctx, testTablesRequest.Position, testTablesRequest.Tables, testTablesRequest.Charset)
   210  	if err != nil {
   211  		t.Fatalf("got error: %v", err)
   212  	}
   213  	if se, err := stream.Recv(); err == nil {
   214  		t.Fatalf("got a response when error expected: %v", se)
   215  	} else {
   216  		if !strings.Contains(err.Error(), "test-triggered panic") {
   217  			t.Errorf("wrong error from panic: %v", err)
   218  		}
   219  	}
   220  }
   221  
   222  // HandlePanic is part of the UpdateStream interface
   223  func (fake *FakeBinlogStreamer) HandlePanic(err *error) {
   224  	if x := recover(); x != nil {
   225  		*err = fmt.Errorf("caught panic: %v", x)
   226  	}
   227  }
   228  
   229  // Run runs the test suite
   230  func Run(t *testing.T, bpc binlogplayer.Client, tablet *topodatapb.Tablet, fake *FakeBinlogStreamer) {
   231  	if err := bpc.Dial(tablet); err != nil {
   232  		t.Fatalf("Dial failed: %v", err)
   233  	}
   234  
   235  	// no panic
   236  	testStreamKeyRange(t, bpc)
   237  	testStreamTables(t, bpc)
   238  
   239  	// panic now, and test
   240  	fake.panics = true
   241  	testStreamKeyRangePanics(t, bpc)
   242  	testStreamTablesPanics(t, bpc)
   243  	fake.panics = false
   244  }