github.com/crspeller/mattermost-server@v0.0.0-20190328001957-a200beb3d111/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/crspeller/mattermost-server/config"
    20  	"github.com/crspeller/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  		assert.Equal(t, "http://minimal", *ds.Get().ServiceSettings.SiteURL)
   411  
   412  		os.Setenv("MM_SERVICESETTINGS_SITEURL", "http://override")
   413  
   414  		err = ds.Load()
   415  		require.NoError(t, err)
   416  		assert.Equal(t, "http://override", *ds.Get().ServiceSettings.SiteURL)
   417  		assert.Equal(t, map[string]interface{}{"ServiceSettings": map[string]interface{}{"SiteURL": true}}, ds.GetEnvironmentOverrides())
   418  	})
   419  
   420  	t.Run("do not persist environment variables", func(t *testing.T) {
   421  		_, tearDown := setupConfigDatabase(t, minimalConfig, nil)
   422  		defer tearDown()
   423  
   424  		os.Setenv("MM_SERVICESETTINGS_SITEURL", "http://overridePersistEnvVariables")
   425  
   426  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   427  		require.NoError(t, err)
   428  		defer ds.Close()
   429  
   430  		_, err = ds.Set(ds.Get())
   431  		require.NoError(t, err)
   432  
   433  		assert.Equal(t, "http://overridePersistEnvVariables", *ds.Get().ServiceSettings.SiteURL)
   434  		assert.Equal(t, map[string]interface{}{"ServiceSettings": map[string]interface{}{"SiteURL": true}}, ds.GetEnvironmentOverrides())
   435  		// check that in DB config does not include overwritten variable
   436  		_, actualConfig := getActualDatabaseConfig(t)
   437  		assert.Equal(t, "http://minimal", *actualConfig.ServiceSettings.SiteURL)
   438  	})
   439  
   440  	t.Run("invalid", func(t *testing.T) {
   441  		_, tearDown := setupConfigDatabase(t, emptyConfig, nil)
   442  		defer tearDown()
   443  
   444  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   445  		require.NoError(t, err)
   446  		defer ds.Close()
   447  
   448  		cfgData, err := config.MarshalConfig(invalidConfig)
   449  		require.NoError(t, err)
   450  
   451  		db := sqlx.NewDb(mainHelper.GetSqlSupplier().GetMaster().Db, *sqlSettings.DriverName)
   452  		truncateTables(t)
   453  		id := model.NewId()
   454  		_, err = db.NamedExec("INSERT INTO Configurations (Id, Value, CreateAt, Active) VALUES(:Id, :Value, :CreateAt, TRUE)", map[string]interface{}{
   455  			"Id":       id,
   456  			"Value":    cfgData,
   457  			"CreateAt": model.GetMillis(),
   458  		})
   459  		require.NoError(t, err)
   460  
   461  		err = ds.Load()
   462  		if assert.Error(t, err) {
   463  			assert.EqualError(t, err, "invalid config: Config.IsValid: model.config.is_valid.site_url.app_error, ")
   464  		}
   465  	})
   466  
   467  	t.Run("fixes required", func(t *testing.T) {
   468  		_, tearDown := setupConfigDatabase(t, fixesRequiredConfig, nil)
   469  		defer tearDown()
   470  
   471  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   472  		require.NoError(t, err)
   473  		defer ds.Close()
   474  
   475  		err = ds.Load()
   476  		require.NoError(t, err)
   477  		assertDatabaseNotEqualsConfig(t, fixesRequiredConfig)
   478  		assert.Equal(t, "http://trailingslash", *ds.Get().ServiceSettings.SiteURL)
   479  	})
   480  
   481  	t.Run("listeners notifed", func(t *testing.T) {
   482  		_, tearDown := setupConfigDatabase(t, emptyConfig, nil)
   483  		defer tearDown()
   484  
   485  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   486  		require.NoError(t, err)
   487  		defer ds.Close()
   488  
   489  		called := make(chan bool, 1)
   490  		callback := func(oldfg, newCfg *model.Config) {
   491  			called <- true
   492  		}
   493  		ds.AddListener(callback)
   494  
   495  		err = ds.Load()
   496  		require.NoError(t, err)
   497  
   498  		select {
   499  		case <-called:
   500  		case <-time.After(5 * time.Second):
   501  			t.Fatal("callback should have been called when config loaded")
   502  		}
   503  	})
   504  }
   505  
   506  func TestDatabaseGetFile(t *testing.T) {
   507  	_, tearDown := setupConfigDatabase(t, minimalConfig, map[string][]byte{
   508  		"empty-file": []byte{},
   509  		"test-file":  []byte("test"),
   510  	})
   511  	defer tearDown()
   512  
   513  	ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *mainHelper.Settings.DriverName, *mainHelper.Settings.DataSource))
   514  	require.NoError(t, err)
   515  	defer ds.Close()
   516  
   517  	t.Run("get empty filename", func(t *testing.T) {
   518  		_, err := ds.GetFile("")
   519  		require.Error(t, err)
   520  	})
   521  
   522  	t.Run("get non-existent file", func(t *testing.T) {
   523  		_, err := ds.GetFile("unknown")
   524  		require.Error(t, err)
   525  	})
   526  
   527  	t.Run("get empty file", func(t *testing.T) {
   528  		data, err := ds.GetFile("empty-file")
   529  		require.NoError(t, err)
   530  		require.Empty(t, data)
   531  	})
   532  
   533  	t.Run("get non-empty file", func(t *testing.T) {
   534  		data, err := ds.GetFile("test-file")
   535  		require.NoError(t, err)
   536  		require.Equal(t, []byte("test"), data)
   537  	})
   538  }
   539  
   540  func TestDatabaseSetFile(t *testing.T) {
   541  	_, tearDown := setupConfigDatabase(t, minimalConfig, nil)
   542  	defer tearDown()
   543  
   544  	ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *mainHelper.Settings.DriverName, *mainHelper.Settings.DataSource))
   545  	require.NoError(t, err)
   546  	defer ds.Close()
   547  
   548  	t.Run("set new file", func(t *testing.T) {
   549  		err := ds.SetFile("new", []byte("new file"))
   550  		require.NoError(t, err)
   551  
   552  		data, err := ds.GetFile("new")
   553  		require.NoError(t, err)
   554  		require.Equal(t, []byte("new file"), data)
   555  	})
   556  
   557  	t.Run("overwrite existing file", func(t *testing.T) {
   558  		err := ds.SetFile("existing", []byte("existing file"))
   559  		require.NoError(t, err)
   560  
   561  		err = ds.SetFile("existing", []byte("overwritten file"))
   562  		require.NoError(t, err)
   563  
   564  		data, err := ds.GetFile("existing")
   565  		require.NoError(t, err)
   566  		require.Equal(t, []byte("overwritten file"), data)
   567  	})
   568  }
   569  
   570  func TestDatabaseHasFile(t *testing.T) {
   571  	t.Run("has non-existent", func(t *testing.T) {
   572  		_, tearDown := setupConfigDatabase(t, minimalConfig, nil)
   573  		defer tearDown()
   574  
   575  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *mainHelper.Settings.DriverName, *mainHelper.Settings.DataSource))
   576  		require.NoError(t, err)
   577  		defer ds.Close()
   578  
   579  		has, err := ds.HasFile("non-existent")
   580  		require.NoError(t, err)
   581  		require.False(t, has)
   582  	})
   583  
   584  	t.Run("has existing", func(t *testing.T) {
   585  		_, tearDown := setupConfigDatabase(t, minimalConfig, nil)
   586  		defer tearDown()
   587  
   588  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *mainHelper.Settings.DriverName, *mainHelper.Settings.DataSource))
   589  		require.NoError(t, err)
   590  		defer ds.Close()
   591  
   592  		err = ds.SetFile("existing", []byte("existing file"))
   593  		require.NoError(t, err)
   594  
   595  		has, err := ds.HasFile("existing")
   596  		require.NoError(t, err)
   597  		require.True(t, has)
   598  	})
   599  
   600  	t.Run("has manually created file", func(t *testing.T) {
   601  		_, tearDown := setupConfigDatabase(t, minimalConfig, map[string][]byte{
   602  			"manual": []byte("manual file"),
   603  		})
   604  		defer tearDown()
   605  
   606  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *mainHelper.Settings.DriverName, *mainHelper.Settings.DataSource))
   607  		require.NoError(t, err)
   608  		defer ds.Close()
   609  
   610  		has, err := ds.HasFile("manual")
   611  		require.NoError(t, err)
   612  		require.True(t, has)
   613  	})
   614  }
   615  
   616  func TestDatabaseRemoveFile(t *testing.T) {
   617  	t.Run("remove non-existent", func(t *testing.T) {
   618  		_, tearDown := setupConfigDatabase(t, minimalConfig, nil)
   619  		defer tearDown()
   620  
   621  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *mainHelper.Settings.DriverName, *mainHelper.Settings.DataSource))
   622  		require.NoError(t, err)
   623  		defer ds.Close()
   624  
   625  		err = ds.RemoveFile("non-existent")
   626  		require.NoError(t, err)
   627  	})
   628  
   629  	t.Run("remove existing", func(t *testing.T) {
   630  		_, tearDown := setupConfigDatabase(t, minimalConfig, nil)
   631  		defer tearDown()
   632  
   633  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *mainHelper.Settings.DriverName, *mainHelper.Settings.DataSource))
   634  		require.NoError(t, err)
   635  		defer ds.Close()
   636  
   637  		err = ds.SetFile("existing", []byte("existing file"))
   638  		require.NoError(t, err)
   639  
   640  		err = ds.RemoveFile("existing")
   641  		require.NoError(t, err)
   642  
   643  		has, err := ds.HasFile("existing")
   644  		require.NoError(t, err)
   645  		require.False(t, has)
   646  
   647  		_, err = ds.GetFile("existing")
   648  		require.Error(t, err)
   649  	})
   650  
   651  	t.Run("remove manually created file", func(t *testing.T) {
   652  		_, tearDown := setupConfigDatabase(t, minimalConfig, map[string][]byte{
   653  			"manual": []byte("manual file"),
   654  		})
   655  		defer tearDown()
   656  
   657  		ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *mainHelper.Settings.DriverName, *mainHelper.Settings.DataSource))
   658  		require.NoError(t, err)
   659  		defer ds.Close()
   660  
   661  		err = ds.RemoveFile("manual")
   662  		require.NoError(t, err)
   663  
   664  		has, err := ds.HasFile("manual")
   665  		require.NoError(t, err)
   666  		require.False(t, has)
   667  
   668  		_, err = ds.GetFile("manual")
   669  		require.Error(t, err)
   670  	})
   671  }
   672  
   673  func TestDatabaseStoreString(t *testing.T) {
   674  	_, tearDown := setupConfigDatabase(t, emptyConfig, nil)
   675  	defer tearDown()
   676  
   677  	sqlSettings := mainHelper.GetSqlSettings()
   678  	ds, err := config.NewDatabaseStore(fmt.Sprintf("%s://%s", *sqlSettings.DriverName, *sqlSettings.DataSource))
   679  	require.NoError(t, err)
   680  	defer ds.Close()
   681  
   682  	actualStringURL, err := url.Parse(ds.String())
   683  	require.NoError(t, err)
   684  
   685  	assert.Equal(t, *sqlSettings.DriverName, actualStringURL.Scheme)
   686  	actualUsername := actualStringURL.User.Username()
   687  	actualPassword, _ := actualStringURL.User.Password()
   688  	assert.NotEmpty(t, actualUsername)
   689  	assert.Empty(t, actualPassword, "should mask password")
   690  }