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