github.com/isacikgoz/mattermost-server@v5.11.1+incompatible/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  	"net/url"
    10  	"os"
    11  	"strings"
    12  	"testing"
    13  	"time"
    14  
    15  	"github.com/jmoiron/sqlx"
    16  	"github.com/stretchr/testify/assert"
    17  	"github.com/stretchr/testify/require"
    18  
    19  	"github.com/mattermost/mattermost-server/config"
    20  	"github.com/mattermost/mattermost-server/model"
    21  )
    22  
    23  func setupConfigDatabase(t *testing.T, cfg *model.Config, files map[string][]byte) (string, func()) {
    24  	t.Helper()
    25  	os.Clearenv()
    26  	truncateTables(t)
    27  
    28  	cfgData, err := config.MarshalConfig(cfg)
    29  	require.NoError(t, err)
    30  
    31  	db := sqlx.NewDb(mainHelper.GetSqlSupplier().GetMaster().Db, *mainHelper.GetSqlSettings().DriverName)
    32  	err = config.InitializeConfigurationsTable(db)
    33  	require.NoError(t, err)
    34  
    35  	id := model.NewId()
    36  	_, err = db.NamedExec("INSERT INTO Configurations (Id, Value, CreateAt, Active) VALUES(:Id, :Value, :CreateAt, TRUE)", map[string]interface{}{
    37  		"Id":       id,
    38  		"Value":    cfgData,
    39  		"CreateAt": model.GetMillis(),
    40  	})
    41  	require.NoError(t, err)
    42  
    43  	for name, data := range files {
    44  		params := map[string]interface{}{
    45  			"name":      name,
    46  			"data":      data,
    47  			"create_at": model.GetMillis(),
    48  			"update_at": model.GetMillis(),
    49  		}
    50  
    51  		_, err = db.NamedExec("INSERT INTO ConfigurationFiles (Name, Data, CreateAt, UpdateAt) VALUES (:name, :data, :create_at, :update_at)", params)
    52  		require.NoError(t, err)
    53  	}
    54  
    55  	return id, func() {
    56  		truncateTables(t)
    57  	}
    58  }
    59  
    60  // getActualDatabaseConfig returns the active configuration in the database without relying on a config store.
    61  func getActualDatabaseConfig(t *testing.T) (string, *model.Config) {
    62  	t.Helper()
    63  
    64  	var actual struct {
    65  		Id    string `db:"Id"`
    66  		Value []byte `db:"Value"`
    67  	}
    68  	db := sqlx.NewDb(mainHelper.GetSqlSupplier().GetMaster().Db, *mainHelper.GetSqlSettings().DriverName)
    69  	err := db.Get(&actual, "SELECT Id, Value FROM Configurations WHERE Active")
    70  	require.NoError(t, err)
    71  
    72  	actualCfg, _, err := config.UnmarshalConfig(bytes.NewReader(actual.Value), false)
    73  	require.Nil(t, err)
    74  
    75  	return actual.Id, actualCfg
    76  }
    77  
    78  // assertDatabaseEqualsConfig verifies the active in-database configuration equals the given config.
    79  func assertDatabaseEqualsConfig(t *testing.T, expectedCfg *model.Config) {
    80  	t.Helper()
    81  
    82  	expectedCfg = prepareExpectedConfig(t, expectedCfg)
    83  	_, actualCfg := getActualDatabaseConfig(t)
    84  	assert.Equal(t, expectedCfg, actualCfg)
    85  }
    86  
    87  // assertDatabaseNotEqualsConfig verifies the in-database configuration does not equal the given config.
    88  func assertDatabaseNotEqualsConfig(t *testing.T, expectedCfg *model.Config) {
    89  	t.Helper()
    90  
    91  	expectedCfg = prepareExpectedConfig(t, expectedCfg)
    92  	_, actualCfg := getActualDatabaseConfig(t)
    93  	assert.NotEqual(t, expectedCfg, actualCfg)
    94  }
    95  
    96  func TestDatabaseStoreNew(t *testing.T) {
    97  	sqlSettings := mainHelper.GetSqlSettings()
    98  
    99  	t.Run("no existing configuration - initialization required", func(t *testing.T) {
   100  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   101  		require.NoError(t, err)
   102  		defer ds.Close()
   103  
   104  		assert.Equal(t, model.SERVICE_SETTINGS_DEFAULT_SITE_URL, *ds.Get().ServiceSettings.SiteURL)
   105  	})
   106  
   107  	t.Run("existing config, initialization required", func(t *testing.T) {
   108  		_, tearDown := setupConfigDatabase(t, testConfig, nil)
   109  		defer tearDown()
   110  
   111  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   112  		require.NoError(t, err)
   113  		defer ds.Close()
   114  
   115  		assert.Equal(t, "http://TestStoreNew", *ds.Get().ServiceSettings.SiteURL)
   116  		assertDatabaseNotEqualsConfig(t, testConfig)
   117  	})
   118  
   119  	t.Run("already minimally configured", func(t *testing.T) {
   120  		_, tearDown := setupConfigDatabase(t, minimalConfig, nil)
   121  		defer tearDown()
   122  
   123  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   124  		require.NoError(t, err)
   125  		defer ds.Close()
   126  
   127  		assert.Equal(t, "http://minimal", *ds.Get().ServiceSettings.SiteURL)
   128  		assertDatabaseEqualsConfig(t, minimalConfig)
   129  	})
   130  
   131  	t.Run("invalid url", func(t *testing.T) {
   132  		_, err := config.NewDatabaseStore("")
   133  		require.Error(t, err)
   134  	})
   135  
   136  	t.Run("unsupported scheme", func(t *testing.T) {
   137  		_, err := config.NewDatabaseStore("invalid")
   138  		require.Error(t, err)
   139  	})
   140  }
   141  
   142  func TestDatabaseStoreGet(t *testing.T) {
   143  	_, tearDown := setupConfigDatabase(t, testConfig, nil)
   144  	defer tearDown()
   145  
   146  	sqlSettings := mainHelper.GetSqlSettings()
   147  	ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   148  	require.NoError(t, err)
   149  	defer ds.Close()
   150  
   151  	cfg := ds.Get()
   152  	assert.Equal(t, "http://TestStoreNew", *cfg.ServiceSettings.SiteURL)
   153  
   154  	cfg2 := ds.Get()
   155  	assert.Equal(t, "http://TestStoreNew", *cfg.ServiceSettings.SiteURL)
   156  
   157  	assert.True(t, cfg == cfg2, "Get() returned different configuration instances")
   158  
   159  	newCfg := &model.Config{}
   160  	oldCfg, err := ds.Set(newCfg)
   161  	require.NoError(t, err)
   162  
   163  	assert.True(t, oldCfg == cfg, "returned config after set() changed original")
   164  	assert.False(t, newCfg == cfg, "returned config should have been different from original")
   165  }
   166  
   167  func TestDatabaseStoreGetEnivironmentOverrides(t *testing.T) {
   168  	_, tearDown := setupConfigDatabase(t, testConfig, nil)
   169  	defer tearDown()
   170  
   171  	sqlSettings := mainHelper.GetSqlSettings()
   172  	ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   173  	require.NoError(t, err)
   174  	defer ds.Close()
   175  
   176  	assert.Equal(t, "http://TestStoreNew", *ds.Get().ServiceSettings.SiteURL)
   177  	assert.Empty(t, ds.GetEnvironmentOverrides())
   178  
   179  	os.Setenv("MM_SERVICESETTINGS_SITEURL", "http://override")
   180  
   181  	ds, err = config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   182  	require.NoError(t, err)
   183  	defer ds.Close()
   184  
   185  	assert.Equal(t, "http://override", *ds.Get().ServiceSettings.SiteURL)
   186  	assert.Equal(t, map[string]interface{}{"ServiceSettings": map[string]interface{}{"SiteURL": true}}, ds.GetEnvironmentOverrides())
   187  }
   188  
   189  func TestDatabaseStoreSet(t *testing.T) {
   190  	sqlSettings := mainHelper.GetSqlSettings()
   191  
   192  	t.Run("set same pointer value", func(t *testing.T) {
   193  		t.Skip("not yet implemented")
   194  
   195  		_, tearDown := setupConfigDatabase(t, emptyConfig, nil)
   196  		defer tearDown()
   197  
   198  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   199  		require.NoError(t, err)
   200  		defer ds.Close()
   201  
   202  		_, err = ds.Set(ds.Get())
   203  		if assert.Error(t, err) {
   204  			assert.EqualError(t, err, "old configuration modified instead of cloning")
   205  		}
   206  	})
   207  
   208  	t.Run("defaults required", func(t *testing.T) {
   209  		_, tearDown := setupConfigDatabase(t, minimalConfig, nil)
   210  		defer tearDown()
   211  
   212  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   213  		require.NoError(t, err)
   214  		defer ds.Close()
   215  
   216  		oldCfg := ds.Get()
   217  
   218  		newCfg := &model.Config{}
   219  
   220  		retCfg, err := ds.Set(newCfg)
   221  		require.NoError(t, err)
   222  		assert.Equal(t, oldCfg, retCfg)
   223  
   224  		assert.Equal(t, model.SERVICE_SETTINGS_DEFAULT_SITE_URL, *ds.Get().ServiceSettings.SiteURL)
   225  	})
   226  
   227  	t.Run("desanitization required", func(t *testing.T) {
   228  		_, tearDown := setupConfigDatabase(t, ldapConfig, nil)
   229  		defer tearDown()
   230  
   231  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   232  		require.NoError(t, err)
   233  		defer ds.Close()
   234  
   235  		oldCfg := ds.Get()
   236  
   237  		newCfg := &model.Config{}
   238  		newCfg.LdapSettings.BindPassword = sToP(model.FAKE_SETTING)
   239  
   240  		retCfg, err := ds.Set(newCfg)
   241  		require.NoError(t, err)
   242  		assert.Equal(t, oldCfg, retCfg)
   243  
   244  		assert.Equal(t, "password", *ds.Get().LdapSettings.BindPassword)
   245  	})
   246  
   247  	t.Run("invalid", func(t *testing.T) {
   248  		_, tearDown := setupConfigDatabase(t, emptyConfig, nil)
   249  		defer tearDown()
   250  
   251  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   252  		require.NoError(t, err)
   253  		defer ds.Close()
   254  
   255  		newCfg := &model.Config{}
   256  		newCfg.ServiceSettings.SiteURL = sToP("invalid")
   257  
   258  		_, err = ds.Set(newCfg)
   259  		if assert.Error(t, err) {
   260  			assert.EqualError(t, err, "new configuration is invalid: Config.IsValid: model.config.is_valid.site_url.app_error, ")
   261  		}
   262  
   263  		assert.Equal(t, model.SERVICE_SETTINGS_DEFAULT_SITE_URL, *ds.Get().ServiceSettings.SiteURL)
   264  	})
   265  
   266  	t.Run("duplicate ignored", func(t *testing.T) {
   267  		_, tearDown := setupConfigDatabase(t, minimalConfig, nil)
   268  		defer tearDown()
   269  
   270  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   271  		require.NoError(t, err)
   272  		defer ds.Close()
   273  
   274  		_, err = ds.Set(ds.Get())
   275  		require.NoError(t, err)
   276  
   277  		beforeId, _ := getActualDatabaseConfig(t)
   278  		_, err = ds.Set(ds.Get())
   279  		require.NoError(t, err)
   280  
   281  		afterId, _ := getActualDatabaseConfig(t)
   282  		assert.Equal(t, beforeId, afterId, "new record should not have been written")
   283  	})
   284  
   285  	t.Run("read-only ignored", func(t *testing.T) {
   286  		_, tearDown := setupConfigDatabase(t, readOnlyConfig, nil)
   287  		defer tearDown()
   288  
   289  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   290  		require.NoError(t, err)
   291  		defer ds.Close()
   292  
   293  		newCfg := &model.Config{
   294  			ServiceSettings: model.ServiceSettings{
   295  				SiteURL: sToP("http://new"),
   296  			},
   297  		}
   298  
   299  		_, err = ds.Set(newCfg)
   300  		require.NoError(t, err)
   301  
   302  		assert.Equal(t, "http://new", *ds.Get().ServiceSettings.SiteURL)
   303  	})
   304  
   305  	t.Run("set with automatic save", func(t *testing.T) {
   306  		_, tearDown := setupConfigDatabase(t, minimalConfig, nil)
   307  		defer tearDown()
   308  
   309  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   310  		require.NoError(t, err)
   311  		defer ds.Close()
   312  
   313  		newCfg := &model.Config{
   314  			ServiceSettings: model.ServiceSettings{
   315  				SiteURL: sToP("http://new"),
   316  			},
   317  		}
   318  
   319  		_, err = ds.Set(newCfg)
   320  		require.NoError(t, err)
   321  
   322  		err = ds.Load()
   323  		require.NoError(t, err)
   324  
   325  		assert.Equal(t, "http://new", *ds.Get().ServiceSettings.SiteURL)
   326  	})
   327  
   328  	t.Run("persist failed", func(t *testing.T) {
   329  		t.Skip("skipping persistence test inside Set")
   330  		_, tearDown := setupConfigDatabase(t, emptyConfig, nil)
   331  		defer tearDown()
   332  
   333  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   334  		require.NoError(t, err)
   335  		defer ds.Close()
   336  
   337  		db := sqlx.NewDb(mainHelper.GetSqlSupplier().GetMaster().Db, *sqlSettings.DriverName)
   338  		_, err = db.Exec("DROP TABLE Configurations")
   339  		require.NoError(t, err)
   340  
   341  		newCfg := &model.Config{}
   342  
   343  		_, err = ds.Set(newCfg)
   344  		if assert.Error(t, err) {
   345  			assert.True(t, strings.HasPrefix(err.Error(), "failed to persist: failed to write to database"))
   346  		}
   347  
   348  		assert.Equal(t, model.SERVICE_SETTINGS_DEFAULT_SITE_URL, *ds.Get().ServiceSettings.SiteURL)
   349  	})
   350  
   351  	t.Run("listeners notified", func(t *testing.T) {
   352  		activeId, tearDown := setupConfigDatabase(t, emptyConfig, nil)
   353  		defer tearDown()
   354  
   355  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   356  		require.NoError(t, err)
   357  		defer ds.Close()
   358  
   359  		oldCfg := ds.Get()
   360  
   361  		called := make(chan bool, 1)
   362  		callback := func(oldfg, newCfg *model.Config) {
   363  			called <- true
   364  		}
   365  		ds.AddListener(callback)
   366  
   367  		newCfg := &model.Config{}
   368  
   369  		retCfg, err := ds.Set(newCfg)
   370  		require.NoError(t, err)
   371  		assert.Equal(t, oldCfg, retCfg)
   372  
   373  		id, _ := getActualDatabaseConfig(t)
   374  		assert.NotEqual(t, activeId, id, "new record should have been written")
   375  
   376  		select {
   377  		case <-called:
   378  		case <-time.After(5 * time.Second):
   379  			t.Fatal("callback should have been called when config written")
   380  		}
   381  	})
   382  }
   383  
   384  func TestDatabaseStoreLoad(t *testing.T) {
   385  	sqlSettings := mainHelper.GetSqlSettings()
   386  
   387  	t.Run("active configuration no longer exists", func(t *testing.T) {
   388  		_, tearDown := setupConfigDatabase(t, emptyConfig, nil)
   389  		defer tearDown()
   390  
   391  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   392  		require.NoError(t, err)
   393  		defer ds.Close()
   394  
   395  		truncateTables(t)
   396  
   397  		err = ds.Load()
   398  		require.NoError(t, err)
   399  		assertDatabaseNotEqualsConfig(t, emptyConfig)
   400  	})
   401  
   402  	t.Run("honour environment", func(t *testing.T) {
   403  		_, tearDown := setupConfigDatabase(t, minimalConfig, nil)
   404  		defer tearDown()
   405  
   406  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   407  		require.NoError(t, err)
   408  		defer ds.Close()
   409  
   410  		os.Setenv("MM_SERVICESETTINGS_SITEURL", "http://override")
   411  
   412  		err = ds.Load()
   413  		require.NoError(t, err)
   414  		assert.Equal(t, "http://override", *ds.Get().ServiceSettings.SiteURL)
   415  		assert.Equal(t, map[string]interface{}{"ServiceSettings": map[string]interface{}{"SiteURL": true}}, ds.GetEnvironmentOverrides())
   416  	})
   417  
   418  	t.Run("invalid", func(t *testing.T) {
   419  		_, tearDown := setupConfigDatabase(t, emptyConfig, nil)
   420  		defer tearDown()
   421  
   422  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   423  		require.NoError(t, err)
   424  		defer ds.Close()
   425  
   426  		cfgData, err := config.MarshalConfig(invalidConfig)
   427  		require.NoError(t, err)
   428  
   429  		db := sqlx.NewDb(mainHelper.GetSqlSupplier().GetMaster().Db, *sqlSettings.DriverName)
   430  		truncateTables(t)
   431  		id := model.NewId()
   432  		_, err = db.NamedExec("INSERT INTO Configurations (Id, Value, CreateAt, Active) VALUES(:Id, :Value, :CreateAt, TRUE)", map[string]interface{}{
   433  			"Id":       id,
   434  			"Value":    cfgData,
   435  			"CreateAt": model.GetMillis(),
   436  		})
   437  		require.NoError(t, err)
   438  
   439  		err = ds.Load()
   440  		if assert.Error(t, err) {
   441  			assert.EqualError(t, err, "invalid config: Config.IsValid: model.config.is_valid.site_url.app_error, ")
   442  		}
   443  	})
   444  
   445  	t.Run("fixes required", func(t *testing.T) {
   446  		_, tearDown := setupConfigDatabase(t, fixesRequiredConfig, nil)
   447  		defer tearDown()
   448  
   449  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   450  		require.NoError(t, err)
   451  		defer ds.Close()
   452  
   453  		err = ds.Load()
   454  		require.NoError(t, err)
   455  		assertDatabaseNotEqualsConfig(t, fixesRequiredConfig)
   456  		assert.Equal(t, "http://trailingslash", *ds.Get().ServiceSettings.SiteURL)
   457  	})
   458  
   459  	t.Run("listeners notifed", func(t *testing.T) {
   460  		_, tearDown := setupConfigDatabase(t, emptyConfig, nil)
   461  		defer tearDown()
   462  
   463  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   464  		require.NoError(t, err)
   465  		defer ds.Close()
   466  
   467  		called := make(chan bool, 1)
   468  		callback := func(oldfg, newCfg *model.Config) {
   469  			called <- true
   470  		}
   471  		ds.AddListener(callback)
   472  
   473  		err = ds.Load()
   474  		require.NoError(t, err)
   475  
   476  		select {
   477  		case <-called:
   478  		case <-time.After(5 * time.Second):
   479  			t.Fatal("callback should have been called when config loaded")
   480  		}
   481  	})
   482  }
   483  
   484  func TestDatabaseGetFile(t *testing.T) {
   485  	_, tearDown := setupConfigDatabase(t, minimalConfig, map[string][]byte{
   486  		"empty-file": []byte{},
   487  		"test-file":  []byte("test"),
   488  	})
   489  	defer tearDown()
   490  
   491  	ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *mainHelper.Settings.DriverName, *mainHelper.Settings.DataSource))
   492  	require.NoError(t, err)
   493  	defer ds.Close()
   494  
   495  	t.Run("get empty filename", func(t *testing.T) {
   496  		_, err := ds.GetFile("")
   497  		require.Error(t, err)
   498  	})
   499  
   500  	t.Run("get non-existent file", func(t *testing.T) {
   501  		_, err := ds.GetFile("unknown")
   502  		require.Error(t, err)
   503  	})
   504  
   505  	t.Run("get empty file", func(t *testing.T) {
   506  		data, err := ds.GetFile("empty-file")
   507  		require.NoError(t, err)
   508  		require.Empty(t, data)
   509  	})
   510  
   511  	t.Run("get non-empty file", func(t *testing.T) {
   512  		data, err := ds.GetFile("test-file")
   513  		require.NoError(t, err)
   514  		require.Equal(t, []byte("test"), data)
   515  	})
   516  }
   517  
   518  func TestDatabaseSetFile(t *testing.T) {
   519  	_, tearDown := setupConfigDatabase(t, minimalConfig, nil)
   520  	defer tearDown()
   521  
   522  	ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *mainHelper.Settings.DriverName, *mainHelper.Settings.DataSource))
   523  	require.NoError(t, err)
   524  	defer ds.Close()
   525  
   526  	t.Run("set new file", func(t *testing.T) {
   527  		err := ds.SetFile("new", []byte("new file"))
   528  		require.NoError(t, err)
   529  
   530  		data, err := ds.GetFile("new")
   531  		require.NoError(t, err)
   532  		require.Equal(t, []byte("new file"), data)
   533  	})
   534  
   535  	t.Run("overwrite existing file", func(t *testing.T) {
   536  		err := ds.SetFile("existing", []byte("existing file"))
   537  		require.NoError(t, err)
   538  
   539  		err = ds.SetFile("existing", []byte("overwritten file"))
   540  		require.NoError(t, err)
   541  
   542  		data, err := ds.GetFile("existing")
   543  		require.NoError(t, err)
   544  		require.Equal(t, []byte("overwritten file"), data)
   545  	})
   546  }
   547  
   548  func TestDatabaseHasFile(t *testing.T) {
   549  	t.Run("has non-existent", func(t *testing.T) {
   550  		_, tearDown := setupConfigDatabase(t, minimalConfig, nil)
   551  		defer tearDown()
   552  
   553  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *mainHelper.Settings.DriverName, *mainHelper.Settings.DataSource))
   554  		require.NoError(t, err)
   555  		defer ds.Close()
   556  
   557  		has, err := ds.HasFile("non-existent")
   558  		require.NoError(t, err)
   559  		require.False(t, has)
   560  	})
   561  
   562  	t.Run("has existing", func(t *testing.T) {
   563  		_, tearDown := setupConfigDatabase(t, minimalConfig, nil)
   564  		defer tearDown()
   565  
   566  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *mainHelper.Settings.DriverName, *mainHelper.Settings.DataSource))
   567  		require.NoError(t, err)
   568  		defer ds.Close()
   569  
   570  		err = ds.SetFile("existing", []byte("existing file"))
   571  		require.NoError(t, err)
   572  
   573  		has, err := ds.HasFile("existing")
   574  		require.NoError(t, err)
   575  		require.True(t, has)
   576  	})
   577  
   578  	t.Run("has manually created file", func(t *testing.T) {
   579  		_, tearDown := setupConfigDatabase(t, minimalConfig, map[string][]byte{
   580  			"manual": []byte("manual file"),
   581  		})
   582  		defer tearDown()
   583  
   584  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *mainHelper.Settings.DriverName, *mainHelper.Settings.DataSource))
   585  		require.NoError(t, err)
   586  		defer ds.Close()
   587  
   588  		has, err := ds.HasFile("manual")
   589  		require.NoError(t, err)
   590  		require.True(t, has)
   591  	})
   592  }
   593  
   594  func TestDatabaseRemoveFile(t *testing.T) {
   595  	t.Run("remove non-existent", func(t *testing.T) {
   596  		_, tearDown := setupConfigDatabase(t, minimalConfig, nil)
   597  		defer tearDown()
   598  
   599  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *mainHelper.Settings.DriverName, *mainHelper.Settings.DataSource))
   600  		require.NoError(t, err)
   601  		defer ds.Close()
   602  
   603  		err = ds.RemoveFile("non-existent")
   604  		require.NoError(t, err)
   605  	})
   606  
   607  	t.Run("remove existing", func(t *testing.T) {
   608  		_, tearDown := setupConfigDatabase(t, minimalConfig, nil)
   609  		defer tearDown()
   610  
   611  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *mainHelper.Settings.DriverName, *mainHelper.Settings.DataSource))
   612  		require.NoError(t, err)
   613  		defer ds.Close()
   614  
   615  		err = ds.SetFile("existing", []byte("existing file"))
   616  		require.NoError(t, err)
   617  
   618  		err = ds.RemoveFile("existing")
   619  		require.NoError(t, err)
   620  
   621  		has, err := ds.HasFile("existing")
   622  		require.NoError(t, err)
   623  		require.False(t, has)
   624  
   625  		_, err = ds.GetFile("existing")
   626  		require.Error(t, err)
   627  	})
   628  
   629  	t.Run("remove manually created file", func(t *testing.T) {
   630  		_, tearDown := setupConfigDatabase(t, minimalConfig, map[string][]byte{
   631  			"manual": []byte("manual file"),
   632  		})
   633  		defer tearDown()
   634  
   635  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *mainHelper.Settings.DriverName, *mainHelper.Settings.DataSource))
   636  		require.NoError(t, err)
   637  		defer ds.Close()
   638  
   639  		err = ds.RemoveFile("manual")
   640  		require.NoError(t, err)
   641  
   642  		has, err := ds.HasFile("manual")
   643  		require.NoError(t, err)
   644  		require.False(t, has)
   645  
   646  		_, err = ds.GetFile("manual")
   647  		require.Error(t, err)
   648  	})
   649  }
   650  
   651  func TestDatabaseStoreString(t *testing.T) {
   652  	_, tearDown := setupConfigDatabase(t, emptyConfig, nil)
   653  	defer tearDown()
   654  
   655  	sqlSettings := mainHelper.GetSqlSettings()
   656  	ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   657  	require.NoError(t, err)
   658  	defer ds.Close()
   659  
   660  	actualStringURL, err := url.Parse(ds.String())
   661  	require.NoError(t, err)
   662  
   663  	assert.Equal(t, *sqlSettings.DriverName, actualStringURL.Scheme)
   664  	actualUsername := actualStringURL.User.Username()
   665  	actualPassword, _ := actualStringURL.User.Password()
   666  	assert.NotEmpty(t, actualUsername)
   667  	assert.Empty(t, actualPassword, "should mask password")
   668  }