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 }