github.com/ecodeclub/eorm@v0.0.2-0.20231001112437-dae71da914d0/internal/datasource/transaction/single_transaction_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 transaction_test 16 17 import ( 18 "context" 19 "database/sql" 20 "errors" 21 "regexp" 22 "strings" 23 "testing" 24 25 "github.com/DATA-DOG/go-sqlmock" 26 "github.com/ecodeclub/eorm" 27 "github.com/ecodeclub/eorm/internal/datasource/masterslave" 28 "github.com/ecodeclub/eorm/internal/datasource/transaction" 29 "github.com/ecodeclub/eorm/internal/errs" 30 "github.com/ecodeclub/eorm/internal/test" 31 "github.com/stretchr/testify/assert" 32 "github.com/stretchr/testify/require" 33 "github.com/stretchr/testify/suite" 34 ) 35 36 type TestSingleTxTestSuite struct { 37 ShardingTransactionSuite 38 } 39 40 func (s *TestSingleTxTestSuite) TestExecute_Commit_Or_Rollback() { 41 t := s.T() 42 testCases := []struct { 43 name string 44 wantAffected int64 45 wantErr error 46 shardingVal int 47 values []*test.OrderDetail 48 querySet []*test.OrderDetail 49 tx *eorm.Tx 50 mockOrder func(mock1, mock2 sqlmock.Sqlmock) 51 afterFunc func(t *testing.T, tx *eorm.Tx, values []*test.OrderDetail) 52 }{ 53 { 54 name: "select insert commit", 55 wantAffected: 1, 56 shardingVal: 234, 57 values: []*test.OrderDetail{ 58 {OrderId: 288, ItemId: 101, UsingCol1: "Jimmy", UsingCol2: "Butler"}, 59 }, 60 querySet: []*test.OrderDetail{ 61 {OrderId: 234, ItemId: 12, UsingCol1: "Kevin", UsingCol2: "Durant"}, 62 }, 63 mockOrder: func(mock1, mock2 sqlmock.Sqlmock) { 64 mock1.MatchExpectationsInOrder(false) 65 mock1.ExpectBegin() 66 67 mock1.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_0` WHERE `order_id`=?;")). 68 WithArgs(234). 69 WillReturnRows(mock1.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}).AddRow(234, 12, "Kevin", "Durant")) 70 71 mock1.ExpectExec(regexp.QuoteMeta("INSERT INTO `order_detail_db_0`.`order_detail_tab_0`(`order_id`,`item_id`,`using_col1`,`using_col2`) VALUES(?,?,?,?);")). 72 WithArgs(288, 101, "Jimmy", "Butler").WillReturnResult(sqlmock.NewResult(1, 1)) 73 74 mock1.ExpectCommit() 75 }, 76 tx: func() *eorm.Tx { 77 tx, er := s.shardingDB.BeginTx( 78 transaction.UsingTxType(context.Background(), transaction.Single), &sql.TxOptions{}) 79 require.NoError(t, er) 80 return tx 81 }(), 82 afterFunc: func(t *testing.T, tx *eorm.Tx, values []*test.OrderDetail) { 83 err := tx.Commit() 84 require.NoError(t, err) 85 86 s.mockMaster.MatchExpectationsInOrder(false) 87 88 s.mockMaster.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_0` WHERE `order_id`=?;")). 89 WithArgs(288).WillReturnRows(s.mockMaster.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}).AddRow(288, 101, "Jimmy", "Butler")) 90 91 queryVal := s.findTgt(t, values) 92 assert.ElementsMatch(t, values, queryVal) 93 }, 94 }, 95 { 96 name: "select insert rollback", 97 wantAffected: 1, 98 shardingVal: 253, 99 values: []*test.OrderDetail{ 100 {OrderId: 199, ItemId: 100, UsingCol1: "Jason", UsingCol2: "Tatum"}, 101 }, 102 querySet: []*test.OrderDetail{ 103 {OrderId: 253, ItemId: 8, UsingCol1: "Stephen", UsingCol2: "Curry"}, 104 }, 105 mockOrder: func(mock1, mock2 sqlmock.Sqlmock) { 106 mock2.MatchExpectationsInOrder(false) 107 mock2.ExpectBegin() 108 109 mock2.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_1` WHERE `order_id`=?;")). 110 WithArgs(253). 111 WillReturnRows(mock1.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}).AddRow(253, 8, "Stephen", "Curry")) 112 113 mock2.ExpectExec(regexp.QuoteMeta("INSERT INTO `order_detail_db_1`.`order_detail_tab_1`(`order_id`,`item_id`,`using_col1`,`using_col2`) VALUES(?,?,?,?);")). 114 WithArgs(199, 100, "Jason", "Tatum").WillReturnResult(sqlmock.NewResult(1, 1)) 115 116 mock2.ExpectRollback() 117 }, 118 tx: func() *eorm.Tx { 119 tx, er := s.shardingDB.BeginTx( 120 transaction.UsingTxType(context.Background(), transaction.Single), &sql.TxOptions{}) 121 require.NoError(t, er) 122 return tx 123 }(), 124 afterFunc: func(t *testing.T, tx *eorm.Tx, values []*test.OrderDetail) { 125 err := tx.Rollback() 126 require.NoError(t, err) 127 128 s.mockMaster2.MatchExpectationsInOrder(false) 129 s.mockMaster2.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_1` WHERE `order_id`=?;")). 130 WithArgs(199).WillReturnRows(s.mockMaster2.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"})) 131 132 queryVal := s.findTgt(t, values) 133 var wantOds []*test.OrderDetail 134 assert.ElementsMatch(t, wantOds, queryVal) 135 }, 136 }, 137 { 138 name: "insert use multi db err", 139 wantAffected: 2, 140 shardingVal: 234, 141 wantErr: errs.NewErrDBNotEqual("order_detail_db_0", "order_detail_db_1"), 142 values: []*test.OrderDetail{ 143 {OrderId: 288, ItemId: 101, UsingCol1: "Jimmy", UsingCol2: "Butler"}, 144 {OrderId: 33, ItemId: 100, UsingCol1: "Nikolai", UsingCol2: "Jokic"}, 145 }, 146 querySet: []*test.OrderDetail{ 147 {OrderId: 234, ItemId: 12, UsingCol1: "Kevin", UsingCol2: "Durant"}, 148 }, 149 mockOrder: func(mock1, mock2 sqlmock.Sqlmock) { 150 mock1.MatchExpectationsInOrder(false) 151 mock2.MatchExpectationsInOrder(false) 152 mock1.ExpectBegin() 153 mock2.ExpectBegin() 154 155 mock1.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_0` WHERE `order_id`=?;")). 156 WithArgs(234). 157 WillReturnRows(mock1.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}).AddRow(234, 12, "Kevin", "Durant")) 158 159 mock1.ExpectExec(regexp.QuoteMeta("INSERT INTO `order_detail_db_0`.`order_detail_tab_0`(`order_id`,`item_id`,`using_col1`,`using_col2`) VALUES(?,?,?,?);")). 160 WithArgs(288, 101, "Jimmy", "Butler").WillReturnResult(sqlmock.NewResult(1, 1)) 161 mock2.ExpectExec(regexp.QuoteMeta("INSERT INTO `order_detail_db_1`.`order_detail_tab_0`(`order_id`,`item_id`,`using_col1`,`using_col2`) VALUES(?,?,?,?);")). 162 WithArgs(33, 100, "Nikolai", "Jokic").WillReturnResult(sqlmock.NewResult(1, 1)) 163 164 commitErr := errors.New("commit fail") 165 mock1.ExpectCommit().WillReturnError(commitErr) 166 mock2.ExpectCommit().WillReturnError(commitErr) 167 }, 168 tx: func() *eorm.Tx { 169 tx, er := s.shardingDB.BeginTx( 170 transaction.UsingTxType(context.Background(), transaction.Single), &sql.TxOptions{}) 171 require.NoError(t, er) 172 return tx 173 }(), 174 afterFunc: func(t *testing.T, tx *eorm.Tx, values []*test.OrderDetail) { 175 err := tx.Commit() 176 newErr := errors.New("commit fail") 177 errSlice := strings.Split(err.Error(), "; ") 178 wantErrSlice := []string{ 179 newMockCommitErr("order_detail_db_0", newErr).Error(), 180 newMockCommitErr("order_detail_db_1", newErr).Error()} 181 assert.ElementsMatch(t, wantErrSlice, errSlice) 182 183 s.mockMaster.MatchExpectationsInOrder(false) 184 s.mockMaster2.MatchExpectationsInOrder(false) 185 186 //row1 := s.mockMaster.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}) 187 //row2 := s.mockMaster.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}) 188 //s.mockMaster.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_0` WHERE (`order_id`=?) OR (`order_id`=?);SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_1` WHERE (`order_id`=?) OR (`order_id`=?);SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_2` WHERE (`order_id`=?) OR (`order_id`=?);")). 189 // WithArgs(288, 33, 288, 33, 288, 33).WillReturnRows(row1) 190 // 191 //s.mockMaster2.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_0` WHERE (`order_id`=?) OR (`order_id`=?);SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_1` WHERE (`order_id`=?) OR (`order_id`=?);SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_2` WHERE (`order_id`=?) OR (`order_id`=?);")). 192 // WithArgs(288, 33, 288, 33, 288, 33).WillReturnRows(row2) 193 194 row1 := s.mockMaster.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}) 195 row2 := s.mockMaster.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}) 196 s.mockMaster.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_0` WHERE (`order_id`=?) OR (`order_id`=?);")). 197 WithArgs(288, 33).WillReturnRows(row1) 198 199 s.mockMaster2.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_0` WHERE (`order_id`=?) OR (`order_id`=?);")). 200 WithArgs(288, 33).WillReturnRows(row2) 201 202 queryVal := s.findTgt(t, values) 203 var wantOds []*test.OrderDetail 204 assert.ElementsMatch(t, wantOds, queryVal) 205 }, 206 }, 207 { 208 name: "select and insert use multi db err", 209 wantAffected: 2, 210 shardingVal: 234, 211 wantErr: errs.NewErrDBNotEqual("order_detail_db_0", "order_detail_db_1"), 212 values: []*test.OrderDetail{ 213 {OrderId: 33, ItemId: 100, UsingCol1: "Nikolai", UsingCol2: "Jokic"}, 214 }, 215 querySet: []*test.OrderDetail{ 216 {OrderId: 234, ItemId: 12, UsingCol1: "Kevin", UsingCol2: "Durant"}, 217 }, 218 mockOrder: func(mock1, mock2 sqlmock.Sqlmock) { 219 mock1.MatchExpectationsInOrder(false) 220 mock1.ExpectBegin() 221 222 mock1.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_0` WHERE `order_id`=?;")). 223 WithArgs(234). 224 WillReturnRows(mock1.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}).AddRow(234, 12, "Kevin", "Durant")) 225 226 mock1.ExpectExec(regexp.QuoteMeta("INSERT INTO `order_detail_db_1`.`order_detail_tab_0`(`order_id`,`item_id`,`using_col1`,`using_col2`) VALUES(?,?,?,?);")). 227 WithArgs(33, 100, "Nikolai", "Jokic").WillReturnResult(sqlmock.NewResult(1, 1)) 228 229 mock1.ExpectCommit() 230 }, 231 tx: func() *eorm.Tx { 232 tx, er := s.shardingDB.BeginTx( 233 transaction.UsingTxType(context.Background(), transaction.Single), &sql.TxOptions{}) 234 require.NoError(t, er) 235 return tx 236 }(), 237 afterFunc: func(t *testing.T, tx *eorm.Tx, values []*test.OrderDetail) { 238 err := tx.Commit() 239 newErr := errors.New("commit fail") 240 errSlice := strings.Split(err.Error(), "; ") 241 wantErrSlice := []string{ 242 newMockCommitErr("order_detail_db_0", newErr).Error(), 243 newMockCommitErr("order_detail_db_1", newErr).Error()} 244 assert.ElementsMatch(t, wantErrSlice, errSlice) 245 246 s.mockMaster.MatchExpectationsInOrder(false) 247 s.mockMaster2.MatchExpectationsInOrder(false) 248 249 row1 := s.mockMaster.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}) 250 row2 := s.mockMaster.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}) 251 //s.mockMaster.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_0` WHERE (`order_id`=?) OR (`order_id`=?);SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_1` WHERE (`order_id`=?) OR (`order_id`=?);SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_2` WHERE (`order_id`=?) OR (`order_id`=?);")). 252 // WithArgs(288, 33, 288, 33, 288, 33).WillReturnRows(row1) 253 // 254 //s.mockMaster2.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_0` WHERE (`order_id`=?) OR (`order_id`=?);SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_1` WHERE (`order_id`=?) OR (`order_id`=?);SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_2` WHERE (`order_id`=?) OR (`order_id`=?);")). 255 // WithArgs(288, 33, 288, 33, 288, 33).WillReturnRows(row2) 256 257 s.mockMaster.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_0` WHERE `order_id`=?;")). 258 WithArgs(33).WillReturnRows(row1) 259 260 s.mockMaster2.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_0` WHERE `order_id`=?;")). 261 WithArgs(33).WillReturnRows(row2) 262 263 queryVal := s.findTgt(t, values) 264 var wantOds []*test.OrderDetail 265 assert.ElementsMatch(t, wantOds, queryVal) 266 }, 267 }, 268 { 269 name: "select insert commit err", 270 wantAffected: 1, 271 shardingVal: 234, 272 values: []*test.OrderDetail{ 273 {OrderId: 288, ItemId: 101, UsingCol1: "Jimmy", UsingCol2: "Butler"}, 274 }, 275 querySet: []*test.OrderDetail{ 276 {OrderId: 234, ItemId: 12, UsingCol1: "Kevin", UsingCol2: "Durant"}, 277 }, 278 mockOrder: func(mock1, mock2 sqlmock.Sqlmock) { 279 mock1.MatchExpectationsInOrder(false) 280 mock1.ExpectBegin() 281 282 mock1.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_0` WHERE `order_id`=?;")). 283 WithArgs(234). 284 WillReturnRows(mock1.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}).AddRow(234, 12, "Kevin", "Durant")) 285 286 mock1.ExpectExec(regexp.QuoteMeta("INSERT INTO `order_detail_db_0`.`order_detail_tab_0`(`order_id`,`item_id`,`using_col1`,`using_col2`) VALUES(?,?,?,?);")). 287 WithArgs(288, 101, "Jimmy", "Butler").WillReturnResult(sqlmock.NewResult(1, 1)) 288 289 mock1.ExpectCommit().WillReturnError(errors.New("commit fail")) 290 }, 291 tx: func() *eorm.Tx { 292 tx, er := s.shardingDB.BeginTx( 293 transaction.UsingTxType(context.Background(), transaction.Single), &sql.TxOptions{}) 294 require.NoError(t, er) 295 return tx 296 }(), 297 afterFunc: func(t *testing.T, tx *eorm.Tx, values []*test.OrderDetail) { 298 err := tx.Commit() 299 wantErr := errors.New("commit fail") 300 assert.Equal(t, wantErr, err) 301 302 s.mockMaster.MatchExpectationsInOrder(false) 303 s.mockMaster.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_0`.`order_detail_tab_0` WHERE `order_id`=?;")). 304 WithArgs(288).WillReturnRows(s.mockMaster.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"})) 305 306 queryVal := s.findTgt(t, values) 307 var wantOds []*test.OrderDetail 308 assert.ElementsMatch(t, wantOds, queryVal) 309 }, 310 }, 311 { 312 name: "select insert rollback err", 313 wantAffected: 1, 314 shardingVal: 253, 315 values: []*test.OrderDetail{ 316 {OrderId: 33, ItemId: 100, UsingCol1: "Nikolai", UsingCol2: "Jokic"}, 317 }, 318 querySet: []*test.OrderDetail{ 319 {OrderId: 253, ItemId: 8, UsingCol1: "Stephen", UsingCol2: "Curry"}, 320 }, 321 mockOrder: func(mock1, mock2 sqlmock.Sqlmock) { 322 mock2.MatchExpectationsInOrder(false) 323 mock2.ExpectBegin() 324 325 mock2.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_1` WHERE `order_id`=?;")). 326 WithArgs(253). 327 WillReturnRows(mock1.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"}).AddRow(253, 8, "Stephen", "Curry")) 328 329 mock2.ExpectExec(regexp.QuoteMeta("INSERT INTO `order_detail_db_1`.`order_detail_tab_0`(`order_id`,`item_id`,`using_col1`,`using_col2`) VALUES(?,?,?,?);")). 330 WithArgs(33, 100, "Nikolai", "Jokic").WillReturnResult(sqlmock.NewResult(1, 1)) 331 332 mock2.ExpectRollback().WillReturnError(errors.New("rollback fail")) 333 }, 334 tx: func() *eorm.Tx { 335 tx, er := s.shardingDB.BeginTx( 336 transaction.UsingTxType(context.Background(), transaction.Single), &sql.TxOptions{}) 337 require.NoError(t, er) 338 return tx 339 }(), 340 afterFunc: func(t *testing.T, tx *eorm.Tx, values []*test.OrderDetail) { 341 err := tx.Rollback() 342 wantErr := errors.New("rollback fail") 343 assert.Equal(t, wantErr, err) 344 345 s.mockMaster2.MatchExpectationsInOrder(false) 346 347 s.mockMaster2.ExpectQuery(regexp.QuoteMeta("SELECT `order_id`,`item_id`,`using_col1`,`using_col2` FROM `order_detail_db_1`.`order_detail_tab_0` WHERE `order_id`=?")). 348 WithArgs(33).WillReturnRows(s.mockMaster2.NewRows([]string{"order_id", "item_id", "using_col1", "using_col2"})) 349 350 queryVal := s.findTgt(t, values) 351 var wantOds []*test.OrderDetail 352 assert.ElementsMatch(t, wantOds, queryVal) 353 }, 354 }, 355 } 356 for _, tc := range testCases { 357 t.Run(tc.name, func(t *testing.T) { 358 tc.mockOrder(s.mockMaster, s.mockMaster2) 359 tx := tc.tx 360 querySet, err := eorm.NewShardingSelector[test.OrderDetail](tx). 361 Where(eorm.C("OrderId").EQ(tc.shardingVal)). 362 GetMulti(masterslave.UseMaster(context.Background())) 363 require.NoError(t, err) 364 assert.ElementsMatch(t, tc.querySet, querySet) 365 366 values := tc.values 367 res := eorm.NewShardingInsert[test.OrderDetail](tx). 368 Values(values).Exec(context.Background()) 369 affected, err := res.RowsAffected() 370 assert.Equal(t, tc.wantErr, err) 371 if err != nil { 372 return 373 } 374 assert.Equal(t, tc.wantAffected, affected) 375 tc.afterFunc(t, tx, values) 376 }) 377 } 378 } 379 380 func TestSingleTransactionSuite(t *testing.T) { 381 suite.Run(t, &TestSingleTxTestSuite{ 382 ShardingTransactionSuite: newShardingTransactionSuite(), 383 }) 384 }