github.com/adacta-ru/mattermost-server/v6@v6.0.0/config/database_test.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package config_test 5 6 import ( 7 "bytes" 8 "encoding/json" 9 "fmt" 10 "os" 11 "strings" 12 "testing" 13 "time" 14 15 "github.com/jmoiron/sqlx" 16 "github.com/stretchr/testify/assert" 17 "github.com/stretchr/testify/require" 18 19 "github.com/adacta-ru/mattermost-server/v6/config" 20 "github.com/adacta-ru/mattermost-server/v6/model" 21 ) 22 23 func getDsn(driver string, source string) string { 24 if driver == model.DATABASE_DRIVER_MYSQL { 25 return driver + "://" + source 26 } 27 return source 28 } 29 30 func setupConfigDatabase(t *testing.T, cfg *model.Config, files map[string][]byte) (string, func()) { 31 if testing.Short() { 32 t.SkipNow() 33 } 34 t.Helper() 35 os.Clearenv() 36 truncateTables(t) 37 38 cfgData, err := config.MarshalConfig(cfg) 39 require.NoError(t, err) 40 41 db := sqlx.NewDb(mainHelper.GetSQLStore().GetMaster().Db, *mainHelper.GetSQLSettings().DriverName) 42 err = config.InitializeConfigurationsTable(db) 43 require.NoError(t, err) 44 45 id := model.NewId() 46 _, err = db.NamedExec("INSERT INTO Configurations (Id, Value, CreateAt, Active) VALUES(:Id, :Value, :CreateAt, TRUE)", map[string]interface{}{ 47 "Id": id, 48 "Value": cfgData, 49 "CreateAt": model.GetMillis(), 50 }) 51 require.NoError(t, err) 52 53 for name, data := range files { 54 params := map[string]interface{}{ 55 "name": name, 56 "data": data, 57 "create_at": model.GetMillis(), 58 "update_at": model.GetMillis(), 59 } 60 61 _, err = db.NamedExec("INSERT INTO ConfigurationFiles (Name, Data, CreateAt, UpdateAt) VALUES (:name, :data, :create_at, :update_at)", params) 62 require.NoError(t, err) 63 } 64 65 return id, func() { 66 truncateTables(t) 67 } 68 } 69 70 // getActualDatabaseConfig returns the active configuration in the database without relying on a config store. 71 func getActualDatabaseConfig(t *testing.T) (string, *model.Config) { 72 t.Helper() 73 74 if *mainHelper.GetSQLSettings().DriverName == "postgres" { 75 var actual struct { 76 ID string `db:"id"` 77 Value []byte `db:"value"` 78 } 79 db := sqlx.NewDb(mainHelper.GetSQLStore().GetMaster().Db, *mainHelper.GetSQLSettings().DriverName) 80 err := db.Get(&actual, "SELECT Id, Value FROM Configurations WHERE Active") 81 require.NoError(t, err) 82 83 var actualCfg *model.Config 84 err = json.Unmarshal(actual.Value, &actualCfg) 85 require.Nil(t, err) 86 return actual.ID, actualCfg 87 } 88 var actual struct { 89 ID string `db:"Id"` 90 Value []byte `db:"Value"` 91 } 92 db := sqlx.NewDb(mainHelper.GetSQLStore().GetMaster().Db, *mainHelper.GetSQLSettings().DriverName) 93 err := db.Get(&actual, "SELECT Id, Value FROM Configurations WHERE Active") 94 require.NoError(t, err) 95 96 var actualCfg *model.Config 97 err = json.Unmarshal(actual.Value, &actualCfg) 98 require.Nil(t, err) 99 return actual.ID, actualCfg 100 } 101 102 // assertDatabaseEqualsConfig verifies the active in-database configuration equals the given config. 103 func assertDatabaseEqualsConfig(t *testing.T, expectedCfg *model.Config) { 104 t.Helper() 105 106 _, actualCfg := getActualDatabaseConfig(t) 107 assert.Equal(t, expectedCfg, actualCfg) 108 } 109 110 // assertDatabaseNotEqualsConfig verifies the in-database configuration does not equal the given config. 111 func assertDatabaseNotEqualsConfig(t *testing.T, expectedCfg *model.Config) { 112 t.Helper() 113 114 _, actualCfg := getActualDatabaseConfig(t) 115 assert.NotEqual(t, expectedCfg, actualCfg) 116 } 117 118 func newTestDatabaseStore(t *testing.T, customDefaults *model.Config) (*config.Store, error) { 119 sqlSettings := mainHelper.GetSQLSettings() 120 dss, err := config.NewDatabaseStore(getDsn(*sqlSettings.DriverName, *sqlSettings.DataSource)) 121 require.NoError(t, err) 122 123 cStore, err := config.NewStoreFromBacking(dss, customDefaults) 124 require.NoError(t, err) 125 126 return cStore, nil 127 } 128 129 func TestDatabaseStoreNew(t *testing.T) { 130 if testing.Short() { 131 t.SkipNow() 132 } 133 sqlSettings := mainHelper.GetSQLSettings() 134 135 t.Run("no existing configuration - initialization required", func(t *testing.T) { 136 ds, err := newTestDatabaseStore(t, nil) 137 require.NoError(t, err) 138 defer ds.Close() 139 140 assert.Equal(t, "", *ds.Get().ServiceSettings.SiteURL) 141 }) 142 143 t.Run("no existing configuration with custom defaults", func(t *testing.T) { 144 truncateTables(t) 145 ds, err := newTestDatabaseStore(t, customConfigDefaults) 146 require.NoError(t, err) 147 defer ds.Close() 148 149 assert.Equal(t, *customConfigDefaults.ServiceSettings.SiteURL, *ds.Get().ServiceSettings.SiteURL) 150 assert.Equal(t, *customConfigDefaults.DisplaySettings.ExperimentalTimezone, *ds.Get().DisplaySettings.ExperimentalTimezone) 151 }) 152 153 t.Run("existing config, initialization required", func(t *testing.T) { 154 _, tearDown := setupConfigDatabase(t, testConfig, nil) 155 defer tearDown() 156 157 ds, err := newTestDatabaseStore(t, nil) 158 require.NoError(t, err) 159 defer ds.Close() 160 161 assert.Equal(t, "http://TestStoreNew", *ds.Get().ServiceSettings.SiteURL) 162 assertDatabaseNotEqualsConfig(t, testConfig) 163 }) 164 165 t.Run("existing config with custom defaults, initialization required", func(t *testing.T) { 166 _, tearDown := setupConfigDatabase(t, testConfig, nil) 167 defer tearDown() 168 169 ds, err := newTestDatabaseStore(t, customConfigDefaults) 170 require.NoError(t, err) 171 defer ds.Close() 172 173 // already existing value should not be overwritten by the 174 // custom default value 175 assert.Equal(t, "http://TestStoreNew", *ds.Get().ServiceSettings.SiteURL) 176 // not existing value should be overwritten by the custom 177 // default value 178 assert.Equal(t, *customConfigDefaults.DisplaySettings.ExperimentalTimezone, *ds.Get().DisplaySettings.ExperimentalTimezone) 179 assertDatabaseNotEqualsConfig(t, testConfig) 180 }) 181 182 t.Run("already minimally configured", func(t *testing.T) { 183 _, tearDown := setupConfigDatabase(t, minimalConfig, nil) 184 defer tearDown() 185 186 ds, err := newTestDatabaseStore(t, nil) 187 require.NoError(t, err) 188 defer ds.Close() 189 190 assert.Equal(t, "http://minimal", *ds.Get().ServiceSettings.SiteURL) 191 assertDatabaseEqualsConfig(t, minimalConfig) 192 }) 193 194 t.Run("already minimally configured with custom defaults", func(t *testing.T) { 195 _, tearDown := setupConfigDatabase(t, minimalConfig, nil) 196 defer tearDown() 197 198 ds, err := newTestDatabaseStore(t, customConfigDefaults) 199 require.NoError(t, err) 200 defer ds.Close() 201 202 // as the whole config has default values already, custom 203 // defaults should have no effect 204 assert.Equal(t, "http://minimal", *ds.Get().ServiceSettings.SiteURL) 205 assert.NotEqual(t, *customConfigDefaults.DisplaySettings.ExperimentalTimezone, *ds.Get().DisplaySettings.ExperimentalTimezone) 206 assertDatabaseEqualsConfig(t, minimalConfig) 207 }) 208 209 t.Run("invalid url", func(t *testing.T) { 210 _, err := config.NewDatabaseStore("") 211 require.Error(t, err) 212 213 _, err = config.NewDatabaseStore("mysql") 214 require.Error(t, err) 215 }) 216 217 t.Run("unsupported scheme", func(t *testing.T) { 218 _, err := config.NewDatabaseStore("invalid") 219 require.Error(t, err) 220 }) 221 222 t.Run("unsupported scheme with valid data source", func(t *testing.T) { 223 _, err := config.NewDatabaseStore(fmt.Sprintf("invalid://%s", *sqlSettings.DataSource)) 224 require.Error(t, err) 225 }) 226 } 227 228 func TestDatabaseStoreGet(t *testing.T) { 229 _, tearDown := setupConfigDatabase(t, testConfig, nil) 230 defer tearDown() 231 232 ds, err := newTestDatabaseStore(t, nil) 233 require.NoError(t, err) 234 defer ds.Close() 235 236 cfg := ds.Get() 237 assert.Equal(t, "http://TestStoreNew", *cfg.ServiceSettings.SiteURL) 238 239 cfg2 := ds.Get() 240 assert.Equal(t, "http://TestStoreNew", *cfg.ServiceSettings.SiteURL) 241 242 assert.True(t, cfg == cfg2, "Get() returned different configuration instances") 243 } 244 245 func TestDatabaseStoreGetEnivironmentOverrides(t *testing.T) { 246 t.Run("get override for a string variable", func(t *testing.T) { 247 _, tearDown := setupConfigDatabase(t, testConfig, nil) 248 defer tearDown() 249 250 ds, err := newTestDatabaseStore(t, nil) 251 require.NoError(t, err) 252 defer ds.Close() 253 254 assert.Equal(t, "http://TestStoreNew", *ds.Get().ServiceSettings.SiteURL) 255 assert.Empty(t, ds.GetEnvironmentOverrides()) 256 257 os.Setenv("MM_SERVICESETTINGS_SITEURL", "http://override") 258 defer os.Unsetenv("MM_SERVICESETTINGS_SITEURL") 259 260 ds, err = newTestDatabaseStore(t, nil) 261 require.NoError(t, err) 262 defer ds.Close() 263 264 assert.Equal(t, "http://override", *ds.Get().ServiceSettings.SiteURL) 265 assert.Equal(t, map[string]interface{}{"ServiceSettings": map[string]interface{}{"SiteURL": true}}, ds.GetEnvironmentOverrides()) 266 }) 267 268 t.Run("get override for a string variable with a custom default value", func(t *testing.T) { 269 _, tearDown := setupConfigDatabase(t, testConfig, nil) 270 defer tearDown() 271 272 ds, err := newTestDatabaseStore(t, customConfigDefaults) 273 require.NoError(t, err) 274 defer ds.Close() 275 276 assert.Equal(t, "http://TestStoreNew", *ds.Get().ServiceSettings.SiteURL) 277 assert.Empty(t, ds.GetEnvironmentOverrides()) 278 279 os.Setenv("MM_SERVICESETTINGS_SITEURL", "http://override") 280 defer os.Unsetenv("MM_SERVICESETTINGS_SITEURL") 281 282 ds, err = newTestDatabaseStore(t, customConfigDefaults) 283 require.NoError(t, err) 284 defer ds.Close() 285 286 // environment override should take priority over the custom default value 287 assert.Equal(t, "http://override", *ds.Get().ServiceSettings.SiteURL) 288 assert.Equal(t, map[string]interface{}{"ServiceSettings": map[string]interface{}{"SiteURL": true}}, ds.GetEnvironmentOverrides()) 289 }) 290 291 t.Run("get override for a bool variable", func(t *testing.T) { 292 _, tearDown := setupConfigDatabase(t, testConfig, nil) 293 defer tearDown() 294 295 ds, err := newTestDatabaseStore(t, nil) 296 require.NoError(t, err) 297 defer ds.Close() 298 299 assert.Equal(t, false, *ds.Get().PluginSettings.EnableUploads) 300 assert.Empty(t, ds.GetEnvironmentOverrides()) 301 302 os.Setenv("MM_PLUGINSETTINGS_ENABLEUPLOADS", "true") 303 defer os.Unsetenv("MM_PLUGINSETTINGS_ENABLEUPLOADS") 304 305 ds, err = newTestDatabaseStore(t, nil) 306 require.NoError(t, err) 307 defer ds.Close() 308 309 assert.Equal(t, true, *ds.Get().PluginSettings.EnableUploads) 310 assert.Equal(t, map[string]interface{}{"PluginSettings": map[string]interface{}{"EnableUploads": true}}, ds.GetEnvironmentOverrides()) 311 }) 312 313 t.Run("get override for an int variable", func(t *testing.T) { 314 _, tearDown := setupConfigDatabase(t, testConfig, nil) 315 defer tearDown() 316 317 ds, err := newTestDatabaseStore(t, nil) 318 require.NoError(t, err) 319 defer ds.Close() 320 321 assert.Equal(t, model.TEAM_SETTINGS_DEFAULT_MAX_USERS_PER_TEAM, *ds.Get().TeamSettings.MaxUsersPerTeam) 322 assert.Empty(t, ds.GetEnvironmentOverrides()) 323 324 os.Setenv("MM_TEAMSETTINGS_MAXUSERSPERTEAM", "3000") 325 defer os.Unsetenv("MM_TEAMSETTINGS_MAXUSERSPERTEAM") 326 327 ds, err = newTestDatabaseStore(t, nil) 328 require.NoError(t, err) 329 defer ds.Close() 330 331 assert.Equal(t, 3000, *ds.Get().TeamSettings.MaxUsersPerTeam) 332 assert.Equal(t, map[string]interface{}{"TeamSettings": map[string]interface{}{"MaxUsersPerTeam": true}}, ds.GetEnvironmentOverrides()) 333 }) 334 335 t.Run("get override for an int64 variable", func(t *testing.T) { 336 _, tearDown := setupConfigDatabase(t, testConfig, nil) 337 defer tearDown() 338 339 ds, err := newTestDatabaseStore(t, nil) 340 require.NoError(t, err) 341 defer ds.Close() 342 343 assert.Equal(t, int64(63072000), *ds.Get().ServiceSettings.TLSStrictTransportMaxAge) 344 assert.Empty(t, ds.GetEnvironmentOverrides()) 345 346 os.Setenv("MM_SERVICESETTINGS_TLSSTRICTTRANSPORTMAXAGE", "123456") 347 defer os.Unsetenv("MM_SERVICESETTINGS_TLSSTRICTTRANSPORTMAXAGE") 348 349 ds, err = newTestDatabaseStore(t, nil) 350 require.NoError(t, err) 351 defer ds.Close() 352 353 assert.Equal(t, int64(123456), *ds.Get().ServiceSettings.TLSStrictTransportMaxAge) 354 assert.Equal(t, map[string]interface{}{"ServiceSettings": map[string]interface{}{"TLSStrictTransportMaxAge": true}}, ds.GetEnvironmentOverrides()) 355 }) 356 357 t.Run("get override for a slice variable - one value", func(t *testing.T) { 358 _, tearDown := setupConfigDatabase(t, testConfig, nil) 359 defer tearDown() 360 361 ds, err := newTestDatabaseStore(t, nil) 362 require.NoError(t, err) 363 defer ds.Close() 364 365 assert.Equal(t, []string{}, ds.Get().SqlSettings.DataSourceReplicas) 366 assert.Empty(t, ds.GetEnvironmentOverrides()) 367 368 os.Setenv("MM_SQLSETTINGS_DATASOURCEREPLICAS", "user:pwd@db:5432/test-db") 369 defer os.Unsetenv("MM_SQLSETTINGS_DATASOURCEREPLICAS") 370 371 ds, err = newTestDatabaseStore(t, nil) 372 require.NoError(t, err) 373 defer ds.Close() 374 375 assert.Equal(t, []string{"user:pwd@db:5432/test-db"}, ds.Get().SqlSettings.DataSourceReplicas) 376 assert.Equal(t, map[string]interface{}{"SqlSettings": map[string]interface{}{"DataSourceReplicas": true}}, ds.GetEnvironmentOverrides()) 377 }) 378 379 t.Run("get override for a slice variable - three values", func(t *testing.T) { 380 // This should work, but Viper (or we) don't parse environment variables to turn strings with spaces into slices. 381 t.Skip("not implemented yet") 382 383 _, tearDown := setupConfigDatabase(t, testConfig, nil) 384 defer tearDown() 385 386 ds, err := newTestDatabaseStore(t, nil) 387 require.NoError(t, err) 388 defer ds.Close() 389 390 assert.Equal(t, []string{}, ds.Get().SqlSettings.DataSourceReplicas) 391 assert.Empty(t, ds.GetEnvironmentOverrides()) 392 393 os.Setenv("MM_SQLSETTINGS_DATASOURCEREPLICAS", "user:pwd@db:5432/test-db user:pwd@db2:5433/test-db2 user:pwd@db3:5434/test-db3") 394 defer os.Unsetenv("MM_SQLSETTINGS_DATASOURCEREPLICAS") 395 396 ds, err = newTestDatabaseStore(t, nil) 397 require.NoError(t, err) 398 defer ds.Close() 399 400 assert.Equal(t, []string{"user:pwd@db:5432/test-db", "user:pwd@db2:5433/test-db2", "user:pwd@db3:5434/test-db3"}, ds.Get().SqlSettings.DataSourceReplicas) 401 assert.Equal(t, map[string]interface{}{"SqlSettings": map[string]interface{}{"DataSourceReplicas": true}}, ds.GetEnvironmentOverrides()) 402 }) 403 } 404 405 func TestDatabaseStoreSet(t *testing.T) { 406 if testing.Short() { 407 t.SkipNow() 408 } 409 410 t.Run("set same pointer value", func(t *testing.T) { 411 t.Skip("not yet implemented") 412 413 _, tearDown := setupConfigDatabase(t, emptyConfig, nil) 414 defer tearDown() 415 416 ds, err := newTestDatabaseStore(t, nil) 417 require.NoError(t, err) 418 defer ds.Close() 419 420 _, err = ds.Set(ds.Get()) 421 if assert.Error(t, err) { 422 assert.EqualError(t, err, "old configuration modified instead of cloning") 423 } 424 }) 425 426 t.Run("defaults required", func(t *testing.T) { 427 _, tearDown := setupConfigDatabase(t, minimalConfig, nil) 428 defer tearDown() 429 430 ds, err := newTestDatabaseStore(t, nil) 431 require.NoError(t, err) 432 defer ds.Close() 433 434 newCfg := &model.Config{} 435 436 _, err = ds.Set(newCfg) 437 require.NoError(t, err) 438 439 assert.Equal(t, "", *ds.Get().ServiceSettings.SiteURL) 440 }) 441 442 t.Run("desanitization required", func(t *testing.T) { 443 _, tearDown := setupConfigDatabase(t, ldapConfig, nil) 444 defer tearDown() 445 446 ds, err := newTestDatabaseStore(t, nil) 447 require.NoError(t, err) 448 defer ds.Close() 449 450 newCfg := &model.Config{} 451 newCfg.LdapSettings.BindPassword = sToP(model.FAKE_SETTING) 452 453 _, err = ds.Set(newCfg) 454 require.NoError(t, err) 455 456 assert.Equal(t, "password", *ds.Get().LdapSettings.BindPassword) 457 }) 458 459 t.Run("invalid", func(t *testing.T) { 460 _, tearDown := setupConfigDatabase(t, emptyConfig, nil) 461 defer tearDown() 462 463 ds, err := newTestDatabaseStore(t, nil) 464 require.NoError(t, err) 465 defer ds.Close() 466 467 newCfg := &model.Config{} 468 newCfg.ServiceSettings.SiteURL = sToP("invalid") 469 470 _, err = ds.Set(newCfg) 471 if assert.Error(t, err) { 472 assert.EqualError(t, err, "new configuration is invalid: Config.IsValid: model.config.is_valid.site_url.app_error, ") 473 } 474 475 assert.Equal(t, "", *ds.Get().ServiceSettings.SiteURL) 476 }) 477 478 t.Run("duplicate ignored", func(t *testing.T) { 479 _, tearDown := setupConfigDatabase(t, minimalConfig, nil) 480 defer tearDown() 481 482 ds, err := newTestDatabaseStore(t, nil) 483 require.NoError(t, err) 484 defer ds.Close() 485 486 _, err = ds.Set(ds.Get()) 487 require.NoError(t, err) 488 489 beforeID, _ := getActualDatabaseConfig(t) 490 _, err = ds.Set(ds.Get()) 491 require.NoError(t, err) 492 493 afterID, _ := getActualDatabaseConfig(t) 494 assert.Equal(t, beforeID, afterID, "new record should not have been written") 495 }) 496 497 t.Run("read-only ignored", func(t *testing.T) { 498 _, tearDown := setupConfigDatabase(t, readOnlyConfig, nil) 499 defer tearDown() 500 501 ds, err := newTestDatabaseStore(t, nil) 502 require.NoError(t, err) 503 defer ds.Close() 504 505 newCfg := &model.Config{ 506 ServiceSettings: model.ServiceSettings{ 507 SiteURL: sToP("http://new"), 508 }, 509 } 510 511 _, err = ds.Set(newCfg) 512 require.NoError(t, err) 513 514 assert.Equal(t, "http://new", *ds.Get().ServiceSettings.SiteURL) 515 }) 516 517 t.Run("set with automatic save", func(t *testing.T) { 518 _, tearDown := setupConfigDatabase(t, minimalConfig, nil) 519 defer tearDown() 520 521 ds, err := newTestDatabaseStore(t, nil) 522 require.NoError(t, err) 523 defer ds.Close() 524 525 newCfg := &model.Config{ 526 ServiceSettings: model.ServiceSettings{ 527 SiteURL: sToP("http://new"), 528 }, 529 } 530 531 _, err = ds.Set(newCfg) 532 require.NoError(t, err) 533 534 err = ds.Load() 535 require.NoError(t, err) 536 537 assert.Equal(t, "http://new", *ds.Get().ServiceSettings.SiteURL) 538 }) 539 540 t.Run("persist failed", func(t *testing.T) { 541 _, tearDown := setupConfigDatabase(t, emptyConfig, nil) 542 defer tearDown() 543 544 ds, err := newTestDatabaseStore(t, nil) 545 require.NoError(t, err) 546 defer ds.Close() 547 548 sqlSettings := mainHelper.GetSQLSettings() 549 db := sqlx.NewDb(mainHelper.GetSQLStore().GetMaster().Db, *sqlSettings.DriverName) 550 _, err = db.Exec("DROP TABLE Configurations") 551 require.NoError(t, err) 552 553 newCfg := &model.Config{} 554 555 _, err = ds.Set(newCfg) 556 require.Error(t, err) 557 assert.True(t, strings.HasPrefix(err.Error(), "failed to persist: failed to query active configuration"), "unexpected error: "+err.Error()) 558 559 assert.Equal(t, "", *ds.Get().ServiceSettings.SiteURL) 560 }) 561 562 t.Run("persist failed: too long", func(t *testing.T) { 563 if *mainHelper.Settings.DriverName == "postgres" { 564 t.Skip("No limit for postgres") 565 } 566 _, tearDown := setupConfigDatabase(t, emptyConfig, nil) 567 defer tearDown() 568 569 ds, err := newTestDatabaseStore(t, nil) 570 require.NoError(t, err) 571 defer ds.Close() 572 573 longSiteURL := fmt.Sprintf("http://%s", strings.Repeat("a", config.MaxWriteLength)) 574 newCfg := emptyConfig.Clone() 575 newCfg.ServiceSettings.SiteURL = sToP(longSiteURL) 576 577 _, err = ds.Set(newCfg) 578 require.Error(t, err) 579 assert.True(t, strings.HasPrefix(err.Error(), "failed to persist: marshalled configuration failed length check: value is too long"), "unexpected error: "+err.Error()) 580 }) 581 582 t.Run("listeners notified", func(t *testing.T) { 583 activeID, tearDown := setupConfigDatabase(t, emptyConfig, nil) 584 defer tearDown() 585 586 ds, err := newTestDatabaseStore(t, nil) 587 require.NoError(t, err) 588 defer ds.Close() 589 590 called := make(chan bool, 1) 591 callback := func(oldfg, newCfg *model.Config) { 592 called <- true 593 } 594 ds.AddListener(callback) 595 596 newCfg := &model.Config{} 597 598 _, err = ds.Set(newCfg) 599 require.NoError(t, err) 600 601 id, _ := getActualDatabaseConfig(t) 602 assert.NotEqual(t, activeID, id, "new record should have been written") 603 604 require.True(t, wasCalled(called, 5*time.Second), "callback should have been called when config written") 605 }) 606 } 607 608 func TestDatabaseStoreLoad(t *testing.T) { 609 if testing.Short() { 610 t.SkipNow() 611 } 612 613 t.Run("active configuration no longer exists", func(t *testing.T) { 614 _, tearDown := setupConfigDatabase(t, emptyConfig, nil) 615 defer tearDown() 616 617 ds, err := newTestDatabaseStore(t, nil) 618 require.NoError(t, err) 619 defer ds.Close() 620 621 truncateTables(t) 622 623 err = ds.Load() 624 require.NoError(t, err) 625 assertDatabaseNotEqualsConfig(t, emptyConfig) 626 }) 627 628 t.Run("honour environment", func(t *testing.T) { 629 _, tearDown := setupConfigDatabase(t, minimalConfig, nil) 630 defer tearDown() 631 632 ds, err := newTestDatabaseStore(t, nil) 633 require.NoError(t, err) 634 defer ds.Close() 635 636 assert.Equal(t, "http://minimal", *ds.Get().ServiceSettings.SiteURL) 637 638 os.Setenv("MM_SERVICESETTINGS_SITEURL", "http://override") 639 defer os.Unsetenv("MM_SERVICESETTINGS_SITEURL") 640 641 err = ds.Load() 642 require.NoError(t, err) 643 assert.Equal(t, "http://override", *ds.Get().ServiceSettings.SiteURL) 644 assert.Equal(t, map[string]interface{}{"ServiceSettings": map[string]interface{}{"SiteURL": true}}, ds.GetEnvironmentOverrides()) 645 }) 646 647 t.Run("do not persist environment variables - string", func(t *testing.T) { 648 _, tearDown := setupConfigDatabase(t, minimalConfig, nil) 649 defer tearDown() 650 651 os.Setenv("MM_SERVICESETTINGS_SITEURL", "http://overridePersistEnvVariables") 652 defer os.Unsetenv("MM_SERVICESETTINGS_SITEURL") 653 654 ds, err := newTestDatabaseStore(t, nil) 655 require.NoError(t, err) 656 defer ds.Close() 657 658 _, err = ds.Set(ds.Get()) 659 require.NoError(t, err) 660 661 assert.Equal(t, "http://overridePersistEnvVariables", *ds.Get().ServiceSettings.SiteURL) 662 assert.Equal(t, map[string]interface{}{"ServiceSettings": map[string]interface{}{"SiteURL": true}}, ds.GetEnvironmentOverrides()) 663 // check that in DB config does not include overwritten variable 664 _, actualConfig := getActualDatabaseConfig(t) 665 assert.Equal(t, "http://minimal", *actualConfig.ServiceSettings.SiteURL) 666 }) 667 668 t.Run("do not persist environment variables - boolean", func(t *testing.T) { 669 _, tearDown := setupConfigDatabase(t, minimalConfig, nil) 670 defer tearDown() 671 672 os.Setenv("MM_PLUGINSETTINGS_ENABLEUPLOADS", "true") 673 defer os.Unsetenv("MM_PLUGINSETTINGS_ENABLEUPLOADS") 674 675 ds, err := newTestDatabaseStore(t, nil) 676 require.NoError(t, err) 677 defer ds.Close() 678 679 assert.Equal(t, true, *ds.Get().PluginSettings.EnableUploads) 680 681 _, err = ds.Set(ds.Get()) 682 require.NoError(t, err) 683 684 assert.Equal(t, true, *ds.Get().PluginSettings.EnableUploads) 685 assert.Equal(t, map[string]interface{}{"PluginSettings": map[string]interface{}{"EnableUploads": true}}, ds.GetEnvironmentOverrides()) 686 // check that in DB config does not include overwritten variable 687 _, actualConfig := getActualDatabaseConfig(t) 688 assert.Equal(t, false, *actualConfig.PluginSettings.EnableUploads) 689 }) 690 691 t.Run("do not persist environment variables - int", func(t *testing.T) { 692 _, tearDown := setupConfigDatabase(t, minimalConfig, nil) 693 defer tearDown() 694 695 os.Setenv("MM_TEAMSETTINGS_MAXUSERSPERTEAM", "3000") 696 defer os.Unsetenv("MM_TEAMSETTINGS_MAXUSERSPERTEAM") 697 698 ds, err := newTestDatabaseStore(t, nil) 699 require.NoError(t, err) 700 defer ds.Close() 701 702 assert.Equal(t, 3000, *ds.Get().TeamSettings.MaxUsersPerTeam) 703 704 _, err = ds.Set(ds.Get()) 705 require.NoError(t, err) 706 707 assert.Equal(t, 3000, *ds.Get().TeamSettings.MaxUsersPerTeam) 708 assert.Equal(t, map[string]interface{}{"TeamSettings": map[string]interface{}{"MaxUsersPerTeam": true}}, ds.GetEnvironmentOverrides()) 709 // check that in DB config does not include overwritten variable 710 _, actualConfig := getActualDatabaseConfig(t) 711 assert.Equal(t, model.TEAM_SETTINGS_DEFAULT_MAX_USERS_PER_TEAM, *actualConfig.TeamSettings.MaxUsersPerTeam) 712 }) 713 714 t.Run("do not persist environment variables - int64", func(t *testing.T) { 715 _, tearDown := setupConfigDatabase(t, minimalConfig, nil) 716 defer tearDown() 717 718 os.Setenv("MM_SERVICESETTINGS_TLSSTRICTTRANSPORTMAXAGE", "123456") 719 defer os.Unsetenv("MM_SERVICESETTINGS_TLSSTRICTTRANSPORTMAXAGE") 720 721 ds, err := newTestDatabaseStore(t, nil) 722 require.NoError(t, err) 723 defer ds.Close() 724 725 assert.Equal(t, int64(123456), *ds.Get().ServiceSettings.TLSStrictTransportMaxAge) 726 727 _, err = ds.Set(ds.Get()) 728 require.NoError(t, err) 729 730 assert.Equal(t, int64(123456), *ds.Get().ServiceSettings.TLSStrictTransportMaxAge) 731 assert.Equal(t, map[string]interface{}{"ServiceSettings": map[string]interface{}{"TLSStrictTransportMaxAge": true}}, ds.GetEnvironmentOverrides()) 732 // check that in DB config does not include overwritten variable 733 _, actualConfig := getActualDatabaseConfig(t) 734 assert.Equal(t, int64(63072000), *actualConfig.ServiceSettings.TLSStrictTransportMaxAge) 735 }) 736 737 t.Run("do not persist environment variables - string slice beginning with default", func(t *testing.T) { 738 _, tearDown := setupConfigDatabase(t, minimalConfig, nil) 739 defer tearDown() 740 741 os.Setenv("MM_SQLSETTINGS_DATASOURCEREPLICAS", "user:pwd@db:5432/test-db") 742 defer os.Unsetenv("MM_SQLSETTINGS_DATASOURCEREPLICAS") 743 744 ds, err := newTestDatabaseStore(t, nil) 745 require.NoError(t, err) 746 defer ds.Close() 747 748 assert.Equal(t, []string{"user:pwd@db:5432/test-db"}, ds.Get().SqlSettings.DataSourceReplicas) 749 750 _, err = ds.Set(ds.Get()) 751 require.NoError(t, err) 752 753 assert.Equal(t, []string{"user:pwd@db:5432/test-db"}, ds.Get().SqlSettings.DataSourceReplicas) 754 assert.Equal(t, map[string]interface{}{"SqlSettings": map[string]interface{}{"DataSourceReplicas": true}}, ds.GetEnvironmentOverrides()) 755 // check that in DB config does not include overwritten variable 756 _, actualConfig := getActualDatabaseConfig(t) 757 assert.Equal(t, []string{}, actualConfig.SqlSettings.DataSourceReplicas) 758 }) 759 760 t.Run("do not persist environment variables - string slice beginning with slice of three", func(t *testing.T) { 761 modifiedMinimalConfig := minimalConfig.Clone() 762 modifiedMinimalConfig.SqlSettings.DataSourceReplicas = []string{"user:pwd@db:5432/test-db", "user:pwd@db2:5433/test-db2", "user:pwd@db3:5434/test-db3"} 763 _, tearDown := setupConfigDatabase(t, modifiedMinimalConfig, nil) 764 defer tearDown() 765 766 os.Setenv("MM_SQLSETTINGS_DATASOURCEREPLICAS", "user:pwd@db:5432/test-db") 767 defer os.Unsetenv("MM_SQLSETTINGS_DATASOURCEREPLICAS") 768 769 ds, err := newTestDatabaseStore(t, nil) 770 require.NoError(t, err) 771 defer ds.Close() 772 773 assert.Equal(t, []string{"user:pwd@db:5432/test-db"}, ds.Get().SqlSettings.DataSourceReplicas) 774 775 _, err = ds.Set(ds.Get()) 776 require.NoError(t, err) 777 778 assert.Equal(t, []string{"user:pwd@db:5432/test-db"}, ds.Get().SqlSettings.DataSourceReplicas) 779 assert.Equal(t, map[string]interface{}{"SqlSettings": map[string]interface{}{"DataSourceReplicas": true}}, ds.GetEnvironmentOverrides()) 780 // check that in DB config does not include overwritten variable 781 _, actualConfig := getActualDatabaseConfig(t) 782 assert.Equal(t, []string{"user:pwd@db:5432/test-db", "user:pwd@db2:5433/test-db2", "user:pwd@db3:5434/test-db3"}, actualConfig.SqlSettings.DataSourceReplicas) 783 }) 784 785 t.Run("invalid", func(t *testing.T) { 786 _, tearDown := setupConfigDatabase(t, emptyConfig, nil) 787 defer tearDown() 788 789 ds, err := newTestDatabaseStore(t, nil) 790 require.NoError(t, err) 791 defer ds.Close() 792 793 cfgData, err := config.MarshalConfig(invalidConfig) 794 require.NoError(t, err) 795 796 sqlSettings := mainHelper.GetSQLSettings() 797 db := sqlx.NewDb(mainHelper.GetSQLStore().GetMaster().Db, *sqlSettings.DriverName) 798 truncateTables(t) 799 id := model.NewId() 800 _, err = db.NamedExec("INSERT INTO Configurations (Id, Value, CreateAt, Active) VALUES(:Id, :Value, :CreateAt, TRUE)", map[string]interface{}{ 801 "Id": id, 802 "Value": cfgData, 803 "CreateAt": model.GetMillis(), 804 }) 805 require.NoError(t, err) 806 807 err = ds.Load() 808 if assert.Error(t, err) { 809 assert.EqualError(t, err, "invalid config: Config.IsValid: model.config.is_valid.site_url.app_error, ") 810 } 811 }) 812 813 t.Run("fixes required", func(t *testing.T) { 814 _, tearDown := setupConfigDatabase(t, fixesRequiredConfig, nil) 815 defer tearDown() 816 817 ds, err := newTestDatabaseStore(t, nil) 818 require.NoError(t, err) 819 defer ds.Close() 820 821 err = ds.Load() 822 require.NoError(t, err) 823 assertDatabaseNotEqualsConfig(t, fixesRequiredConfig) 824 assert.Equal(t, "http://trailingslash", *ds.Get().ServiceSettings.SiteURL) 825 }) 826 827 t.Run("listeners notifed", func(t *testing.T) { 828 _, tearDown := setupConfigDatabase(t, emptyConfig, nil) 829 defer tearDown() 830 831 ds, err := newTestDatabaseStore(t, nil) 832 require.NoError(t, err) 833 defer ds.Close() 834 835 called := make(chan bool, 1) 836 callback := func(oldfg, newCfg *model.Config) { 837 called <- true 838 } 839 ds.AddListener(callback) 840 841 err = ds.Load() 842 require.NoError(t, err) 843 844 require.True(t, wasCalled(called, 5*time.Second), "callback should have been called when config loaded") 845 }) 846 } 847 848 func TestDatabaseGetFile(t *testing.T) { 849 _, tearDown := setupConfigDatabase(t, minimalConfig, map[string][]byte{ 850 "empty-file": {}, 851 "test-file": []byte("test"), 852 }) 853 defer tearDown() 854 855 ds, err := newTestDatabaseStore(t, nil) 856 require.NoError(t, err) 857 defer ds.Close() 858 859 t.Run("get empty filename", func(t *testing.T) { 860 _, err := ds.GetFile("") 861 require.Error(t, err) 862 }) 863 864 t.Run("get non-existent file", func(t *testing.T) { 865 _, err := ds.GetFile("unknown") 866 require.Error(t, err) 867 }) 868 869 t.Run("get empty file", func(t *testing.T) { 870 data, err := ds.GetFile("empty-file") 871 require.NoError(t, err) 872 require.Empty(t, data) 873 }) 874 875 t.Run("get non-empty file", func(t *testing.T) { 876 data, err := ds.GetFile("test-file") 877 require.NoError(t, err) 878 require.Equal(t, []byte("test"), data) 879 }) 880 } 881 882 func TestDatabaseSetFile(t *testing.T) { 883 _, tearDown := setupConfigDatabase(t, minimalConfig, nil) 884 defer tearDown() 885 886 ds, err := newTestDatabaseStore(t, nil) 887 require.NoError(t, err) 888 defer ds.Close() 889 890 t.Run("set new file", func(t *testing.T) { 891 err := ds.SetFile("new", []byte("new file")) 892 require.NoError(t, err) 893 894 data, err := ds.GetFile("new") 895 require.NoError(t, err) 896 require.Equal(t, []byte("new file"), data) 897 }) 898 899 t.Run("overwrite existing file", func(t *testing.T) { 900 err := ds.SetFile("existing", []byte("existing file")) 901 require.NoError(t, err) 902 903 err = ds.SetFile("existing", []byte("overwritten file")) 904 require.NoError(t, err) 905 906 data, err := ds.GetFile("existing") 907 require.NoError(t, err) 908 require.Equal(t, []byte("overwritten file"), data) 909 }) 910 911 t.Run("max length", func(t *testing.T) { 912 if *mainHelper.Settings.DriverName == "postgres" { 913 t.Skip("No limit for postgres") 914 } 915 longFile := bytes.Repeat([]byte("a"), config.MaxWriteLength) 916 917 err := ds.SetFile("toolong", longFile) 918 require.NoError(t, err) 919 }) 920 921 t.Run("too long", func(t *testing.T) { 922 if *mainHelper.Settings.DriverName == "postgres" { 923 t.Skip("No limit for postgres") 924 } 925 longFile := bytes.Repeat([]byte("a"), config.MaxWriteLength+1) 926 927 err := ds.SetFile("toolong", longFile) 928 if assert.Error(t, err) { 929 assert.True(t, strings.HasPrefix(err.Error(), "file data failed length check: value is too long")) 930 } 931 }) 932 } 933 934 func TestDatabaseHasFile(t *testing.T) { 935 t.Run("has non-existent", func(t *testing.T) { 936 _, tearDown := setupConfigDatabase(t, minimalConfig, nil) 937 defer tearDown() 938 939 ds, err := newTestDatabaseStore(t, nil) 940 require.NoError(t, err) 941 defer ds.Close() 942 943 has, err := ds.HasFile("non-existent") 944 require.NoError(t, err) 945 require.False(t, has) 946 }) 947 948 t.Run("has existing", func(t *testing.T) { 949 _, tearDown := setupConfigDatabase(t, minimalConfig, nil) 950 defer tearDown() 951 952 ds, err := newTestDatabaseStore(t, nil) 953 require.NoError(t, err) 954 defer ds.Close() 955 956 err = ds.SetFile("existing", []byte("existing file")) 957 require.NoError(t, err) 958 959 has, err := ds.HasFile("existing") 960 require.NoError(t, err) 961 require.True(t, has) 962 }) 963 964 t.Run("has manually created file", func(t *testing.T) { 965 _, tearDown := setupConfigDatabase(t, minimalConfig, map[string][]byte{ 966 "manual": []byte("manual file"), 967 }) 968 defer tearDown() 969 970 ds, err := newTestDatabaseStore(t, nil) 971 require.NoError(t, err) 972 defer ds.Close() 973 974 has, err := ds.HasFile("manual") 975 require.NoError(t, err) 976 require.True(t, has) 977 }) 978 979 t.Run("has non-existent empty string", func(t *testing.T) { 980 _, tearDown := setupConfigDatabase(t, minimalConfig, nil) 981 defer tearDown() 982 983 ds, err := newTestDatabaseStore(t, nil) 984 require.NoError(t, err) 985 defer ds.Close() 986 987 has, err := ds.HasFile("") 988 require.NoError(t, err) 989 require.False(t, has) 990 }) 991 } 992 993 func TestDatabaseRemoveFile(t *testing.T) { 994 t.Run("remove non-existent", func(t *testing.T) { 995 _, tearDown := setupConfigDatabase(t, minimalConfig, nil) 996 defer tearDown() 997 998 ds, err := newTestDatabaseStore(t, nil) 999 require.NoError(t, err) 1000 defer ds.Close() 1001 1002 err = ds.RemoveFile("non-existent") 1003 require.NoError(t, err) 1004 }) 1005 1006 t.Run("remove existing", func(t *testing.T) { 1007 _, tearDown := setupConfigDatabase(t, minimalConfig, nil) 1008 defer tearDown() 1009 1010 ds, err := newTestDatabaseStore(t, nil) 1011 require.NoError(t, err) 1012 defer ds.Close() 1013 1014 err = ds.SetFile("existing", []byte("existing file")) 1015 require.NoError(t, err) 1016 1017 err = ds.RemoveFile("existing") 1018 require.NoError(t, err) 1019 1020 has, err := ds.HasFile("existing") 1021 require.NoError(t, err) 1022 require.False(t, has) 1023 1024 _, err = ds.GetFile("existing") 1025 require.Error(t, err) 1026 }) 1027 1028 t.Run("remove manually created file", func(t *testing.T) { 1029 _, tearDown := setupConfigDatabase(t, minimalConfig, map[string][]byte{ 1030 "manual": []byte("manual file"), 1031 }) 1032 defer tearDown() 1033 1034 ds, err := newTestDatabaseStore(t, nil) 1035 require.NoError(t, err) 1036 defer ds.Close() 1037 1038 err = ds.RemoveFile("manual") 1039 require.NoError(t, err) 1040 1041 has, err := ds.HasFile("manual") 1042 require.NoError(t, err) 1043 require.False(t, has) 1044 1045 _, err = ds.GetFile("manual") 1046 require.Error(t, err) 1047 }) 1048 } 1049 1050 func TestDatabaseStoreString(t *testing.T) { 1051 if testing.Short() { 1052 t.SkipNow() 1053 } 1054 _, tearDown := setupConfigDatabase(t, emptyConfig, nil) 1055 defer tearDown() 1056 1057 ds, err := newTestDatabaseStore(t, nil) 1058 require.NoError(t, err) 1059 require.NotNil(t, ds) 1060 defer ds.Close() 1061 1062 if *mainHelper.GetSQLSettings().DriverName == "postgres" { 1063 maskedDSN := ds.String() 1064 assert.True(t, strings.HasPrefix(maskedDSN, "postgres://")) 1065 assert.True(t, strings.Contains(maskedDSN, "mmuser")) 1066 assert.False(t, strings.Contains(maskedDSN, "mostest")) 1067 } else { 1068 maskedDSN := ds.String() 1069 assert.True(t, strings.HasPrefix(maskedDSN, "mysql://")) 1070 assert.True(t, strings.Contains(maskedDSN, "mmuser")) 1071 assert.False(t, strings.Contains(maskedDSN, "mostest")) 1072 } 1073 }