github.com/ecodeclub/eorm@v0.0.2-0.20231001112437-dae71da914d0/internal/merger/batchmerger/merger_test.go (about)

     1  // Copyright 2021 ecodeclub
     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  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package batchmerger
    16  
    17  import (
    18  	"context"
    19  	"database/sql"
    20  	"errors"
    21  	"fmt"
    22  	"testing"
    23  
    24  	"github.com/ecodeclub/eorm/internal/rows"
    25  
    26  	"go.uber.org/multierr"
    27  
    28  	"github.com/DATA-DOG/go-sqlmock"
    29  	"github.com/ecodeclub/eorm/internal/merger/internal/errs"
    30  	"github.com/stretchr/testify/assert"
    31  	"github.com/stretchr/testify/require"
    32  	"github.com/stretchr/testify/suite"
    33  )
    34  
    35  var (
    36  	nextMockErr error = errors.New("rows: MockNextErr")
    37  )
    38  
    39  func newCloseMockErr(dbName string) error {
    40  	return fmt.Errorf("rows: %s MockCloseErr", dbName)
    41  }
    42  
    43  type MergerSuite struct {
    44  	suite.Suite
    45  	mockDB01 *sql.DB
    46  	mock01   sqlmock.Sqlmock
    47  	mockDB02 *sql.DB
    48  	mock02   sqlmock.Sqlmock
    49  	mockDB03 *sql.DB
    50  	mock03   sqlmock.Sqlmock
    51  	mockDB04 *sql.DB
    52  	mock04   sqlmock.Sqlmock
    53  }
    54  
    55  func (ms *MergerSuite) SetupTest() {
    56  	t := ms.T()
    57  	ms.initMock(t)
    58  }
    59  
    60  func (ms *MergerSuite) TearDownTest() {
    61  	_ = ms.mockDB01.Close()
    62  	_ = ms.mockDB02.Close()
    63  	_ = ms.mockDB03.Close()
    64  	_ = ms.mockDB04.Close()
    65  }
    66  
    67  func (ms *MergerSuite) initMock(t *testing.T) {
    68  	var err error
    69  	ms.mockDB01, ms.mock01, err = sqlmock.New()
    70  	if err != nil {
    71  		t.Fatal(err)
    72  	}
    73  	ms.mockDB02, ms.mock02, err = sqlmock.New()
    74  	if err != nil {
    75  		t.Fatal(err)
    76  	}
    77  	ms.mockDB03, ms.mock03, err = sqlmock.New()
    78  	if err != nil {
    79  		t.Fatal(err)
    80  	}
    81  	ms.mockDB04, ms.mock04, err = sqlmock.New()
    82  	if err != nil {
    83  		t.Fatal(err)
    84  	}
    85  }
    86  
    87  func (ms *MergerSuite) TestMerger_Merge() {
    88  	testcases := []struct {
    89  		name     string
    90  		rowsList func() []rows.Rows
    91  		ctx      func() (context.Context, context.CancelFunc)
    92  		wantErr  error
    93  	}{
    94  		{
    95  			name: "sql.Rows列表中没有元素",
    96  			rowsList: func() []rows.Rows {
    97  				return []rows.Rows{}
    98  			},
    99  			ctx: func() (context.Context, context.CancelFunc) {
   100  				return context.WithCancel(context.Background())
   101  			},
   102  			wantErr: errs.ErrMergerEmptyRows,
   103  		},
   104  		{
   105  			name: "sql.Rows列表中有元素为nil",
   106  			rowsList: func() []rows.Rows {
   107  				return []rows.Rows{nil}
   108  			},
   109  			ctx: func() (context.Context, context.CancelFunc) {
   110  				return context.WithCancel(context.Background())
   111  			},
   112  			wantErr: errs.ErrMergerRowsIsNull,
   113  		},
   114  		{
   115  			name: "sqlRows字段不同_少一个字段",
   116  			ctx: func() (context.Context, context.CancelFunc) {
   117  				return context.WithCancel(context.Background())
   118  			},
   119  			rowsList: func() []rows.Rows {
   120  				query := "SELECT * FROM `t1`"
   121  				ms.mock01.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows([]string{"id", "name", "address"}).AddRow(1, "abex", "cn").AddRow(5, "bruce", "cn"))
   122  				ms.mock02.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows([]string{"id", "name"}).AddRow(3, "alex").AddRow(4, "x"))
   123  				dbs := []*sql.DB{ms.mockDB01, ms.mockDB02}
   124  				rowsList := make([]rows.Rows, 0, len(dbs))
   125  				for _, db := range dbs {
   126  					row, err := db.QueryContext(context.Background(), query)
   127  					require.NoError(ms.T(), err)
   128  					rowsList = append(rowsList, row)
   129  				}
   130  				return rowsList
   131  			},
   132  			wantErr: errs.ErrMergerRowsDiff,
   133  		},
   134  		{
   135  			name: "sqlRows字段不同",
   136  			ctx: func() (context.Context, context.CancelFunc) {
   137  				return context.WithCancel(context.Background())
   138  			},
   139  			rowsList: func() []rows.Rows {
   140  				query := "SELECT * FROM `t1`"
   141  				ms.mock01.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows([]string{"id", "name", "address"}).AddRow(1, "abex", "cn").AddRow(5, "bruce", "cn"))
   142  				ms.mock02.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows([]string{"id", "name", "email"}).AddRow(3, "alex", "cn").AddRow(4, "x", "cn"))
   143  				dbs := []*sql.DB{ms.mockDB01, ms.mockDB02}
   144  				rowsList := make([]rows.Rows, 0, len(dbs))
   145  				for _, db := range dbs {
   146  					row, err := db.QueryContext(context.Background(), query)
   147  					require.NoError(ms.T(), err)
   148  					rowsList = append(rowsList, row)
   149  				}
   150  				return rowsList
   151  			},
   152  			wantErr: errs.ErrMergerRowsDiff,
   153  		},
   154  		{
   155  			name: "正常的案例",
   156  			rowsList: func() []rows.Rows {
   157  				query := "SELECT * FROM `t1`"
   158  				ms.mock01.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows([]string{"id", "name", "address"}).AddRow(1, "abex", "cn").AddRow(5, "bruce", "cn"))
   159  				ms.mock02.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows([]string{"id", "name", "address"}).AddRow(3, "alex", "cn").AddRow(4, "x", "cn"))
   160  				dbs := []*sql.DB{ms.mockDB01, ms.mockDB02}
   161  				rowsList := make([]rows.Rows, 0, len(dbs))
   162  				for _, db := range dbs {
   163  					row, err := db.QueryContext(context.Background(), query)
   164  					require.NoError(ms.T(), err)
   165  					rowsList = append(rowsList, row)
   166  				}
   167  				return rowsList
   168  			},
   169  			ctx: func() (context.Context, context.CancelFunc) {
   170  				return context.WithCancel(context.Background())
   171  			},
   172  		},
   173  		{
   174  			name: "超时",
   175  			ctx: func() (context.Context, context.CancelFunc) {
   176  				ctx, cancel := context.WithTimeout(context.Background(), 0)
   177  				return ctx, cancel
   178  			},
   179  			wantErr: context.DeadlineExceeded,
   180  			rowsList: func() []rows.Rows {
   181  				query := "SELECT * FROM `t1`;"
   182  				cols := []string{"id"}
   183  				res := make([]rows.Rows, 0, 1)
   184  				ms.mock01.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow(1))
   185  				rows, _ := ms.mockDB01.QueryContext(context.Background(), query)
   186  				res = append(res, rows)
   187  				return res
   188  			},
   189  		},
   190  	}
   191  	for _, tc := range testcases {
   192  		ms.T().Run(tc.name, func(t *testing.T) {
   193  			merger := NewMerger()
   194  			ctx, cancel := tc.ctx()
   195  			rows, err := merger.Merge(ctx, tc.rowsList())
   196  			cancel()
   197  			assert.Equal(t, tc.wantErr, err)
   198  			if err != nil {
   199  				return
   200  			}
   201  			require.NotNil(t, rows)
   202  		})
   203  	}
   204  }
   205  
   206  func (ms *MergerSuite) TestRows_NextAndScan() {
   207  
   208  	testCases := []struct {
   209  		name    string
   210  		sqlRows func() []rows.Rows
   211  		wantVal []string
   212  		wantErr error
   213  		scanErr error
   214  	}{
   215  		{
   216  			name: "sqlRows列表中没有空行",
   217  			sqlRows: func() []rows.Rows {
   218  				query := "SELECT * FROM `t1`;"
   219  				cols := []string{"id"}
   220  				ms.mock01.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("1").AddRow("2"))
   221  				ms.mock02.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("2").AddRow("2"))
   222  				ms.mock03.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("3").AddRow("4"))
   223  				dbs := []*sql.DB{ms.mockDB01, ms.mockDB02, ms.mockDB03}
   224  				res := make([]rows.Rows, 0, len(dbs))
   225  				for _, db := range dbs {
   226  					row, _ := db.QueryContext(context.Background(), query)
   227  					res = append(res, row)
   228  				}
   229  				return res
   230  			},
   231  			wantVal: []string{"1", "2", "2", "2", "3", "4"},
   232  		},
   233  		{
   234  			name: "sqlRows列表中,在前面有一个sqlRows返回空行在前面",
   235  			sqlRows: func() []rows.Rows {
   236  				query := "SELECT * FROM `t1`;"
   237  				cols := []string{"id"}
   238  				ms.mock01.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("1"))
   239  				ms.mock02.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("2"))
   240  				ms.mock03.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("3").AddRow("4"))
   241  				ms.mock04.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols))
   242  				dbs := []*sql.DB{ms.mockDB04, ms.mockDB01, ms.mockDB02, ms.mockDB03}
   243  				res := make([]rows.Rows, 0, len(dbs))
   244  				for _, db := range dbs {
   245  					row, _ := db.QueryContext(context.Background(), query)
   246  					res = append(res, row)
   247  				}
   248  				return res
   249  			},
   250  			wantVal: []string{"1", "2", "3", "4"},
   251  		},
   252  		{
   253  			name: "sqlRows列表中,在前面有多个sqlRows返回空行",
   254  			sqlRows: func() []rows.Rows {
   255  				query := "SELECT * FROM `t1`;"
   256  				cols := []string{"id"}
   257  				ms.mock01.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols))
   258  				ms.mock02.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("2"))
   259  				ms.mock03.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("3").AddRow("4"))
   260  				ms.mock04.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols))
   261  				dbs := []*sql.DB{ms.mockDB04, ms.mockDB01, ms.mockDB02, ms.mockDB03}
   262  				res := make([]rows.Rows, 0, len(dbs))
   263  				for _, db := range dbs {
   264  					row, _ := db.QueryContext(context.Background(), query)
   265  					res = append(res, row)
   266  				}
   267  				return res
   268  			},
   269  			wantVal: []string{"2", "3", "4"},
   270  		},
   271  		{
   272  			name: "sqlRows列表中,在中间有一个sqlRows返回空行",
   273  			sqlRows: func() []rows.Rows {
   274  				query := "SELECT * FROM `t1`;"
   275  				cols := []string{"id"}
   276  				ms.mock01.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols))
   277  				ms.mock02.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("2"))
   278  				ms.mock03.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("3").AddRow("4"))
   279  				dbs := []*sql.DB{ms.mockDB02, ms.mockDB01, ms.mockDB03}
   280  				res := make([]rows.Rows, 0, len(dbs))
   281  				for _, db := range dbs {
   282  					row, _ := db.QueryContext(context.Background(), query)
   283  					res = append(res, row)
   284  				}
   285  				return res
   286  			},
   287  			wantVal: []string{"2", "3", "4"},
   288  		},
   289  		{
   290  			name: "sqlRows列表中,在中间有多个sqlRows返回空行",
   291  			sqlRows: func() []rows.Rows {
   292  				query := "SELECT * FROM `t1`;"
   293  				cols := []string{"id"}
   294  				ms.mock01.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols))
   295  				ms.mock02.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("2"))
   296  				ms.mock03.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("3").AddRow("4"))
   297  				ms.mock04.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols))
   298  				dbs := []*sql.DB{ms.mockDB02, ms.mockDB01, ms.mockDB04, ms.mockDB03}
   299  				res := make([]rows.Rows, 0, len(dbs))
   300  				for _, db := range dbs {
   301  					row, _ := db.QueryContext(context.Background(), query)
   302  					res = append(res, row)
   303  				}
   304  				return res
   305  			},
   306  			wantVal: []string{"2", "3", "4"},
   307  		},
   308  		{
   309  			name: "sqlRows列表中,在后面有一个sqlRows返回空行",
   310  			sqlRows: func() []rows.Rows {
   311  				query := "SELECT * FROM `t1`;"
   312  				cols := []string{"id"}
   313  				ms.mock01.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols))
   314  				ms.mock02.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("2"))
   315  				ms.mock03.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("3").AddRow("4"))
   316  				dbs := []*sql.DB{ms.mockDB02, ms.mockDB03, ms.mockDB01}
   317  				res := make([]rows.Rows, 0, len(dbs))
   318  				for _, db := range dbs {
   319  					row, _ := db.QueryContext(context.Background(), query)
   320  					res = append(res, row)
   321  				}
   322  				return res
   323  			},
   324  			wantVal: []string{"2", "3", "4"},
   325  		},
   326  		{
   327  			name: "sqlRows列表中,在后面有多个个sqlRows返回空行",
   328  			sqlRows: func() []rows.Rows {
   329  				query := "SELECT * FROM `t1`;"
   330  				cols := []string{"id"}
   331  				ms.mock01.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols))
   332  				ms.mock02.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("2"))
   333  				ms.mock03.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("3").AddRow("4"))
   334  				ms.mock04.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols))
   335  				dbs := []*sql.DB{ms.mockDB02, ms.mockDB03, ms.mockDB01, ms.mockDB04}
   336  				res := make([]rows.Rows, 0, len(dbs))
   337  				for _, db := range dbs {
   338  					row, _ := db.QueryContext(context.Background(), query)
   339  					res = append(res, row)
   340  				}
   341  				return res
   342  			},
   343  			wantVal: []string{"2", "3", "4"},
   344  		},
   345  		{
   346  			name: "sqlRows列表中的元素均返回空行",
   347  			sqlRows: func() []rows.Rows {
   348  				ms.mock01.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows([]string{"id"}))
   349  				ms.mock02.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows([]string{"id"}))
   350  				ms.mock03.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows([]string{"id"}))
   351  				res := make([]rows.Rows, 0, 3)
   352  				row01, _ := ms.mockDB01.QueryContext(context.Background(), "SELECT * FROM `t1`;")
   353  				res = append(res, row01)
   354  				row02, _ := ms.mockDB02.QueryContext(context.Background(), "SELECT * FROM `t1`;")
   355  				res = append(res, row02)
   356  				row03, _ := ms.mockDB03.QueryContext(context.Background(), "SELECT * FROM `t1`;")
   357  				res = append(res, row03)
   358  				return res
   359  			},
   360  			wantVal: []string{},
   361  		},
   362  	}
   363  	for _, tc := range testCases {
   364  		ms.T().Run(tc.name, func(t *testing.T) {
   365  			merger := Merger{}
   366  			rows, err := merger.Merge(context.Background(), tc.sqlRows())
   367  			assert.Equal(t, tc.wantErr, err)
   368  			if err != nil {
   369  				return
   370  			}
   371  			res := make([]string, 0, 4)
   372  			for rows.Next() {
   373  				var id string
   374  				err = rows.Scan(&id)
   375  				assert.Equal(t, tc.scanErr, err)
   376  				if err != nil {
   377  					return
   378  				}
   379  				res = append(res, id)
   380  			}
   381  			require.NoError(t, rows.Err())
   382  			assert.Equal(t, tc.wantVal, res)
   383  		})
   384  	}
   385  
   386  }
   387  
   388  func (ms *MergerSuite) TestRows_NextAndErr() {
   389  	testcases := []struct {
   390  		name     string
   391  		rowsList func() []rows.Rows
   392  		wantErr  error
   393  	}{
   394  		{
   395  			name: "sqlRows列表中有一个返回error",
   396  			rowsList: func() []rows.Rows {
   397  				cols := []string{"id"}
   398  				query := "SELECT * FROM `t1`"
   399  				ms.mock01.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("1"))
   400  				ms.mock02.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("2"))
   401  				ms.mock03.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("3").AddRow("4").RowError(1, nextMockErr))
   402  				ms.mock04.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("5"))
   403  				dbs := []*sql.DB{ms.mockDB01, ms.mockDB02, ms.mockDB04, ms.mockDB03}
   404  				rowsList := make([]rows.Rows, 0, len(dbs))
   405  				for _, db := range dbs {
   406  					row, err := db.QueryContext(context.Background(), query)
   407  					require.NoError(ms.T(), err)
   408  					rowsList = append(rowsList, row)
   409  				}
   410  				return rowsList
   411  			},
   412  			wantErr: nextMockErr,
   413  		},
   414  	}
   415  	for _, tc := range testcases {
   416  		ms.T().Run(tc.name, func(t *testing.T) {
   417  			merger := NewMerger()
   418  			rows, err := merger.Merge(context.Background(), tc.rowsList())
   419  			require.NoError(t, err)
   420  			for rows.Next() {
   421  			}
   422  			assert.Equal(t, tc.wantErr, rows.Err())
   423  		})
   424  	}
   425  }
   426  
   427  func (ms *MergerSuite) TestRows_ScanAndErr() {
   428  	ms.T().Run("未调用Next,直接Scan,返回错", func(t *testing.T) {
   429  		cols := []string{"id"}
   430  		query := "SELECT * FROM `t1`"
   431  		ms.mock01.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow(1).AddRow(5))
   432  		r, err := ms.mockDB01.QueryContext(context.Background(), query)
   433  		require.NoError(t, err)
   434  		rowsList := []rows.Rows{r}
   435  		merger := NewMerger()
   436  		rows, err := merger.Merge(context.Background(), rowsList)
   437  		require.NoError(t, err)
   438  		id := 0
   439  		err = rows.Scan(&id)
   440  		require.Error(t, err)
   441  	})
   442  	ms.T().Run("迭代过程中发现错误,调用Scan,返回迭代中发现的错误", func(t *testing.T) {
   443  		cols := []string{"id"}
   444  		query := "SELECT * FROM `t1`"
   445  		ms.mock01.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow(1).RowError(0, nextMockErr))
   446  		r, err := ms.mockDB01.QueryContext(context.Background(), query)
   447  		require.NoError(t, err)
   448  		rowsList := []rows.Rows{r}
   449  		merger := NewMerger()
   450  		rows, err := merger.Merge(context.Background(), rowsList)
   451  		require.NoError(t, err)
   452  		for rows.Next() {
   453  		}
   454  		id := 0
   455  		err = rows.Scan(&id)
   456  		assert.Equal(t, nextMockErr, err)
   457  	})
   458  
   459  }
   460  
   461  func (ms *MergerSuite) TestRows_Close() {
   462  	cols := []string{"id"}
   463  	query := "SELECT * FROM `t1`"
   464  	ms.mock01.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("1"))
   465  	ms.mock02.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("2").CloseError(newCloseMockErr("db02")))
   466  	ms.mock03.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("3").AddRow("4").CloseError(newCloseMockErr("db03")))
   467  	merger := NewMerger()
   468  	dbs := []*sql.DB{ms.mockDB01, ms.mockDB02, ms.mockDB03}
   469  	rowsList := make([]rows.Rows, 0, len(dbs))
   470  	for _, db := range dbs {
   471  		row, err := db.QueryContext(context.Background(), query)
   472  		require.NoError(ms.T(), err)
   473  		rowsList = append(rowsList, row)
   474  	}
   475  	rs, err := merger.Merge(context.Background(), rowsList)
   476  	require.NoError(ms.T(), err)
   477  	// 判断当前是可以正常读取的
   478  	require.True(ms.T(), rs.Next())
   479  	var id int
   480  	err = rs.Scan(&id)
   481  	require.NoError(ms.T(), err)
   482  	err = rs.Close()
   483  	ms.T().Run("close返回multierror", func(t *testing.T) {
   484  		assert.Equal(ms.T(), multierr.Combine(newCloseMockErr("db02"), newCloseMockErr("db03")), err)
   485  	})
   486  	ms.T().Run("close之后Next返回false", func(t *testing.T) {
   487  		for i := 0; i < len(rowsList); i++ {
   488  			require.False(ms.T(), rowsList[i].Next())
   489  		}
   490  		require.False(ms.T(), rs.Next())
   491  	})
   492  	ms.T().Run("close之后Scan返回迭代过程中的错误", func(t *testing.T) {
   493  		var id int
   494  		err := rs.Scan(&id)
   495  		assert.Equal(t, errs.ErrMergerRowsClosed, err)
   496  	})
   497  	ms.T().Run("close之后调用Columns方法返回错误", func(t *testing.T) {
   498  		_, err := rs.Columns()
   499  		require.Error(t, err)
   500  	})
   501  	ms.T().Run("close多次是等效的", func(t *testing.T) {
   502  		for i := 0; i < 4; i++ {
   503  			err = rs.Close()
   504  			require.NoError(t, err)
   505  		}
   506  	})
   507  }
   508  
   509  func (ms *MergerSuite) TestRows_Columns() {
   510  	cols := []string{"id"}
   511  	query := "SELECT * FROM `t1`"
   512  	ms.mock01.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("1"))
   513  	ms.mock02.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("2"))
   514  	ms.mock03.ExpectQuery("SELECT *").WillReturnRows(sqlmock.NewRows(cols).AddRow("3").AddRow("4"))
   515  	merger := NewMerger()
   516  	dbs := []*sql.DB{ms.mockDB01, ms.mockDB02, ms.mockDB03}
   517  	rowsList := make([]rows.Rows, 0, len(dbs))
   518  	for _, db := range dbs {
   519  		row, err := db.QueryContext(context.Background(), query)
   520  		require.NoError(ms.T(), err)
   521  		rowsList = append(rowsList, row)
   522  	}
   523  
   524  	rows, err := merger.Merge(context.Background(), rowsList)
   525  	require.NoError(ms.T(), err)
   526  	ms.T().Run("Next没有迭代完", func(t *testing.T) {
   527  		for rows.Next() {
   528  			columns, err := rows.Columns()
   529  			require.NoError(t, err)
   530  			assert.Equal(t, cols, columns)
   531  		}
   532  		require.NoError(t, rows.Err())
   533  	})
   534  	ms.T().Run("Next迭代完", func(t *testing.T) {
   535  		require.False(t, rows.Next())
   536  		require.NoError(t, rows.Err())
   537  		_, err := rows.Columns()
   538  		assert.Equal(t, errs.ErrMergerRowsClosed, err)
   539  
   540  	})
   541  }
   542  
   543  func TestMerger(t *testing.T) {
   544  	suite.Run(t, &MergerSuite{})
   545  }
   546  
   547  func TestRows_NextResultSet(t *testing.T) {
   548  	assert.False(t, (&Rows{}).NextResultSet())
   549  }