hunk.code_window: //      "name": "My Plugin",
   //      "description": "This is my plugin",
   //      "homepage_url": "https://example.com",
   //      "support_url": "https://example.com/support",
   //      "icon_path": "assets/logo.svg",
   //      "version": "0.1.0",
   //      "min_server_version": "5.6.0",
  
hunk.labels: keep keep keep add keep keep keep
hunk.after_edit: //      "release_notes_url": "https://example.com/releases/v0.0.1",
  
hunk.file_path: model/manifest.go
hunk.type: add
file: // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
  // See LICENSE.txt for license information.
  
  package config_test
  
  import (
  	"os"
  	"testing"
  	"time"
  
  	"github.com/stretchr/testify/assert"
  	"github.com/stretchr/testify/require"
  
  	"github.com/mattermost/mattermost-server/v5/config"
  	"github.com/mattermost/mattermost-server/v5/model"
  )
  
  func setupConfigMemory(t *testing.T) {
  	t.Helper()
  	os.Clearenv()
  }
  
  func TestMemoryStoreNew(t *testing.T) {
  	t.Run("no existing configuration - initialization required", func(t *testing.T) {
  		ms, err := config.NewMemoryStore()
  		require.NoError(t, err)
  		defer ms.Close()
  
  		assert.Equal(t, "", *ms.Get().ServiceSettings.SiteURL)
  	})
  
  	t.Run("existing config, initialization required", func(t *testing.T) {
  		setupConfigMemory(t)
  
  		ms, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{InitialConfig: testConfig})
  		require.NoError(t, err)
  		defer ms.Close()
  
  		assert.Equal(t, "http://TestStoreNew", *ms.Get().ServiceSettings.SiteURL)
  	})
  
  	t.Run("already minimally configured", func(t *testing.T) {
  		setupConfigMemory(t)
  
  		ms, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{InitialConfig: minimalConfig})
  		require.NoError(t, err)
  		defer ms.Close()
  
  		assert.Equal(t, "http://minimal", *ms.Get().ServiceSettings.SiteURL)
  	})
  
  	t.Run("invalid config, validation enabled", func(t *testing.T) {
  		_, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{InitialConfig: invalidConfig})
  		require.Error(t, err)
  	})
  
  	t.Run("invalid config, validation disabled", func(t *testing.T) {
  		_, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{InitialConfig: invalidConfig, SkipValidation: true})
  		require.NoError(t, err)
  	})
  }
  
  func TestMemoryStoreGet(t *testing.T) {
  	setupConfigMemory(t)
  
  	ms, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{InitialConfig: testConfig})
  	require.NoError(t, err)
  	defer ms.Close()
  
  	cfg := ms.Get()
  	assert.Equal(t, "http://TestStoreNew", *cfg.ServiceSettings.SiteURL)
  
  	cfg2 := ms.Get()
  	assert.Equal(t, "http://TestStoreNew", *cfg.ServiceSettings.SiteURL)
  
  	assert.True(t, cfg == cfg2, "Get() returned different configuration instances")
  
  	newCfg := &model.Config{}
  	oldCfg, err := ms.Set(newCfg)
  	require.NoError(t, err)
  
  	assert.True(t, oldCfg == cfg, "returned config after set() changed original")
  	assert.False(t, newCfg == cfg, "returned config should have been different from original")
  }
  
  func TestMemoryStoreGetEnivironmentOverrides(t *testing.T) {
  	setupConfigMemory(t)
  
  	ms, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{InitialConfig: testConfig})
  	require.NoError(t, err)
  	defer ms.Close()
  
  	assert.Equal(t, "http://TestStoreNew", *ms.Get().ServiceSettings.SiteURL)
  	assert.Empty(t, ms.GetEnvironmentOverrides())
  
  	os.Setenv("MM_SERVICESETTINGS_SITEURL", "http://override")
  	defer os.Unsetenv("MM_SERVICESETTINGS_SITEURL")
  
  	ms, err = config.NewMemoryStore()
  	require.NoError(t, err)
  	defer ms.Close()
  
  	assert.Equal(t, "http://override", *ms.Get().ServiceSettings.SiteURL)
  	assert.Equal(t, map[string]interface{}{"ServiceSettings": map[string]interface{}{"SiteURL": true}}, ms.GetEnvironmentOverrides())
  }
  
  func TestMemoryStoreSet(t *testing.T) {
  	t.Run("set same pointer value", func(t *testing.T) {
  		t.Skip("not yet implemented")
  
  		setupConfigMemory(t)
  
  		ms, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{InitialConfig: emptyConfig})
  		require.NoError(t, err)
  		defer ms.Close()
  
  		_, err = ms.Set(ms.Get())
  		if assert.Error(t, err) {
  			assert.EqualError(t, err, "old configuration modified instead of cloning")
  		}
  	})
  
  	t.Run("defaults required", func(t *testing.T) {
  		setupConfigMemory(t)
  
  		ms, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{InitialConfig: minimalConfig})
  		require.NoError(t, err)
  		defer ms.Close()
  
  		oldCfg := ms.Get()
  
  		newCfg := &model.Config{}
  
  		retCfg, err := ms.Set(newCfg)
  		require.NoError(t, err)
  		assert.Equal(t, oldCfg, retCfg)
  
  		assert.Equal(t, "", *ms.Get().ServiceSettings.SiteURL)
  	})
  
  	t.Run("desanitization required", func(t *testing.T) {
  		setupConfigMemory(t)
  
  		ms, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{InitialConfig: ldapConfig})
  		require.NoError(t, err)
  		defer ms.Close()
  
  		oldCfg := ms.Get()
  
  		newCfg := &model.Config{}
  		newCfg.LdapSettings.BindPassword = sToP(model.FAKE_SETTING)
  
  		retCfg, err := ms.Set(newCfg)
  		require.NoError(t, err)
  		assert.Equal(t, oldCfg, retCfg)
  
  		assert.Equal(t, "password", *ms.Get().LdapSettings.BindPassword)
  	})
  
  	t.Run("invalid", func(t *testing.T) {
  		setupConfigMemory(t)
  
  		ms, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{InitialConfig: emptyConfig})
  		require.NoError(t, err)
  		defer ms.Close()
  
  		newCfg := &model.Config{}
  		newCfg.ServiceSettings.SiteURL = sToP("invalid")
  
  		_, err = ms.Set(newCfg)
  		if assert.Error(t, err) {
  			assert.EqualError(t, err, "new configuration is invalid: Config.IsValid: model.config.is_valid.site_url.app_error, ")
  		}
  
  		assert.Equal(t, "", *ms.Get().ServiceSettings.SiteURL)
  	})
  
  	t.Run("read-only ignored", func(t *testing.T) {
  		setupConfigMemory(t)
  
  		ms, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{InitialConfig: readOnlyConfig})
  		require.NoError(t, err)
  		defer ms.Close()
  
  		newCfg := &model.Config{
  			ServiceSettings: model.ServiceSettings{
  				SiteURL: sToP("http://new"),
  			},
  		}
  
  		_, err = ms.Set(newCfg)
  		require.NoError(t, err)
  
  		assert.Equal(t, "http://new", *ms.Get().ServiceSettings.SiteURL)
  	})
  
  	t.Run("listeners notified", func(t *testing.T) {
  		setupConfigMemory(t)
  
  		ms, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{InitialConfig: emptyConfig})
  		require.NoError(t, err)
  		defer ms.Close()
  
  		oldCfg := ms.Get()
  
  		called := make(chan bool, 1)
  		callback := func(oldfg, newCfg *model.Config) {
  			called <- true
  		}
  		ms.AddListener(callback)
  
  		newCfg := &model.Config{}
  
  		retCfg, err := ms.Set(newCfg)
  		require.NoError(t, err)
  		assert.Equal(t, oldCfg, retCfg)
  
  		require.True(t, wasCalled(called, 5*time.Second), "callback should have been called when config written")
  	})
  }
  
  func TestMemoryStoreLoad(t *testing.T) {
  	t.Run("honour environment", func(t *testing.T) {
  		setupConfigMemory(t)
  
  		ms, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{InitialConfig: minimalConfig})
  		require.NoError(t, err)
  		defer ms.Close()
  
  		os.Setenv("MM_SERVICESETTINGS_SITEURL", "http://override")
  		defer os.Unsetenv("MM_SERVICESETTINGS_SITEURL")
  
  		err = ms.Load()
  		require.NoError(t, err)
  		assert.Equal(t, "http://override", *ms.Get().ServiceSettings.SiteURL)
  		assert.Equal(t, map[string]interface{}{"ServiceSettings": map[string]interface{}{"SiteURL": true}}, ms.GetEnvironmentOverrides())
  	})
  
  	t.Run("fixes required", func(t *testing.T) {
  		setupConfigMemory(t)
  
  		ms, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{InitialConfig: fixesRequiredConfig})
  		require.NoError(t, err)
  		defer ms.Close()
  
  		err = ms.Load()
  		require.NoError(t, err)
  		assert.Equal(t, "http://trailingslash", *ms.Get().ServiceSettings.SiteURL)
  	})
  
  	t.Run("listeners notifed", func(t *testing.T) {
  		setupConfigMemory(t)
  
  		ms, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{InitialConfig: emptyConfig})
  		require.NoError(t, err)
  		defer ms.Close()
  
  		called := make(chan bool, 1)
  		callback := func(oldfg, newCfg *model.Config) {
  			called <- true
  		}
  		ms.AddListener(callback)
  
  		err = ms.Load()
  		require.NoError(t, err)
  
  		require.True(t, wasCalled(called, 5*time.Second), "callback should have been called when config loaded")
  	})
  }
  
  func TestMemoryGetFile(t *testing.T) {
  	setupConfigMemory(t)
  
  	ms, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{
  		InitialConfig: minimalConfig,
  		InitialFiles: map[string][]byte{
  			"empty-file": {},
  			"test-file":  []byte("test"),
  		},
  	})
  	require.NoError(t, err)
  	defer ms.Close()
  
  	t.Run("get empty filename", func(t *testing.T) {
  		_, err := ms.GetFile("")
  		require.Error(t, err)
  	})
  
  	t.Run("get non-existent file", func(t *testing.T) {
  		_, err := ms.GetFile("unknown")
  		require.Error(t, err)
  	})
  
  	t.Run("get empty file", func(t *testing.T) {
  		data, err := ms.GetFile("empty-file")
  		require.NoError(t, err)
  		require.Empty(t, data)
  	})
  
  	t.Run("get non-empty file", func(t *testing.T) {
  		data, err := ms.GetFile("test-file")
  		require.NoError(t, err)
  		require.Equal(t, []byte("test"), data)
  	})
  }
  
  func TestMemorySetFile(t *testing.T) {
  	setupConfigMemory(t)
  
  	ms, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{
  		InitialConfig: minimalConfig,
  	})
  	require.NoError(t, err)
  	defer ms.Close()
  
  	t.Run("set new file", func(t *testing.T) {
  		err := ms.SetFile("new", []byte("new file"))
  		require.NoError(t, err)
  
  		data, err := ms.GetFile("new")
  		require.NoError(t, err)
  		require.Equal(t, []byte("new file"), data)
  	})
  
  	t.Run("overwrite existing file", func(t *testing.T) {
  		err := ms.SetFile("existing", []byte("existing file"))
  		require.NoError(t, err)
  
  		err = ms.SetFile("existing", []byte("overwritten file"))
  		require.NoError(t, err)
  
  		data, err := ms.GetFile("existing")
  		require.NoError(t, err)
  		require.Equal(t, []byte("overwritten file"), data)
  	})
  }
  
  func TestMemoryHasFile(t *testing.T) {
  	t.Run("has non-existent", func(t *testing.T) {
  		setupConfigMemory(t)
  
  		ms, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{
  			InitialConfig: minimalConfig,
  		})
  		require.NoError(t, err)
  		defer ms.Close()
  
  		has, err := ms.HasFile("non-existent")
  		require.NoError(t, err)
  		require.False(t, has)
  	})
  
  	t.Run("has existing", func(t *testing.T) {
  		setupConfigMemory(t)
  
  		ms, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{
  			InitialConfig: minimalConfig,
  		})
  		require.NoError(t, err)
  		defer ms.Close()
  
  		err = ms.SetFile("existing", []byte("existing file"))
  		require.NoError(t, err)
  
  		has, err := ms.HasFile("existing")
  		require.NoError(t, err)
  		require.True(t, has)
  	})
  
  	t.Run("has manually created file", func(t *testing.T) {
  		setupConfigMemory(t)
  
  		ms, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{
  			InitialConfig: minimalConfig,
  			InitialFiles: map[string][]byte{
  				"manual": []byte("manual file"),
  			},
  		})
  		require.NoError(t, err)
  		defer ms.Close()
  
  		has, err := ms.HasFile("manual")
  		require.NoError(t, err)
  		require.True(t, has)
  	})
  }
  
  func TestMemoryRemoveFile(t *testing.T) {
  	t.Run("remove non-existent", func(t *testing.T) {
  		setupConfigMemory(t)
  
  		ms, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{
  			InitialConfig: minimalConfig,
  		})
  		require.NoError(t, err)
  		defer ms.Close()
  
  		err = ms.RemoveFile("non-existent")
  		require.NoError(t, err)
  	})
  
  	t.Run("remove existing", func(t *testing.T) {
  		setupConfigMemory(t)
  
  		ms, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{
  			InitialConfig: minimalConfig,
  		})
  		require.NoError(t, err)
  		defer ms.Close()
  
  		err = ms.SetFile("existing", []byte("existing file"))
  		require.NoError(t, err)
  
  		err = ms.RemoveFile("existing")
  		require.NoError(t, err)
  
  		has, err := ms.HasFile("existing")
  		require.NoError(t, err)
  		require.False(t, has)
  
  		_, err = ms.GetFile("existing")
  		require.Error(t, err)
  	})
  
  	t.Run("remove manually created file", func(t *testing.T) {
  		setupConfigMemory(t)
  
  		ms, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{
  			InitialConfig: minimalConfig,
  			InitialFiles: map[string][]byte{
  				"manual": []byte("manual file"),
  			},
  		})
  		require.NoError(t, err)
  		defer ms.Close()
  
  		err = ms.RemoveFile("manual")
  		require.NoError(t, err)
  
  		has, err := ms.HasFile("manual")
  		require.NoError(t, err)
  		require.False(t, has)
  
  		_, err = ms.GetFile("manual")
  		require.Error(t, err)
  	})
  }
  
  func TestMemoryStoreString(t *testing.T) {
  	setupConfigMemory(t)
  
  	ms, err := config.NewMemoryStoreWithOptions(&config.MemoryStoreOptions{InitialConfig: emptyConfig})
  	require.NoError(t, err)
  	defer ms.Close()
  
  	assert.Equal(t, "memory://", ms.String())
  }
  