github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/cdc/redo/writer/file/file_log_writer_test.go (about) 1 // Copyright 2023 PingCAP, Inc. 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 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package file 15 16 import ( 17 "context" 18 "testing" 19 20 "github.com/pingcap/log" 21 "github.com/pingcap/tiflow/cdc/model" 22 "github.com/pingcap/tiflow/cdc/redo/writer" 23 "github.com/pingcap/tiflow/pkg/errors" 24 "github.com/stretchr/testify/mock" 25 "github.com/stretchr/testify/require" 26 "go.uber.org/zap" 27 ) 28 29 func TestLogWriterWriteLog(t *testing.T) { 30 t.Parallel() 31 32 type arg struct { 33 ctx context.Context 34 rows []writer.RedoEvent 35 } 36 tableInfo := &model.TableInfo{ 37 TableName: model.TableName{ 38 Schema: "test", 39 Table: "t", 40 }, 41 } 42 tests := []struct { 43 name string 44 args arg 45 wantTs uint64 46 isRunning bool 47 writerErr error 48 wantErr error 49 }{ 50 { 51 name: "happy", 52 args: arg{ 53 ctx: context.Background(), 54 rows: []writer.RedoEvent{ 55 &model.RowChangedEvent{CommitTs: 1, PhysicalTableID: 111, TableInfo: tableInfo}, 56 }, 57 }, 58 isRunning: true, 59 writerErr: nil, 60 }, 61 { 62 name: "writer err", 63 args: arg{ 64 ctx: context.Background(), 65 rows: []writer.RedoEvent{ 66 nil, 67 &model.RowChangedEvent{CommitTs: 1, PhysicalTableID: 11, TableInfo: tableInfo}, 68 }, 69 }, 70 writerErr: errors.New("err"), 71 wantErr: errors.New("err"), 72 isRunning: true, 73 }, 74 { 75 name: "len(rows)==0", 76 args: arg{ 77 ctx: context.Background(), 78 rows: []writer.RedoEvent{}, 79 }, 80 writerErr: errors.New("err"), 81 isRunning: true, 82 }, 83 { 84 name: "isStopped", 85 args: arg{ 86 ctx: context.Background(), 87 rows: []writer.RedoEvent{}, 88 }, 89 writerErr: errors.ErrRedoWriterStopped, 90 isRunning: false, 91 wantErr: errors.ErrRedoWriterStopped, 92 }, 93 { 94 name: "context cancel", 95 args: arg{ 96 ctx: context.Background(), 97 rows: []writer.RedoEvent{}, 98 }, 99 writerErr: nil, 100 isRunning: true, 101 wantErr: context.Canceled, 102 }, 103 } 104 105 for _, tt := range tests { 106 mockWriter := &mockFileWriter{} 107 mockWriter.On("Write", mock.Anything).Return(1, tt.writerErr) 108 mockWriter.On("IsRunning").Return(tt.isRunning) 109 mockWriter.On("AdvanceTs", mock.Anything) 110 w := logWriter{ 111 cfg: &writer.LogWriterConfig{}, 112 backendWriter: mockWriter, 113 } 114 if tt.name == "context cancel" { 115 ctx, cancel := context.WithCancel(context.Background()) 116 cancel() 117 tt.args.ctx = ctx 118 } 119 120 err := w.WriteEvents(tt.args.ctx, tt.args.rows...) 121 if tt.wantErr != nil { 122 log.Info("log error", 123 zap.String("wantErr", tt.wantErr.Error()), 124 zap.String("gotErr", err.Error())) 125 require.Equal(t, tt.wantErr.Error(), err.Error(), tt.name) 126 } else { 127 require.Nil(t, err, tt.name) 128 } 129 } 130 } 131 132 func TestLogWriterWriteDDL(t *testing.T) { 133 t.Parallel() 134 135 type arg struct { 136 ctx context.Context 137 tableID int64 138 ddl *model.RedoDDLEvent 139 } 140 tests := []struct { 141 name string 142 args arg 143 wantTs uint64 144 isRunning bool 145 writerErr error 146 wantErr error 147 }{ 148 { 149 name: "happy", 150 args: arg{ 151 ctx: context.Background(), 152 tableID: 1, 153 ddl: &model.RedoDDLEvent{DDL: &model.DDLEvent{CommitTs: 1}}, 154 }, 155 isRunning: true, 156 writerErr: nil, 157 }, 158 { 159 name: "writer err", 160 args: arg{ 161 ctx: context.Background(), 162 tableID: 1, 163 ddl: &model.RedoDDLEvent{DDL: &model.DDLEvent{CommitTs: 1}}, 164 }, 165 writerErr: errors.New("err"), 166 wantErr: errors.New("err"), 167 isRunning: true, 168 }, 169 { 170 name: "ddl nil", 171 args: arg{ 172 ctx: context.Background(), 173 tableID: 1, 174 ddl: nil, 175 }, 176 writerErr: errors.New("err"), 177 isRunning: true, 178 }, 179 { 180 name: "isStopped", 181 args: arg{ 182 ctx: context.Background(), 183 tableID: 1, 184 ddl: &model.RedoDDLEvent{DDL: &model.DDLEvent{CommitTs: 1}}, 185 }, 186 writerErr: errors.ErrRedoWriterStopped, 187 isRunning: false, 188 wantErr: errors.ErrRedoWriterStopped, 189 }, 190 { 191 name: "context cancel", 192 args: arg{ 193 ctx: context.Background(), 194 tableID: 1, 195 ddl: &model.RedoDDLEvent{DDL: &model.DDLEvent{CommitTs: 1}}, 196 }, 197 writerErr: nil, 198 isRunning: true, 199 wantErr: context.Canceled, 200 }, 201 } 202 203 for _, tt := range tests { 204 mockWriter := &mockFileWriter{} 205 mockWriter.On("Write", mock.Anything).Return(1, tt.writerErr) 206 mockWriter.On("IsRunning").Return(tt.isRunning) 207 mockWriter.On("AdvanceTs", mock.Anything) 208 w := logWriter{ 209 cfg: &writer.LogWriterConfig{}, 210 backendWriter: mockWriter, 211 } 212 213 if tt.name == "context cancel" { 214 ctx, cancel := context.WithCancel(context.Background()) 215 cancel() 216 tt.args.ctx = ctx 217 } 218 219 var e writer.RedoEvent 220 if tt.args.ddl != nil { 221 e = tt.args.ddl.DDL 222 } 223 err := w.WriteEvents(tt.args.ctx, e) 224 if tt.wantErr != nil { 225 log.Info("log error", 226 zap.String("wantErr", tt.wantErr.Error()), 227 zap.String("gotErr", err.Error())) 228 require.Equal(t, tt.wantErr.Error(), err.Error(), tt.name) 229 } else { 230 require.Nil(t, err, tt.name) 231 } 232 } 233 } 234 235 func TestLogWriterFlushLog(t *testing.T) { 236 t.Parallel() 237 238 type arg struct { 239 ctx context.Context 240 tableID int64 241 ts uint64 242 } 243 tests := []struct { 244 name string 245 args arg 246 wantTs uint64 247 isRunning bool 248 flushErr error 249 wantErr error 250 }{ 251 { 252 name: "happy", 253 args: arg{ 254 ctx: context.Background(), 255 tableID: 1, 256 ts: 1, 257 }, 258 isRunning: true, 259 flushErr: nil, 260 }, 261 { 262 name: "flush err", 263 args: arg{ 264 ctx: context.Background(), 265 tableID: 1, 266 ts: 1, 267 }, 268 flushErr: errors.New("err"), 269 wantErr: errors.New("err"), 270 isRunning: true, 271 }, 272 { 273 name: "isStopped", 274 args: arg{ 275 ctx: context.Background(), 276 tableID: 1, 277 ts: 1, 278 }, 279 flushErr: errors.ErrRedoWriterStopped, 280 isRunning: false, 281 wantErr: errors.ErrRedoWriterStopped, 282 }, 283 { 284 name: "context cancel", 285 args: arg{ 286 ctx: context.Background(), 287 tableID: 1, 288 ts: 1, 289 }, 290 flushErr: nil, 291 isRunning: true, 292 wantErr: context.Canceled, 293 }, 294 } 295 296 dir := t.TempDir() 297 298 for _, tt := range tests { 299 mockWriter := &mockFileWriter{} 300 mockWriter.On("Flush", mock.Anything).Return(tt.flushErr) 301 mockWriter.On("IsRunning").Return(tt.isRunning) 302 cfg := &writer.LogWriterConfig{ 303 Dir: dir, 304 ChangeFeedID: model.DefaultChangeFeedID("test-cf"), 305 CaptureID: "cp", 306 MaxLogSizeInBytes: 10, 307 UseExternalStorage: true, 308 } 309 w := logWriter{ 310 cfg: cfg, 311 backendWriter: mockWriter, 312 } 313 314 if tt.name == "context cancel" { 315 ctx, cancel := context.WithCancel(context.Background()) 316 cancel() 317 tt.args.ctx = ctx 318 } 319 err := w.FlushLog(tt.args.ctx) 320 if tt.wantErr != nil { 321 log.Info("log error", 322 zap.String("wantErr", tt.wantErr.Error()), 323 zap.String("gotErr", err.Error())) 324 require.Equal(t, tt.wantErr.Error(), err.Error(), tt.name) 325 } else { 326 require.Nil(t, err, tt.name) 327 } 328 } 329 }