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  }