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