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  }