github.com/mattermost/mattermost-server/server/v8@v8.0.0-20230610055354-a6d1d38b273d/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
     5  
     6  import (
     7  	"bytes"
     8  	"encoding/json"
     9  	"errors"
    10  	"fmt"
    11  	"os"
    12  	"strings"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  
    19  	"github.com/mattermost/mattermost-server/server/public/model"
    20  )
    21  
    22  func getDsn(driver string, source string) string {
    23  	if driver == model.DatabaseDriverMysql {
    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 := marshalConfig(cfg)
    38  	require.NoError(t, err)
    39  
    40  	ds := &DatabaseStore{
    41  		driverName:     *mainHelper.GetSQLSettings().DriverName,
    42  		db:             mainHelper.GetSQLStore().GetMasterX().DB,
    43  		dataSourceName: *mainHelper.Settings.DataSource,
    44  	}
    45  
    46  	err = ds.initializeConfigurationsTable()
    47  	require.NoError(t, err)
    48  
    49  	id := model.NewId()
    50  	_, err = ds.db.NamedExec("INSERT INTO Configurations (Id, Value, CreateAt, Active) VALUES(:Id, :Value, :CreateAt, TRUE)", map[string]any{
    51  		"Id":       id,
    52  		"Value":    cfgData,
    53  		"CreateAt": model.GetMillis(),
    54  	})
    55  	require.NoError(t, err)
    56  
    57  	for name, data := range files {
    58  		params := map[string]any{
    59  			"name":      name,
    60  			"data":      data,
    61  			"create_at": model.GetMillis(),
    62  			"update_at": model.GetMillis(),
    63  		}
    64  
    65  		_, err = ds.db.NamedExec("INSERT INTO ConfigurationFiles (Name, Data, CreateAt, UpdateAt) VALUES (:name, :data, :create_at, :update_at)", params)
    66  		require.NoError(t, err)
    67  	}
    68  
    69  	return id, func() {
    70  		truncateTables(t)
    71  	}
    72  }
    73  
    74  // getActualDatabaseConfig returns the active configuration in the database without relying on a config store.
    75  func getActualDatabaseConfig(t *testing.T) (string, *model.Config) {
    76  	t.Helper()
    77  
    78  	if *mainHelper.GetSQLSettings().DriverName == "postgres" {
    79  		var actual struct {
    80  			ID    string `db:"id"`
    81  			Value []byte `db:"value"`
    82  		}
    83  		err := mainHelper.GetSQLStore().GetMasterX().Get(&actual, "SELECT Id, Value FROM Configurations WHERE Active")
    84  		require.NoError(t, err)
    85  
    86  		var actualCfg *model.Config
    87  		err = json.Unmarshal(actual.Value, &actualCfg)
    88  		require.NoError(t, err)
    89  		return actual.ID, actualCfg
    90  	}
    91  	var actual struct {
    92  		ID    string `db:"Id"`
    93  		Value []byte `db:"Value"`
    94  	}
    95  	err := mainHelper.GetSQLStore().GetMasterX().Get(&actual, "SELECT Id, Value FROM Configurations WHERE Active")
    96  	require.NoError(t, err)
    97  
    98  	var actualCfg *model.Config
    99  	err = json.Unmarshal(actual.Value, &actualCfg)
   100  	require.NoError(t, err)
   101  	return actual.ID, actualCfg
   102  }
   103  
   104  // assertDatabaseEqualsConfig verifies the active in-database configuration equals the given config.
   105  func assertDatabaseEqualsConfig(t *testing.T, expectedCfg *model.Config) {
   106  	t.Helper()
   107  
   108  	_, actualCfg := getActualDatabaseConfig(t)
   109  	assert.Equal(t, expectedCfg, actualCfg)
   110  }
   111  
   112  // assertDatabaseNotEqualsConfig verifies the in-database configuration does not equal the given config.
   113  func assertDatabaseNotEqualsConfig(t *testing.T, expectedCfg *model.Config) {
   114  	t.Helper()
   115  
   116  	_, actualCfg := getActualDatabaseConfig(t)
   117  	assert.NotEqual(t, expectedCfg, actualCfg)
   118  }
   119  
   120  func newTestDatabaseStore(customDefaults *model.Config) (*Store, error) {
   121  	sqlSettings := mainHelper.GetSQLSettings()
   122  	dss, err := NewDatabaseStore(getDsn(*sqlSettings.DriverName, *sqlSettings.DataSource))
   123  	if err != nil {
   124  		return nil, err
   125  	}
   126  
   127  	cStore, err := NewStoreFromBacking(dss, customDefaults, false)
   128  	if err != nil {
   129  		return nil, err
   130  	}
   131  
   132  	return cStore, nil
   133  }
   134  
   135  func TestDatabaseStoreNew(t *testing.T) {
   136  	if testing.Short() {
   137  		t.SkipNow()
   138  	}
   139  	sqlSettings := mainHelper.GetSQLSettings()
   140  
   141  	t.Run("no existing configuration - initialization required", func(t *testing.T) {
   142  		ds, err := newTestDatabaseStore(nil)
   143  		require.NoError(t, err)
   144  		defer ds.Close()
   145  
   146  		assert.Equal(t, "", *ds.Get().ServiceSettings.SiteURL)
   147  	})
   148  
   149  	t.Run("no existing configuration with custom defaults", func(t *testing.T) {
   150  		truncateTables(t)
   151  		ds, err := newTestDatabaseStore(customConfigDefaults)
   152  		require.NoError(t, err)
   153  		defer ds.Close()
   154  
   155  		assert.Equal(t, *customConfigDefaults.ServiceSettings.SiteURL, *ds.Get().ServiceSettings.SiteURL)
   156  		assert.Equal(t, *customConfigDefaults.DisplaySettings.ExperimentalTimezone, *ds.Get().DisplaySettings.ExperimentalTimezone)
   157  	})
   158  
   159  	t.Run("existing config, initialization required", func(t *testing.T) {
   160  		_, tearDown := setupConfigDatabase(t, testConfig, nil)
   161  		defer tearDown()
   162  
   163  		ds, err := newTestDatabaseStore(nil)
   164  		require.NoError(t, err)
   165  		defer ds.Close()
   166  
   167  		assert.Equal(t, "http://TestStoreNew", *ds.Get().ServiceSettings.SiteURL)
   168  		assertDatabaseNotEqualsConfig(t, testConfig)
   169  	})
   170  
   171  	t.Run("existing config with custom defaults, initialization required", func(t *testing.T) {
   172  		_, tearDown := setupConfigDatabase(t, testConfig, nil)
   173  		defer tearDown()
   174  
   175  		ds, err := newTestDatabaseStore(customConfigDefaults)
   176  		require.NoError(t, err)
   177  		defer ds.Close()
   178  
   179  		// already existing value should not be overwritten by the
   180  		// custom default value
   181  		assert.Equal(t, "http://TestStoreNew", *ds.Get().ServiceSettings.SiteURL)
   182  		// not existing value should be overwritten by the custom
   183  		// default value
   184  		assert.Equal(t, *customConfigDefaults.DisplaySettings.ExperimentalTimezone, *ds.Get().DisplaySettings.ExperimentalTimezone)
   185  		assertDatabaseNotEqualsConfig(t, testConfig)
   186  	})
   187  
   188  	t.Run("already minimally configured", func(t *testing.T) {
   189  		_, tearDown := setupConfigDatabase(t, minimalConfigNoFF, nil)
   190  		defer tearDown()
   191  
   192  		ds, err := newTestDatabaseStore(nil)
   193  		require.NoError(t, err)
   194  		defer ds.Close()
   195  
   196  		assert.Equal(t, "http://minimal", *ds.Get().ServiceSettings.SiteURL)
   197  		assertDatabaseEqualsConfig(t, minimalConfigNoFF)
   198  	})
   199  
   200  	t.Run("already minimally configured with custom defaults", func(t *testing.T) {
   201  		_, tearDown := setupConfigDatabase(t, minimalConfigNoFF, nil)
   202  		defer tearDown()
   203  
   204  		ds, err := newTestDatabaseStore(customConfigDefaults)
   205  		require.NoError(t, err)
   206  		defer ds.Close()
   207  
   208  		// as the whole config has default values already, custom
   209  		// defaults should have no effect
   210  		assert.Equal(t, "http://minimal", *ds.Get().ServiceSettings.SiteURL)
   211  		assert.NotEqual(t, *customConfigDefaults.DisplaySettings.ExperimentalTimezone, *ds.Get().DisplaySettings.ExperimentalTimezone)
   212  		assertDatabaseEqualsConfig(t, minimalConfigNoFF)
   213  	})
   214  
   215  	t.Run("invalid url", func(t *testing.T) {
   216  		_, err := NewDatabaseStore("")
   217  		require.Error(t, err)
   218  
   219  		_, err = NewDatabaseStore("mysql")
   220  		require.Error(t, err)
   221  	})
   222  
   223  	t.Run("unsupported scheme", func(t *testing.T) {
   224  		_, err := NewDatabaseStore("invalid")
   225  		require.Error(t, err)
   226  	})
   227  
   228  	t.Run("unsupported scheme with valid data source", func(t *testing.T) {
   229  		_, err := NewDatabaseStore(fmt.Sprintf("invalid://%s", *sqlSettings.DataSource))
   230  		require.Error(t, err)
   231  	})
   232  }
   233  
   234  func TestDatabaseStoreGet(t *testing.T) {
   235  	_, tearDown := setupConfigDatabase(t, testConfig, nil)
   236  	defer tearDown()
   237  
   238  	ds, err := newTestDatabaseStore(nil)
   239  	require.NoError(t, err)
   240  	defer ds.Close()
   241  
   242  	cfg := ds.Get()
   243  	assert.Equal(t, "http://TestStoreNew", *cfg.ServiceSettings.SiteURL)
   244  
   245  	cfg2 := ds.Get()
   246  	assert.Equal(t, "http://TestStoreNew", *cfg.ServiceSettings.SiteURL)
   247  
   248  	assert.True(t, cfg == cfg2, "Get() returned different configuration instances")
   249  }
   250  
   251  func TestDatabaseStoreGetEnvironmentOverrides(t *testing.T) {
   252  	t.Run("get override for a string variable", func(t *testing.T) {
   253  		_, tearDown := setupConfigDatabase(t, testConfig, nil)
   254  		defer tearDown()
   255  
   256  		ds, err := newTestDatabaseStore(nil)
   257  		require.NoError(t, err)
   258  		defer ds.Close()
   259  
   260  		assert.Equal(t, "http://TestStoreNew", *ds.Get().ServiceSettings.SiteURL)
   261  		assert.Empty(t, ds.GetEnvironmentOverrides())
   262  
   263  		os.Setenv("MM_SERVICESETTINGS_SITEURL", "http://override")
   264  		defer os.Unsetenv("MM_SERVICESETTINGS_SITEURL")
   265  
   266  		ds, err = newTestDatabaseStore(nil)
   267  		require.NoError(t, err)
   268  		defer ds.Close()
   269  
   270  		assert.Equal(t, "http://override", *ds.Get().ServiceSettings.SiteURL)
   271  		assert.Equal(t, map[string]any{"ServiceSettings": map[string]any{"SiteURL": true}}, ds.GetEnvironmentOverrides())
   272  	})
   273  
   274  	t.Run("get override for a string variable with a custom default value", func(t *testing.T) {
   275  		_, tearDown := setupConfigDatabase(t, testConfig, nil)
   276  		defer tearDown()
   277  
   278  		ds, err := newTestDatabaseStore(customConfigDefaults)
   279  		require.NoError(t, err)
   280  		defer ds.Close()
   281  
   282  		assert.Equal(t, "http://TestStoreNew", *ds.Get().ServiceSettings.SiteURL)
   283  		assert.Empty(t, ds.GetEnvironmentOverrides())
   284  
   285  		os.Setenv("MM_SERVICESETTINGS_SITEURL", "http://override")
   286  		defer os.Unsetenv("MM_SERVICESETTINGS_SITEURL")
   287  
   288  		ds, err = newTestDatabaseStore(customConfigDefaults)
   289  		require.NoError(t, err)
   290  		defer ds.Close()
   291  
   292  		// environment override should take priority over the custom default value
   293  		assert.Equal(t, "http://override", *ds.Get().ServiceSettings.SiteURL)
   294  		assert.Equal(t, map[string]any{"ServiceSettings": map[string]any{"SiteURL": true}}, ds.GetEnvironmentOverrides())
   295  	})
   296  
   297  	t.Run("get override for a bool variable", func(t *testing.T) {
   298  		_, tearDown := setupConfigDatabase(t, testConfig, nil)
   299  		defer tearDown()
   300  
   301  		ds, err := newTestDatabaseStore(nil)
   302  		require.NoError(t, err)
   303  		defer ds.Close()
   304  
   305  		assert.Equal(t, false, *ds.Get().PluginSettings.EnableUploads)
   306  		assert.Empty(t, ds.GetEnvironmentOverrides())
   307  
   308  		os.Setenv("MM_PLUGINSETTINGS_ENABLEUPLOADS", "true")
   309  		defer os.Unsetenv("MM_PLUGINSETTINGS_ENABLEUPLOADS")
   310  
   311  		ds, err = newTestDatabaseStore(nil)
   312  		require.NoError(t, err)
   313  		defer ds.Close()
   314  
   315  		assert.Equal(t, true, *ds.Get().PluginSettings.EnableUploads)
   316  		assert.Equal(t, map[string]any{"PluginSettings": map[string]any{"EnableUploads": true}}, ds.GetEnvironmentOverrides())
   317  	})
   318  
   319  	t.Run("get override for an int variable", func(t *testing.T) {
   320  		_, tearDown := setupConfigDatabase(t, testConfig, nil)
   321  		defer tearDown()
   322  
   323  		ds, err := newTestDatabaseStore(nil)
   324  		require.NoError(t, err)
   325  		defer ds.Close()
   326  
   327  		assert.Equal(t, model.TeamSettingsDefaultMaxUsersPerTeam, *ds.Get().TeamSettings.MaxUsersPerTeam)
   328  		assert.Empty(t, ds.GetEnvironmentOverrides())
   329  
   330  		os.Setenv("MM_TEAMSETTINGS_MAXUSERSPERTEAM", "3000")
   331  		defer os.Unsetenv("MM_TEAMSETTINGS_MAXUSERSPERTEAM")
   332  
   333  		ds, err = newTestDatabaseStore(nil)
   334  		require.NoError(t, err)
   335  		defer ds.Close()
   336  
   337  		assert.Equal(t, 3000, *ds.Get().TeamSettings.MaxUsersPerTeam)
   338  		assert.Equal(t, map[string]any{"TeamSettings": map[string]any{"MaxUsersPerTeam": true}}, ds.GetEnvironmentOverrides())
   339  	})
   340  
   341  	t.Run("get override for an int64 variable", func(t *testing.T) {
   342  		_, tearDown := setupConfigDatabase(t, testConfig, nil)
   343  		defer tearDown()
   344  
   345  		ds, err := newTestDatabaseStore(nil)
   346  		require.NoError(t, err)
   347  		defer ds.Close()
   348  
   349  		assert.Equal(t, int64(63072000), *ds.Get().ServiceSettings.TLSStrictTransportMaxAge)
   350  		assert.Empty(t, ds.GetEnvironmentOverrides())
   351  
   352  		os.Setenv("MM_SERVICESETTINGS_TLSSTRICTTRANSPORTMAXAGE", "123456")
   353  		defer os.Unsetenv("MM_SERVICESETTINGS_TLSSTRICTTRANSPORTMAXAGE")
   354  
   355  		ds, err = newTestDatabaseStore(nil)
   356  		require.NoError(t, err)
   357  		defer ds.Close()
   358  
   359  		assert.Equal(t, int64(123456), *ds.Get().ServiceSettings.TLSStrictTransportMaxAge)
   360  		assert.Equal(t, map[string]any{"ServiceSettings": map[string]any{"TLSStrictTransportMaxAge": true}}, ds.GetEnvironmentOverrides())
   361  	})
   362  
   363  	t.Run("get override for a slice variable - one value", func(t *testing.T) {
   364  		_, tearDown := setupConfigDatabase(t, testConfig, nil)
   365  		defer tearDown()
   366  
   367  		ds, err := newTestDatabaseStore(nil)
   368  		require.NoError(t, err)
   369  		defer ds.Close()
   370  
   371  		assert.Equal(t, []string{}, ds.Get().SqlSettings.DataSourceReplicas)
   372  		assert.Empty(t, ds.GetEnvironmentOverrides())
   373  
   374  		os.Setenv("MM_SQLSETTINGS_DATASOURCEREPLICAS", "user:pwd@db:5432/test-db")
   375  		defer os.Unsetenv("MM_SQLSETTINGS_DATASOURCEREPLICAS")
   376  
   377  		ds, err = newTestDatabaseStore(nil)
   378  		require.NoError(t, err)
   379  		defer ds.Close()
   380  
   381  		assert.Equal(t, []string{"user:pwd@db:5432/test-db"}, ds.Get().SqlSettings.DataSourceReplicas)
   382  		assert.Equal(t, map[string]any{"SqlSettings": map[string]any{"DataSourceReplicas": true}}, ds.GetEnvironmentOverrides())
   383  	})
   384  
   385  	t.Run("get override for a slice variable - three values", func(t *testing.T) {
   386  		// This should work, but Viper (or we) don't parse environment variables to turn strings with spaces into slices.
   387  		t.Skip("not implemented yet")
   388  
   389  		_, tearDown := setupConfigDatabase(t, testConfig, nil)
   390  		defer tearDown()
   391  
   392  		ds, err := newTestDatabaseStore(nil)
   393  		require.NoError(t, err)
   394  		defer ds.Close()
   395  
   396  		assert.Equal(t, []string{}, ds.Get().SqlSettings.DataSourceReplicas)
   397  		assert.Empty(t, ds.GetEnvironmentOverrides())
   398  
   399  		os.Setenv("MM_SQLSETTINGS_DATASOURCEREPLICAS", "user:pwd@db:5432/test-db user:pwd@db2:5433/test-db2 user:pwd@db3:5434/test-db3")
   400  		defer os.Unsetenv("MM_SQLSETTINGS_DATASOURCEREPLICAS")
   401  
   402  		ds, err = newTestDatabaseStore(nil)
   403  		require.NoError(t, err)
   404  		defer ds.Close()
   405  
   406  		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)
   407  		assert.Equal(t, map[string]any{"SqlSettings": map[string]any{"DataSourceReplicas": true}}, ds.GetEnvironmentOverrides())
   408  	})
   409  }
   410  
   411  func TestDatabaseStoreSet(t *testing.T) {
   412  	if testing.Short() {
   413  		t.SkipNow()
   414  	}
   415  
   416  	t.Run("set same pointer value", func(t *testing.T) {
   417  		t.Skip("not yet implemented")
   418  
   419  		_, tearDown := setupConfigDatabase(t, emptyConfig, nil)
   420  		defer tearDown()
   421  
   422  		ds, err := newTestDatabaseStore(nil)
   423  		require.NoError(t, err)
   424  		defer ds.Close()
   425  
   426  		_, _, err = ds.Set(ds.Get())
   427  		if assert.Error(t, err) {
   428  			assert.EqualError(t, err, "old configuration modified instead of cloning")
   429  		}
   430  	})
   431  
   432  	t.Run("defaults required", func(t *testing.T) {
   433  		_, tearDown := setupConfigDatabase(t, minimalConfig, nil)
   434  		defer tearDown()
   435  
   436  		ds, err := newTestDatabaseStore(nil)
   437  		require.NoError(t, err)
   438  		defer ds.Close()
   439  
   440  		newCfg := &model.Config{}
   441  
   442  		_, _, err = ds.Set(newCfg)
   443  		require.NoError(t, err)
   444  
   445  		assert.Equal(t, "", *ds.Get().ServiceSettings.SiteURL)
   446  	})
   447  
   448  	t.Run("desanitization required", func(t *testing.T) {
   449  		_, tearDown := setupConfigDatabase(t, ldapConfig, nil)
   450  		defer tearDown()
   451  
   452  		ds, err := newTestDatabaseStore(nil)
   453  		require.NoError(t, err)
   454  		defer ds.Close()
   455  
   456  		newCfg := &model.Config{}
   457  		newCfg.LdapSettings.BindPassword = model.NewString(model.FakeSetting)
   458  
   459  		_, _, err = ds.Set(newCfg)
   460  		require.NoError(t, err)
   461  
   462  		assert.Equal(t, "password", *ds.Get().LdapSettings.BindPassword)
   463  	})
   464  
   465  	t.Run("invalid", func(t *testing.T) {
   466  		_, tearDown := setupConfigDatabase(t, emptyConfig, nil)
   467  		defer tearDown()
   468  
   469  		ds, err := newTestDatabaseStore(nil)
   470  		require.NoError(t, err)
   471  		defer ds.Close()
   472  
   473  		newCfg := &model.Config{}
   474  		newCfg.ServiceSettings.SiteURL = model.NewString("invalid")
   475  
   476  		_, _, err = ds.Set(newCfg)
   477  		if assert.Error(t, err) {
   478  			assert.EqualError(t, err, "new configuration is invalid: Config.IsValid: model.config.is_valid.site_url.app_error, parse \"invalid\": invalid URI for request")
   479  		}
   480  
   481  		assert.Equal(t, "", *ds.Get().ServiceSettings.SiteURL)
   482  	})
   483  
   484  	t.Run("duplicate ignored", func(t *testing.T) {
   485  		_, tearDown := setupConfigDatabase(t, minimalConfig, nil)
   486  		defer tearDown()
   487  
   488  		ds, err := newTestDatabaseStore(nil)
   489  		require.NoError(t, err)
   490  		defer ds.Close()
   491  
   492  		_, _, err = ds.Set(ds.Get())
   493  		require.NoError(t, err)
   494  
   495  		beforeID, _ := getActualDatabaseConfig(t)
   496  		_, _, err = ds.Set(ds.Get())
   497  		require.NoError(t, err)
   498  
   499  		afterID, _ := getActualDatabaseConfig(t)
   500  		assert.Equal(t, beforeID, afterID, "new record should not have been written")
   501  	})
   502  
   503  	t.Run("read-only ignored", func(t *testing.T) {
   504  		_, tearDown := setupConfigDatabase(t, readOnlyConfig, nil)
   505  		defer tearDown()
   506  
   507  		ds, err := newTestDatabaseStore(nil)
   508  		require.NoError(t, err)
   509  		defer ds.Close()
   510  
   511  		newCfg := &model.Config{
   512  			ServiceSettings: model.ServiceSettings{
   513  				SiteURL: model.NewString("http://new"),
   514  			},
   515  		}
   516  
   517  		_, _, err = ds.Set(newCfg)
   518  		require.NoError(t, err)
   519  
   520  		assert.Equal(t, "http://new", *ds.Get().ServiceSettings.SiteURL)
   521  	})
   522  
   523  	t.Run("set with automatic save", func(t *testing.T) {
   524  		_, tearDown := setupConfigDatabase(t, minimalConfig, nil)
   525  		defer tearDown()
   526  
   527  		ds, err := newTestDatabaseStore(nil)
   528  		require.NoError(t, err)
   529  		defer ds.Close()
   530  
   531  		newCfg := &model.Config{
   532  			ServiceSettings: model.ServiceSettings{
   533  				SiteURL: model.NewString("http://new"),
   534  			},
   535  		}
   536  
   537  		_, _, err = ds.Set(newCfg)
   538  		require.NoError(t, err)
   539  
   540  		err = ds.Load()
   541  		require.NoError(t, err)
   542  
   543  		assert.Equal(t, "http://new", *ds.Get().ServiceSettings.SiteURL)
   544  	})
   545  
   546  	t.Run("persist failed", func(t *testing.T) {
   547  		_, tearDown := setupConfigDatabase(t, emptyConfig, nil)
   548  		defer tearDown()
   549  
   550  		ds, err := newTestDatabaseStore(nil)
   551  		require.NoError(t, err)
   552  		defer ds.Close()
   553  
   554  		_, err = mainHelper.GetSQLStore().GetMasterX().Exec("DROP TABLE Configurations")
   555  		require.NoError(t, err)
   556  
   557  		newCfg := minimalConfig
   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", MaxWriteLength))
   578  		newCfg := emptyConfig.Clone()
   579  		newCfg.ServiceSettings.SiteURL = model.NewString(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 := minimalConfig
   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.SetReadOnlyFF(true)
   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.SetReadOnlyFF(false)
   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]any{"ServiceSettings": map[string]any{"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]any{"ServiceSettings": map[string]any{"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]any{"PluginSettings": map[string]any{"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]any{"TeamSettings": map[string]any{"MaxUsersPerTeam": true}}, ds.GetEnvironmentOverrides())
   747  		// check that in DB config does not include overwritten variable
   748  		_, actualConfig := getActualDatabaseConfig(t)
   749  		assert.Equal(t, model.TeamSettingsDefaultMaxUsersPerTeam, *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]any{"ServiceSettings": map[string]any{"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]any{"SqlSettings": map[string]any{"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]any{"SqlSettings": map[string]any{"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 := marshalConfig(invalidConfig)
   832  		require.NoError(t, err)
   833  
   834  		truncateTables(t)
   835  		id := model.NewId()
   836  		_, err = mainHelper.GetSQLStore().GetMasterX().NamedExec("INSERT INTO Configurations (Id, Value, CreateAt, Active) VALUES(:id, :value, :createat, TRUE)", map[string]any{
   837  			"id":       id,
   838  			"value":    cfgData,
   839  			"createat": model.GetMillis(),
   840  		})
   841  		t.Logf("%v\n", err)
   842  		require.NoErrorf(t, err, "what is - %v", err)
   843  
   844  		err = ds.Load()
   845  		if assert.Error(t, err) {
   846  			var appErr *model.AppError
   847  			require.True(t, errors.As(err, &appErr))
   848  			assert.Equal(t, appErr.Id, "model.config.is_valid.site_url.app_error")
   849  		}
   850  	})
   851  
   852  	t.Run("fixes required", func(t *testing.T) {
   853  		_, tearDown := setupConfigDatabase(t, fixesRequiredConfig, nil)
   854  		defer tearDown()
   855  
   856  		ds, err := newTestDatabaseStore(nil)
   857  		require.NoError(t, err)
   858  		defer ds.Close()
   859  
   860  		err = ds.Load()
   861  		require.NoError(t, err)
   862  		assertDatabaseNotEqualsConfig(t, fixesRequiredConfig)
   863  		assert.Equal(t, "http://trailingslash", *ds.Get().ServiceSettings.SiteURL)
   864  	})
   865  
   866  	t.Run("listeners notified on change", func(t *testing.T) {
   867  		_, tearDown := setupConfigDatabase(t, emptyConfig, nil)
   868  		defer tearDown()
   869  
   870  		ds, err := newTestDatabaseStore(nil)
   871  		require.NoError(t, err)
   872  		defer ds.Close()
   873  
   874  		called := make(chan bool, 1)
   875  		callback := func(oldfg, newCfg *model.Config) {
   876  			called <- true
   877  		}
   878  		ds.AddListener(callback)
   879  
   880  		newCfg := minimalConfig.Clone()
   881  		dbStore, ok := ds.backingStore.(*DatabaseStore)
   882  		require.True(t, ok)
   883  		err = dbStore.persist(newCfg)
   884  		require.NoError(t, err)
   885  
   886  		err = ds.Load()
   887  		require.NoError(t, err)
   888  
   889  		require.True(t, wasCalled(called, 5*time.Second), "callback should have been called when config changed on load")
   890  	})
   891  }
   892  
   893  func TestDatabaseGetFile(t *testing.T) {
   894  	_, tearDown := setupConfigDatabase(t, minimalConfig, map[string][]byte{
   895  		"empty-file": {},
   896  		"test-file":  []byte("test"),
   897  	})
   898  	defer tearDown()
   899  
   900  	ds, err := newTestDatabaseStore(nil)
   901  	require.NoError(t, err)
   902  	defer ds.Close()
   903  
   904  	t.Run("get empty filename", func(t *testing.T) {
   905  		_, err := ds.GetFile("")
   906  		require.Error(t, err)
   907  	})
   908  
   909  	t.Run("get non-existent file", func(t *testing.T) {
   910  		_, err := ds.GetFile("unknown")
   911  		require.Error(t, err)
   912  	})
   913  
   914  	t.Run("get empty file", func(t *testing.T) {
   915  		data, err := ds.GetFile("empty-file")
   916  		require.NoError(t, err)
   917  		require.Empty(t, data)
   918  	})
   919  
   920  	t.Run("get non-empty file", func(t *testing.T) {
   921  		data, err := ds.GetFile("test-file")
   922  		require.NoError(t, err)
   923  		require.Equal(t, []byte("test"), data)
   924  	})
   925  }
   926  
   927  func TestDatabaseSetFile(t *testing.T) {
   928  	_, tearDown := setupConfigDatabase(t, minimalConfig, nil)
   929  	defer tearDown()
   930  
   931  	ds, err := newTestDatabaseStore(nil)
   932  	require.NoError(t, err)
   933  	defer ds.Close()
   934  
   935  	t.Run("set new file", func(t *testing.T) {
   936  		err := ds.SetFile("new", []byte("new file"))
   937  		require.NoError(t, err)
   938  
   939  		data, err := ds.GetFile("new")
   940  		require.NoError(t, err)
   941  		require.Equal(t, []byte("new file"), data)
   942  	})
   943  
   944  	t.Run("overwrite existing file", func(t *testing.T) {
   945  		err := ds.SetFile("existing", []byte("existing file"))
   946  		require.NoError(t, err)
   947  
   948  		err = ds.SetFile("existing", []byte("overwritten file"))
   949  		require.NoError(t, err)
   950  
   951  		data, err := ds.GetFile("existing")
   952  		require.NoError(t, err)
   953  		require.Equal(t, []byte("overwritten file"), data)
   954  	})
   955  
   956  	t.Run("max length", func(t *testing.T) {
   957  		if *mainHelper.Settings.DriverName == "postgres" {
   958  			t.Skip("No limit for postgres")
   959  		}
   960  		longFile := bytes.Repeat([]byte("a"), MaxWriteLength)
   961  
   962  		err := ds.SetFile("toolong", longFile)
   963  		require.NoError(t, err)
   964  	})
   965  
   966  	t.Run("too long", func(t *testing.T) {
   967  		if *mainHelper.Settings.DriverName == "postgres" {
   968  			t.Skip("No limit for postgres")
   969  		}
   970  		longFile := bytes.Repeat([]byte("a"), MaxWriteLength+1)
   971  
   972  		err := ds.SetFile("toolong", longFile)
   973  		if assert.Error(t, err) {
   974  			assert.True(t, strings.HasPrefix(err.Error(), "file data failed length check: value is too long"))
   975  		}
   976  	})
   977  }
   978  
   979  func TestDatabaseHasFile(t *testing.T) {
   980  	t.Run("has non-existent", func(t *testing.T) {
   981  		_, tearDown := setupConfigDatabase(t, minimalConfig, nil)
   982  		defer tearDown()
   983  
   984  		ds, err := newTestDatabaseStore(nil)
   985  		require.NoError(t, err)
   986  		defer ds.Close()
   987  
   988  		has, err := ds.HasFile("non-existent")
   989  		require.NoError(t, err)
   990  		require.False(t, has)
   991  	})
   992  
   993  	t.Run("has existing", func(t *testing.T) {
   994  		_, tearDown := setupConfigDatabase(t, minimalConfig, nil)
   995  		defer tearDown()
   996  
   997  		ds, err := newTestDatabaseStore(nil)
   998  		require.NoError(t, err)
   999  		defer ds.Close()
  1000  
  1001  		err = ds.SetFile("existing", []byte("existing file"))
  1002  		require.NoError(t, err)
  1003  
  1004  		has, err := ds.HasFile("existing")
  1005  		require.NoError(t, err)
  1006  		require.True(t, has)
  1007  	})
  1008  
  1009  	t.Run("has manually created file", func(t *testing.T) {
  1010  		_, tearDown := setupConfigDatabase(t, minimalConfig, map[string][]byte{
  1011  			"manual": []byte("manual file"),
  1012  		})
  1013  		defer tearDown()
  1014  
  1015  		ds, err := newTestDatabaseStore(nil)
  1016  		require.NoError(t, err)
  1017  		defer ds.Close()
  1018  
  1019  		has, err := ds.HasFile("manual")
  1020  		require.NoError(t, err)
  1021  		require.True(t, has)
  1022  	})
  1023  
  1024  	t.Run("has non-existent empty string", func(t *testing.T) {
  1025  		_, tearDown := setupConfigDatabase(t, minimalConfig, nil)
  1026  		defer tearDown()
  1027  
  1028  		ds, err := newTestDatabaseStore(nil)
  1029  		require.NoError(t, err)
  1030  		defer ds.Close()
  1031  
  1032  		has, err := ds.HasFile("")
  1033  		require.NoError(t, err)
  1034  		require.False(t, has)
  1035  	})
  1036  }
  1037  
  1038  func TestDatabaseRemoveFile(t *testing.T) {
  1039  	t.Run("remove non-existent", func(t *testing.T) {
  1040  		_, tearDown := setupConfigDatabase(t, minimalConfig, nil)
  1041  		defer tearDown()
  1042  
  1043  		ds, err := newTestDatabaseStore(nil)
  1044  		require.NoError(t, err)
  1045  		defer ds.Close()
  1046  
  1047  		err = ds.RemoveFile("non-existent")
  1048  		require.NoError(t, err)
  1049  	})
  1050  
  1051  	t.Run("remove existing", func(t *testing.T) {
  1052  		_, tearDown := setupConfigDatabase(t, minimalConfig, nil)
  1053  		defer tearDown()
  1054  
  1055  		ds, err := newTestDatabaseStore(nil)
  1056  		require.NoError(t, err)
  1057  		defer ds.Close()
  1058  
  1059  		err = ds.SetFile("existing", []byte("existing file"))
  1060  		require.NoError(t, err)
  1061  
  1062  		err = ds.RemoveFile("existing")
  1063  		require.NoError(t, err)
  1064  
  1065  		has, err := ds.HasFile("existing")
  1066  		require.NoError(t, err)
  1067  		require.False(t, has)
  1068  
  1069  		_, err = ds.GetFile("existing")
  1070  		require.Error(t, err)
  1071  	})
  1072  
  1073  	t.Run("remove manually created file", func(t *testing.T) {
  1074  		_, tearDown := setupConfigDatabase(t, minimalConfig, map[string][]byte{
  1075  			"manual": []byte("manual file"),
  1076  		})
  1077  		defer tearDown()
  1078  
  1079  		ds, err := newTestDatabaseStore(nil)
  1080  		require.NoError(t, err)
  1081  		defer ds.Close()
  1082  
  1083  		err = ds.RemoveFile("manual")
  1084  		require.NoError(t, err)
  1085  
  1086  		has, err := ds.HasFile("manual")
  1087  		require.NoError(t, err)
  1088  		require.False(t, has)
  1089  
  1090  		_, err = ds.GetFile("manual")
  1091  		require.Error(t, err)
  1092  	})
  1093  }
  1094  
  1095  func TestDatabaseStoreString(t *testing.T) {
  1096  	if testing.Short() {
  1097  		t.SkipNow()
  1098  	}
  1099  	_, tearDown := setupConfigDatabase(t, emptyConfig, nil)
  1100  	defer tearDown()
  1101  
  1102  	ds, err := newTestDatabaseStore(nil)
  1103  	require.NoError(t, err)
  1104  	require.NotNil(t, ds)
  1105  	defer ds.Close()
  1106  
  1107  	if *mainHelper.GetSQLSettings().DriverName == "postgres" {
  1108  		maskedDSN := ds.String()
  1109  		assert.True(t, strings.HasPrefix(maskedDSN, "postgres://"))
  1110  		assert.False(t, strings.Contains(maskedDSN, "mmuser"))
  1111  		assert.False(t, strings.Contains(maskedDSN, "mostest"))
  1112  	} else {
  1113  		maskedDSN := ds.String()
  1114  		assert.False(t, strings.HasPrefix(maskedDSN, "mysql://"))
  1115  		assert.False(t, strings.Contains(maskedDSN, "mmuser"))
  1116  		assert.False(t, strings.Contains(maskedDSN, "mostest"))
  1117  	}
  1118  }
  1119  
  1120  func TestCleanUp(t *testing.T) {
  1121  	_, tearDown := setupConfigDatabase(t, emptyConfig, nil)
  1122  	defer tearDown()
  1123  
  1124  	ds, err := newTestDatabaseStore(nil)
  1125  	require.NoError(t, err)
  1126  	require.NotNil(t, ds)
  1127  	defer ds.Close()
  1128  
  1129  	dbs, ok := ds.backingStore.(*DatabaseStore)
  1130  	require.True(t, ok, "should be a DatabaseStore instance")
  1131  
  1132  	b, err := marshalConfig(ds.config)
  1133  	require.NoError(t, err)
  1134  
  1135  	ds.config.JobSettings.CleanupConfigThresholdDays = model.NewInt(30) // we set 30 days as threshold
  1136  
  1137  	now := time.Now()
  1138  	for i := 0; i < 5; i++ {
  1139  		// 20 days, we expect to remove at least 3 configuration values from the store
  1140  		// first 2 (0 and 1) will be within a month constraint, others will be older than
  1141  		// a month hence we expect 3 configurations to be removed from the database.
  1142  		m := -1 * i * 24 * 20
  1143  		params := map[string]any{
  1144  			"id":        model.NewId(),
  1145  			"value":     string(b),
  1146  			"create_at": model.GetMillisForTime(now.Add(time.Duration(m) * time.Hour)),
  1147  		}
  1148  
  1149  		_, err = dbs.db.NamedExec("INSERT INTO Configurations (Id, Value, CreateAt) VALUES (:id, :value, :create_at)", params)
  1150  		require.NoError(t, err)
  1151  	}
  1152  	var initialCount int
  1153  	row := dbs.db.QueryRow("SELECT COUNT(*) FROM Configurations")
  1154  	err = row.Scan(&initialCount)
  1155  	require.NoError(t, err)
  1156  
  1157  	err = ds.CleanUp()
  1158  	require.NoError(t, err)
  1159  
  1160  	var count int
  1161  	row = dbs.db.QueryRow("SELECT COUNT(*) FROM Configurations")
  1162  	err = row.Scan(&count)
  1163  	require.NoError(t, err)
  1164  	require.True(t, count+3 == initialCount)
  1165  }