github.com/ecodeclub/eorm@v0.0.2-0.20231001112437-dae71da914d0/update_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 eorm 16 17 import ( 18 "context" 19 "database/sql" 20 "errors" 21 "fmt" 22 "testing" 23 24 "github.com/ecodeclub/eorm/internal/datasource/single" 25 26 "github.com/DATA-DOG/go-sqlmock" 27 "github.com/ecodeclub/eorm/internal/errs" 28 "github.com/stretchr/testify/require" 29 30 "github.com/stretchr/testify/assert" 31 ) 32 33 func TestUpdater_Set(t *testing.T) { 34 tm := &TestModel{ 35 Id: 12, 36 FirstName: "Tom", 37 Age: 18, 38 LastName: &sql.NullString{String: "Jerry", Valid: true}, 39 } 40 db := memoryDB() 41 testCases := []CommonTestCase{ 42 { 43 name: "no set and update", 44 builder: NewUpdater[TestModel](db), 45 wantSql: "UPDATE `test_model` SET `id`=?,`first_name`=?,`age`=?,`last_name`=?;", 46 wantArgs: []interface{}{int64(0), "", int8(0), (*sql.NullString)(nil)}, 47 }, 48 { 49 name: "no set", 50 builder: NewUpdater[TestModel](db).Update(&TestModel{ 51 Id: 12, 52 FirstName: "Tom", 53 Age: 18, 54 }), 55 wantSql: "UPDATE `test_model` SET `id`=?,`first_name`=?,`age`=?,`last_name`=?;", 56 wantArgs: []interface{}{int64(12), "Tom", int8(18), (*sql.NullString)(nil)}, 57 }, 58 { 59 name: "set columns", 60 builder: NewUpdater[TestModel](db).Update(tm).Set(Columns("FirstName", "Age")), 61 wantSql: "UPDATE `test_model` SET `first_name`=?,`age`=?;", 62 wantArgs: []interface{}{"Tom", int8(18)}, 63 }, 64 { 65 name: "set invalid columns", 66 builder: NewUpdater[TestModel](db).Update(tm).Set(Columns("FirstNameInvalid", "Age")), 67 wantErr: errs.NewInvalidFieldError("FirstNameInvalid"), 68 }, 69 { 70 name: "set c2", 71 builder: NewUpdater[TestModel](db).Update(tm).Set(C("FirstName"), C("Age")), 72 wantSql: "UPDATE `test_model` SET `first_name`=?,`age`=?;", 73 wantArgs: []interface{}{"Tom", int8(18)}, 74 }, 75 { 76 name: "set invalid c2", 77 builder: NewUpdater[TestModel](db).Update(tm).Set(C("FirstNameInvalid"), C("Age")), 78 wantErr: errs.NewInvalidFieldError("FirstNameInvalid"), 79 }, 80 { 81 name: "set assignment", 82 builder: NewUpdater[TestModel](db).Update(tm).Set(C("FirstName"), Assign("Age", 30)), 83 wantSql: "UPDATE `test_model` SET `first_name`=?,`age`=?;", 84 wantArgs: []interface{}{"Tom", 30}, 85 }, 86 { 87 name: "set invalid assignment", 88 builder: NewUpdater[TestModel](db).Update(tm).Set(C("FirstName"), Assign("InvalidAge", 30)), 89 wantErr: errs.NewInvalidFieldError("InvalidAge"), 90 }, 91 { 92 name: "set age+1", 93 builder: NewUpdater[TestModel](db).Update(tm).Set(C("FirstName"), Assign("Age", C("Age").Add(1))), 94 wantSql: "UPDATE `test_model` SET `first_name`=?,`age`=(`age`+?);", 95 wantArgs: []interface{}{"Tom", 1}, 96 }, 97 { 98 name: "set age=id+1", 99 builder: NewUpdater[TestModel](db).Update(tm).Set(C("FirstName"), Assign("Age", C("Id").Add(10))), 100 wantSql: "UPDATE `test_model` SET `first_name`=?,`age`=(`id`+?);", 101 wantArgs: []interface{}{"Tom", 10}, 102 }, 103 { 104 name: "set age=id+(age*100)+10", 105 builder: NewUpdater[TestModel](db).Update(tm).Set(C("FirstName"), Assign("Age", C("Id").Add(C("Age").Multi(100)).Add(10))), 106 wantSql: "UPDATE `test_model` SET `first_name`=?,`age`=((`id`+(`age`*?))+?);", 107 wantArgs: []interface{}{"Tom", 100, 10}, 108 }, 109 { 110 name: "set age=(id+(age*100))*110", 111 builder: NewUpdater[TestModel](db).Update(tm).Set(C("FirstName"), Assign("Age", C("Id").Add(C("Age").Multi(100)).Multi(110))), 112 wantSql: "UPDATE `test_model` SET `first_name`=?,`age`=((`id`+(`age`*?))*?);", 113 wantArgs: []interface{}{"Tom", 100, 110}, 114 }, 115 { 116 name: "not nil columns", 117 builder: NewUpdater[TestModel](db).Update(&TestModel{Id: 13}).SkipNilValue(), 118 wantSql: "UPDATE `test_model` SET `id`=?,`first_name`=?,`age`=?;", 119 wantArgs: []interface{}{int64(13), "", int8(0)}, 120 }, 121 { 122 name: "not zero columns", 123 builder: NewUpdater[TestModel](db).Update(&TestModel{Id: 13}).SkipZeroValue(), 124 wantSql: "UPDATE `test_model` SET `id`=?;", 125 wantArgs: []interface{}{int64(13)}, 126 }, 127 } 128 129 for _, tc := range testCases { 130 c := tc 131 t.Run(c.name, func(t *testing.T) { 132 query, err := tc.builder.Build() 133 assert.Equal(t, err, c.wantErr) 134 if err != nil { 135 return 136 } 137 assert.Equal(t, c.wantSql, query.SQL) 138 assert.Equal(t, c.wantArgs, query.Args) 139 }) 140 } 141 } 142 143 func TestUpdater_SetForCombination(t *testing.T) { 144 u := &User{ 145 Id: 12, 146 Person: Person{ 147 FirstName: "Tom", 148 Age: 18, 149 LastName: &sql.NullString{String: "Jerry", Valid: true}, 150 }, 151 } 152 db := memoryDB() 153 testCases := []CommonTestCase{ 154 { 155 name: "no set", 156 builder: NewUpdater[User](db).Update(u), 157 wantSql: "UPDATE `user` SET `id`=?,`first_name`=?,`age`=?,`last_name`=?;", 158 wantArgs: []interface{}{int64(12), "Tom", int8(18), &sql.NullString{String: "Jerry", Valid: true}}, 159 }, 160 { 161 name: "set columns", 162 builder: NewUpdater[User](db).Update(u).Set(Columns("FirstName", "Age")), 163 wantSql: "UPDATE `user` SET `first_name`=?,`age`=?;", 164 wantArgs: []interface{}{"Tom", int8(18)}, 165 }, 166 { 167 name: "set invalid columns", 168 builder: NewUpdater[User](db).Update(u).Set(Columns("FirstNameInvalid", "Age")), 169 wantErr: errs.NewInvalidFieldError("FirstNameInvalid"), 170 }, 171 { 172 name: "set c2", 173 builder: NewUpdater[User](db).Update(u).Set(C("FirstName"), C("Age")), 174 wantSql: "UPDATE `user` SET `first_name`=?,`age`=?;", 175 wantArgs: []interface{}{"Tom", int8(18)}, 176 }, 177 178 { 179 name: "set invalid c2", 180 builder: NewUpdater[User](db).Update(u).Set(C("FirstNameInvalid"), C("Age")), 181 wantErr: errs.NewInvalidFieldError("FirstNameInvalid"), 182 }, 183 184 { 185 name: "set assignment", 186 builder: NewUpdater[User](db).Update(u).Set(C("FirstName"), Assign("Age", 30)), 187 wantSql: "UPDATE `user` SET `first_name`=?,`age`=?;", 188 wantArgs: []interface{}{"Tom", 30}, 189 }, 190 { 191 name: "set invalid assignment", 192 builder: NewUpdater[User](db).Update(u).Set(C("FirstName"), Assign("InvalidAge", 30)), 193 wantErr: errs.NewInvalidFieldError("InvalidAge"), 194 }, 195 { 196 name: "set age+1", 197 builder: NewUpdater[User](db).Update(u).Set(C("FirstName"), Assign("Age", C("Age").Add(1))), 198 wantSql: "UPDATE `user` SET `first_name`=?,`age`=(`age`+?);", 199 wantArgs: []interface{}{"Tom", 1}, 200 }, 201 { 202 name: "set age=id+1", 203 builder: NewUpdater[User](db).Update(u).Set(C("FirstName"), Assign("Age", C("Id").Add(10))), 204 wantSql: "UPDATE `user` SET `first_name`=?,`age`=(`id`+?);", 205 wantArgs: []interface{}{"Tom", 10}, 206 }, 207 { 208 name: "set age=id+(age*100)", 209 builder: NewUpdater[User](db).Update(u).Set(C("FirstName"), Assign("Age", C("Id").Add(C("Age").Multi(100)))), 210 wantSql: "UPDATE `user` SET `first_name`=?,`age`=(`id`+(`age`*?));", 211 wantArgs: []interface{}{"Tom", 100}, 212 }, 213 { 214 name: "set age=(id+(age*100))*110", 215 builder: NewUpdater[User](db).Update(u).Set(C("FirstName"), Assign("Age", C("Id").Add(C("Age").Multi(100)).Multi(110))), 216 wantSql: "UPDATE `user` SET `first_name`=?,`age`=((`id`+(`age`*?))*?);", 217 wantArgs: []interface{}{"Tom", 100, 110}, 218 }, 219 { 220 name: "not nil columns", 221 builder: NewUpdater[User](db).Update(&User{Id: 13}).SkipNilValue(), 222 wantSql: "UPDATE `user` SET `id`=?,`first_name`=?,`age`=?;", 223 wantArgs: []interface{}{int64(13), "", int8(0)}, 224 }, 225 { 226 name: "not zero columns", 227 builder: NewUpdater[User](db).Update(&User{Id: 13}).SkipZeroValue(), 228 wantSql: "UPDATE `user` SET `id`=?;", 229 wantArgs: []interface{}{int64(13)}, 230 }, 231 } 232 233 for _, tc := range testCases { 234 c := tc 235 t.Run(c.name, func(t *testing.T) { 236 query, err := tc.builder.Build() 237 assert.Equal(t, err, c.wantErr) 238 if err != nil { 239 return 240 } 241 assert.Equal(t, c.wantSql, query.SQL) 242 assert.Equal(t, c.wantArgs, query.Args) 243 }) 244 } 245 } 246 247 func TestUpdater_Exec(t *testing.T) { 248 tm := &TestModel{ 249 Id: 12, 250 FirstName: "Tom", 251 Age: 18, 252 LastName: &sql.NullString{String: "Jerry", Valid: true}, 253 } 254 testCases := []struct { 255 name string 256 u *Updater[TestModel] 257 update func(*DB, *testing.T) Result 258 wantErr error 259 mockOrder func(mock sqlmock.Sqlmock) 260 wantVal sql.Result 261 }{ 262 { 263 name: "update err", 264 update: func(db *DB, t *testing.T) Result { 265 updater := NewUpdater[TestModel](db).Set(Assign("Age", 12)) 266 result := updater.Exec(context.Background()) 267 return result 268 }, 269 mockOrder: func(mock sqlmock.Sqlmock) { 270 mock.ExpectExec("UPDATE `test_model` SET `age`="). 271 WithArgs(int64(12)). 272 WillReturnError(errors.New("no such table: test_model")) 273 }, 274 wantErr: errors.New("no such table: test_model"), 275 }, 276 { 277 name: "specify columns", 278 update: func(db *DB, t *testing.T) Result { 279 updater := NewUpdater[TestModel](db).Update(tm).Set(Columns("FirstName")) 280 result := updater.Exec(context.Background()) 281 return result 282 }, 283 mockOrder: func(mock sqlmock.Sqlmock) { 284 mock.ExpectExec("UPDATE `test_model` SET `first_name`="). 285 WithArgs("Tom").WillReturnResult(sqlmock.NewResult(100, 1)) 286 }, 287 wantVal: sqlmock.NewResult(100, 1), 288 }, 289 } 290 291 for _, tc := range testCases { 292 t.Run(tc.name, func(t *testing.T) { 293 mockDB, mock, err := sqlmock.New() 294 if err != nil { 295 t.Fatal(err) 296 } 297 db, err := OpenDS("mysql", single.NewDB(mockDB)) 298 defer func(db *DB) { _ = db.Close() }(db) 299 if err != nil { 300 t.Fatal(err) 301 } 302 tc.mockOrder(mock) 303 304 res := tc.update(db, t) 305 if res.Err() != nil { 306 assert.Equal(t, tc.wantErr, res.Err()) 307 return 308 } 309 assert.Nil(t, tc.wantErr) 310 rowsAffectedExpect, err := tc.wantVal.RowsAffected() 311 require.NoError(t, err) 312 rowsAffected, err := res.RowsAffected() 313 require.NoError(t, err) 314 315 lastInsertIdExpected, err := tc.wantVal.LastInsertId() 316 require.NoError(t, err) 317 lastInsertId, err := res.LastInsertId() 318 require.NoError(t, err) 319 assert.Equal(t, lastInsertIdExpected, lastInsertId) 320 assert.Equal(t, rowsAffectedExpect, rowsAffected) 321 322 if err = mock.ExpectationsWereMet(); err != nil { 323 t.Error(err) 324 } 325 }) 326 } 327 } 328 329 func ExampleUpdater_SkipNilValue() { 330 db := memoryDB() 331 query, _ := NewUpdater[TestModel](db).Update(&TestModel{Id: 13}).SkipNilValue().Build() 332 fmt.Println(query.String()) 333 // Output: 334 // SQL: UPDATE `test_model` SET `id`=?,`first_name`=?,`age`=?; 335 // Args: []interface {}{13, "", 0} 336 } 337 338 func ExampleUpdater_SkipZeroValue() { 339 db := memoryDB() 340 query, _ := NewUpdater[TestModel](db).Update(&TestModel{Id: 13}).SkipZeroValue().Build() 341 fmt.Println(query.String()) 342 // Output: 343 // SQL: UPDATE `test_model` SET `id`=?; 344 // Args: []interface {}{13} 345 } 346 347 type Person struct { 348 FirstName string 349 Age int8 350 LastName *sql.NullString 351 } 352 type User struct { 353 Id int64 `eorm:"auto_increment,primary_key"` 354 Person 355 }