github.com/haalcala/mattermost-server-change-repo@v0.0.0-20210713015153-16753fbeee5f/store/sqlstore/store_test.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package sqlstore 5 6 import ( 7 "fmt" 8 "os" 9 "regexp" 10 "sync" 11 "testing" 12 13 "github.com/go-sql-driver/mysql" 14 "github.com/lib/pq" 15 "github.com/mattermost/gorp" 16 _ "github.com/mattn/go-sqlite3" 17 "github.com/pkg/errors" 18 "github.com/stretchr/testify/assert" 19 "github.com/stretchr/testify/require" 20 21 "github.com/mattermost/mattermost-server/v5/model" 22 "github.com/mattermost/mattermost-server/v5/store" 23 "github.com/mattermost/mattermost-server/v5/store/searchtest" 24 "github.com/mattermost/mattermost-server/v5/store/storetest" 25 ) 26 27 type storeType struct { 28 Name string 29 SqlSettings *model.SqlSettings 30 SqlStore *SqlStore 31 Store store.Store 32 } 33 34 var storeTypes []*storeType 35 36 func newStoreType(name, driver string) *storeType { 37 return &storeType{ 38 Name: name, 39 SqlSettings: storetest.MakeSqlSettings(driver), 40 } 41 } 42 43 func StoreTest(t *testing.T, f func(*testing.T, store.Store)) { 44 defer func() { 45 if err := recover(); err != nil { 46 tearDownStores() 47 panic(err) 48 } 49 }() 50 for _, st := range storeTypes { 51 st := st 52 t.Run(st.Name, func(t *testing.T) { 53 if testing.Short() { 54 t.SkipNow() 55 } 56 f(t, st.Store) 57 }) 58 } 59 } 60 61 func StoreTestWithSearchTestEngine(t *testing.T, f func(*testing.T, store.Store, *searchtest.SearchTestEngine)) { 62 defer func() { 63 if err := recover(); err != nil { 64 tearDownStores() 65 panic(err) 66 } 67 }() 68 69 for _, st := range storeTypes { 70 st := st 71 searchTestEngine := &searchtest.SearchTestEngine{ 72 Driver: *st.SqlSettings.DriverName, 73 } 74 75 t.Run(st.Name, func(t *testing.T) { f(t, st.Store, searchTestEngine) }) 76 } 77 } 78 79 func StoreTestWithSqlStore(t *testing.T, f func(*testing.T, store.Store, storetest.SqlStore)) { 80 defer func() { 81 if err := recover(); err != nil { 82 tearDownStores() 83 panic(err) 84 } 85 }() 86 for _, st := range storeTypes { 87 st := st 88 t.Run(st.Name, func(t *testing.T) { 89 if testing.Short() { 90 t.SkipNow() 91 } 92 f(t, st.Store, st.SqlStore) 93 }) 94 } 95 } 96 97 func initStores() { 98 if testing.Short() { 99 return 100 } 101 // In CI, we already run the entire test suite for both mysql and postgres in parallel. 102 // So we just run the tests for the current database set. 103 if os.Getenv("IS_CI") == "true" { 104 switch os.Getenv("MM_SQLSETTINGS_DRIVERNAME") { 105 case "mysql": 106 storeTypes = append(storeTypes, newStoreType("MySQL", model.DATABASE_DRIVER_MYSQL)) 107 case "postgres": 108 storeTypes = append(storeTypes, newStoreType("PostgreSQL", model.DATABASE_DRIVER_POSTGRES)) 109 } 110 } else { 111 storeTypes = append(storeTypes, newStoreType("MySQL", model.DATABASE_DRIVER_MYSQL), 112 newStoreType("PostgreSQL", model.DATABASE_DRIVER_POSTGRES)) 113 } 114 115 defer func() { 116 if err := recover(); err != nil { 117 tearDownStores() 118 panic(err) 119 } 120 }() 121 var wg sync.WaitGroup 122 for _, st := range storeTypes { 123 st := st 124 wg.Add(1) 125 go func() { 126 defer wg.Done() 127 st.SqlStore = New(*st.SqlSettings, nil) 128 st.Store = st.SqlStore 129 st.Store.DropAllTables() 130 st.Store.MarkSystemRanUnitTests() 131 }() 132 } 133 wg.Wait() 134 } 135 136 var tearDownStoresOnce sync.Once 137 138 func tearDownStores() { 139 if testing.Short() { 140 return 141 } 142 tearDownStoresOnce.Do(func() { 143 var wg sync.WaitGroup 144 wg.Add(len(storeTypes)) 145 for _, st := range storeTypes { 146 st := st 147 go func() { 148 if st.Store != nil { 149 st.Store.Close() 150 } 151 if st.SqlSettings != nil { 152 storetest.CleanupSqlSettings(st.SqlSettings) 153 } 154 wg.Done() 155 }() 156 } 157 wg.Wait() 158 }) 159 } 160 161 // This test was used to consistently reproduce the race 162 // before the fix in MM-28397. 163 // Keeping it here to help avoiding future regressions. 164 func TestStoreLicenseRace(t *testing.T) { 165 settings := makeSqlSettings(model.DATABASE_DRIVER_SQLITE) 166 settings.DataSourceReplicas = []string{":memory:"} 167 settings.DataSourceSearchReplicas = []string{":memory:"} 168 store := New(*settings, nil) 169 170 wg := sync.WaitGroup{} 171 wg.Add(3) 172 173 go func() { 174 store.UpdateLicense(&model.License{}) 175 wg.Done() 176 }() 177 178 go func() { 179 store.GetReplica() 180 wg.Done() 181 }() 182 183 go func() { 184 store.GetSearchReplica() 185 wg.Done() 186 }() 187 188 wg.Wait() 189 } 190 191 func TestGetReplica(t *testing.T) { 192 t.Parallel() 193 testCases := []struct { 194 Description string 195 DataSourceReplicas []string 196 DataSourceSearchReplicas []string 197 }{ 198 { 199 "no replicas", 200 []string{}, 201 []string{}, 202 }, 203 { 204 "one source replica", 205 []string{":memory:"}, 206 []string{}, 207 }, 208 { 209 "multiple source replicas", 210 []string{":memory:", ":memory:", ":memory:"}, 211 []string{}, 212 }, 213 { 214 "one source search replica", 215 []string{}, 216 []string{":memory:"}, 217 }, 218 { 219 "multiple source search replicas", 220 []string{}, 221 []string{":memory:", ":memory:", ":memory:"}, 222 }, 223 { 224 "one source replica, one source search replica", 225 []string{":memory:"}, 226 []string{":memory:"}, 227 }, 228 { 229 "one source replica, multiple source search replicas", 230 []string{":memory:"}, 231 []string{":memory:", ":memory:", ":memory:"}, 232 }, 233 { 234 "multiple source replica, one source search replica", 235 []string{":memory:", ":memory:", ":memory:"}, 236 []string{":memory:"}, 237 }, 238 { 239 "multiple source replica, multiple source search replicas", 240 []string{":memory:", ":memory:", ":memory:"}, 241 []string{":memory:", ":memory:", ":memory:"}, 242 }, 243 } 244 245 for _, testCase := range testCases { 246 testCase := testCase 247 t.Run(testCase.Description+" with license", func(t *testing.T) { 248 t.Parallel() 249 250 settings := makeSqlSettings(model.DATABASE_DRIVER_SQLITE) 251 settings.DataSourceReplicas = testCase.DataSourceReplicas 252 settings.DataSourceSearchReplicas = testCase.DataSourceSearchReplicas 253 store := New(*settings, nil) 254 store.UpdateLicense(&model.License{}) 255 256 replicas := make(map[*gorp.DbMap]bool) 257 for i := 0; i < 5; i++ { 258 replicas[store.GetReplica()] = true 259 } 260 261 searchReplicas := make(map[*gorp.DbMap]bool) 262 for i := 0; i < 5; i++ { 263 searchReplicas[store.GetSearchReplica()] = true 264 } 265 266 if len(testCase.DataSourceReplicas) > 0 { 267 // If replicas were defined, ensure none are the master. 268 assert.Len(t, replicas, len(testCase.DataSourceReplicas)) 269 270 for replica := range replicas { 271 assert.NotSame(t, store.GetMaster(), replica) 272 } 273 274 } else if assert.Len(t, replicas, 1) { 275 // Otherwise ensure the replicas contains only the master. 276 for replica := range replicas { 277 assert.Same(t, store.GetMaster(), replica) 278 } 279 } 280 281 if len(testCase.DataSourceSearchReplicas) > 0 { 282 // If search replicas were defined, ensure none are the master nor the replicas. 283 assert.Len(t, searchReplicas, len(testCase.DataSourceSearchReplicas)) 284 285 for searchReplica := range searchReplicas { 286 assert.NotSame(t, store.GetMaster(), searchReplica) 287 for replica := range replicas { 288 assert.NotSame(t, searchReplica, replica) 289 } 290 } 291 } else if len(testCase.DataSourceReplicas) > 0 { 292 assert.Equal(t, len(replicas), len(searchReplicas)) 293 for k := range replicas { 294 assert.True(t, searchReplicas[k]) 295 } 296 } else if len(testCase.DataSourceReplicas) == 0 && assert.Len(t, searchReplicas, 1) { 297 // Otherwise ensure the search replicas contains the master. 298 for searchReplica := range searchReplicas { 299 assert.Same(t, store.GetMaster(), searchReplica) 300 } 301 } 302 }) 303 304 t.Run(testCase.Description+" without license", func(t *testing.T) { 305 t.Parallel() 306 307 settings := makeSqlSettings(model.DATABASE_DRIVER_SQLITE) 308 settings.DataSourceReplicas = testCase.DataSourceReplicas 309 settings.DataSourceSearchReplicas = testCase.DataSourceSearchReplicas 310 store := New(*settings, nil) 311 312 replicas := make(map[*gorp.DbMap]bool) 313 for i := 0; i < 5; i++ { 314 replicas[store.GetReplica()] = true 315 } 316 317 searchReplicas := make(map[*gorp.DbMap]bool) 318 for i := 0; i < 5; i++ { 319 searchReplicas[store.GetSearchReplica()] = true 320 } 321 322 if len(testCase.DataSourceReplicas) > 0 { 323 // If replicas were defined, ensure none are the master. 324 assert.Len(t, replicas, 1) 325 326 for replica := range replicas { 327 assert.Same(t, store.GetMaster(), replica) 328 } 329 330 } else if assert.Len(t, replicas, 1) { 331 // Otherwise ensure the replicas contains only the master. 332 for replica := range replicas { 333 assert.Same(t, store.GetMaster(), replica) 334 } 335 } 336 337 if len(testCase.DataSourceSearchReplicas) > 0 { 338 // If search replicas were defined, ensure none are the master nor the replicas. 339 assert.Len(t, searchReplicas, 1) 340 341 for searchReplica := range searchReplicas { 342 assert.Same(t, store.GetMaster(), searchReplica) 343 } 344 345 } else if len(testCase.DataSourceReplicas) > 0 { 346 assert.Equal(t, len(replicas), len(searchReplicas)) 347 for k := range replicas { 348 assert.True(t, searchReplicas[k]) 349 } 350 } else if assert.Len(t, searchReplicas, 1) { 351 // Otherwise ensure the search replicas contains the master. 352 for searchReplica := range searchReplicas { 353 assert.Same(t, store.GetMaster(), searchReplica) 354 } 355 } 356 }) 357 } 358 } 359 360 func TestGetDbVersion(t *testing.T) { 361 testDrivers := []string{ 362 model.DATABASE_DRIVER_POSTGRES, 363 model.DATABASE_DRIVER_MYSQL, 364 model.DATABASE_DRIVER_SQLITE, 365 } 366 367 for _, driver := range testDrivers { 368 t.Run("Should return db version for "+driver, func(t *testing.T) { 369 t.Parallel() 370 settings := makeSqlSettings(driver) 371 store := New(*settings, nil) 372 373 version, err := store.GetDbVersion(false) 374 require.NoError(t, err) 375 require.Regexp(t, regexp.MustCompile(`\d+\.\d+(\.\d+)?`), version) 376 }) 377 } 378 } 379 380 func TestGetAllConns(t *testing.T) { 381 t.Parallel() 382 testCases := []struct { 383 Description string 384 DataSourceReplicas []string 385 DataSourceSearchReplicas []string 386 ExpectedNumConnections int 387 }{ 388 { 389 "no replicas", 390 []string{}, 391 []string{}, 392 1, 393 }, 394 { 395 "one source replica", 396 []string{":memory:"}, 397 []string{}, 398 2, 399 }, 400 { 401 "multiple source replicas", 402 []string{":memory:", ":memory:", ":memory:"}, 403 []string{}, 404 4, 405 }, 406 { 407 "one source search replica", 408 []string{}, 409 []string{":memory:"}, 410 1, 411 }, 412 { 413 "multiple source search replicas", 414 []string{}, 415 []string{":memory:", ":memory:", ":memory:"}, 416 1, 417 }, 418 { 419 "one source replica, one source search replica", 420 []string{":memory:"}, 421 []string{":memory:"}, 422 2, 423 }, 424 { 425 "one source replica, multiple source search replicas", 426 []string{":memory:"}, 427 []string{":memory:", ":memory:", ":memory:"}, 428 2, 429 }, 430 { 431 "multiple source replica, one source search replica", 432 []string{":memory:", ":memory:", ":memory:"}, 433 []string{":memory:"}, 434 4, 435 }, 436 { 437 "multiple source replica, multiple source search replicas", 438 []string{":memory:", ":memory:", ":memory:"}, 439 []string{":memory:", ":memory:", ":memory:"}, 440 4, 441 }, 442 } 443 444 for _, testCase := range testCases { 445 testCase := testCase 446 t.Run(testCase.Description, func(t *testing.T) { 447 t.Parallel() 448 settings := makeSqlSettings(model.DATABASE_DRIVER_SQLITE) 449 settings.DataSourceReplicas = testCase.DataSourceReplicas 450 settings.DataSourceSearchReplicas = testCase.DataSourceSearchReplicas 451 store := New(*settings, nil) 452 453 assert.Len(t, store.GetAllConns(), testCase.ExpectedNumConnections) 454 }) 455 } 456 } 457 458 func TestIsDuplicate(t *testing.T) { 459 testErrors := map[error]bool{ 460 &pq.Error{Code: "42P06"}: false, 461 &pq.Error{Code: PGDupTableErrorCode}: true, 462 &mysql.MySQLError{Number: uint16(1000)}: false, 463 &mysql.MySQLError{Number: MySQLDupTableErrorCode}: true, 464 errors.New("Random error"): false, 465 } 466 467 for err, expected := range testErrors { 468 t.Run(fmt.Sprintf("Should return %t for %s", expected, err.Error()), func(t *testing.T) { 469 t.Parallel() 470 assert.Equal(t, expected, IsDuplicate(err)) 471 }) 472 } 473 } 474 475 func TestVersionString(t *testing.T) { 476 versions := []struct { 477 input int 478 output string 479 }{ 480 { 481 input: 100000, 482 output: "10.0", 483 }, 484 { 485 input: 90603, 486 output: "9.603", 487 }, 488 { 489 input: 120005, 490 output: "12.5", 491 }, 492 } 493 494 for _, v := range versions { 495 out := VersionString(v.input) 496 assert.Equal(t, v.output, out) 497 } 498 } 499 500 func makeSqlSettings(driver string) *model.SqlSettings { 501 switch driver { 502 case model.DATABASE_DRIVER_POSTGRES: 503 return storetest.MakeSqlSettings(driver) 504 case model.DATABASE_DRIVER_MYSQL: 505 return storetest.MakeSqlSettings(driver) 506 case model.DATABASE_DRIVER_SQLITE: 507 return makeSqliteSettings() 508 } 509 510 return nil 511 } 512 513 func makeSqliteSettings() *model.SqlSettings { 514 driverName := model.DATABASE_DRIVER_SQLITE 515 dataSource := ":memory:" 516 maxIdleConns := 1 517 connMaxLifetimeMilliseconds := 3600000 518 connMaxIdleTimeMilliseconds := 300000 519 maxOpenConns := 1 520 queryTimeout := 5 521 522 return &model.SqlSettings{ 523 DriverName: &driverName, 524 DataSource: &dataSource, 525 MaxIdleConns: &maxIdleConns, 526 ConnMaxLifetimeMilliseconds: &connMaxLifetimeMilliseconds, 527 ConnMaxIdleTimeMilliseconds: &connMaxIdleTimeMilliseconds, 528 MaxOpenConns: &maxOpenConns, 529 QueryTimeout: &queryTimeout, 530 } 531 }