github.com/mattermosttest/mattermost-server/v5@v5.0.0-20200917143240-9dfa12e121f9/cmd/mattermost/commands/config_test.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package commands
     5  
     6  import (
     7  	"encoding/json"
     8  	"io/ioutil"
     9  	"os"
    10  	"reflect"
    11  	"sort"
    12  	"strings"
    13  	"testing"
    14  
    15  	"github.com/stretchr/testify/assert"
    16  	"github.com/stretchr/testify/require"
    17  
    18  	"github.com/mattermost/mattermost-server/v5/config"
    19  	"github.com/mattermost/mattermost-server/v5/model"
    20  )
    21  
    22  type TestConfig struct {
    23  	TestServiceSettings       TestServiceSettings
    24  	TestTeamSettings          TestTeamSettings
    25  	TestClientRequirements    TestClientRequirements
    26  	TestMessageExportSettings TestMessageExportSettings
    27  }
    28  
    29  type TestMessageExportSettings struct {
    30  	Enableexport            bool
    31  	Exportformat            string
    32  	TestGlobalRelaySettings TestGlobalRelaySettings
    33  }
    34  
    35  type TestGlobalRelaySettings struct {
    36  	Customertype string
    37  	Smtpusername string
    38  	Smtppassword string
    39  }
    40  
    41  type TestServiceSettings struct {
    42  	Siteurl               string
    43  	Websocketurl          string
    44  	Licensedfieldlocation string
    45  }
    46  
    47  type TestTeamSettings struct {
    48  	Sitename       string
    49  	Maxuserperteam int
    50  }
    51  
    52  type TestClientRequirements struct {
    53  	Androidlatestversion string
    54  	Androidminversion    string
    55  	Desktoplatestversion string
    56  }
    57  
    58  type TestNewConfig struct {
    59  	TestNewServiceSettings TestNewServiceSettings
    60  	TestNewTeamSettings    TestNewTeamSettings
    61  }
    62  
    63  type TestNewServiceSettings struct {
    64  	SiteUrl                  *string
    65  	UseLetsEncrypt           *bool
    66  	TLSStrictTransportMaxAge *int64
    67  	AllowedThemes            []string
    68  }
    69  
    70  type TestNewTeamSettings struct {
    71  	SiteName       *string
    72  	MaxUserPerTeam *int
    73  }
    74  
    75  type TestPluginSettings struct {
    76  	Enable                  *bool
    77  	Directory               *string `restricted:"true"`
    78  	Plugins                 map[string]map[string]interface{}
    79  	PluginStates            map[string]*model.PluginState
    80  	SignaturePublicKeyFiles []string
    81  }
    82  
    83  func getDsn(driver string, source string) string {
    84  	if driver == model.DATABASE_DRIVER_MYSQL {
    85  		return driver + "://" + source
    86  	}
    87  	return source
    88  }
    89  
    90  func TestConfigValidate(t *testing.T) {
    91  	th := Setup(t)
    92  	defer th.TearDown()
    93  
    94  	tempFile, err := ioutil.TempFile("", "TestConfigValidate")
    95  	require.NoError(t, err)
    96  	defer os.Remove(tempFile.Name())
    97  
    98  	assert.Error(t, th.RunCommand(t, "--config", tempFile.Name(), "config", "validate"))
    99  	th.CheckCommand(t, "config", "validate")
   100  }
   101  
   102  func TestConfigGet(t *testing.T) {
   103  	th := Setup(t)
   104  	defer th.TearDown()
   105  
   106  	t.Run("Error when no arguments are given", func(t *testing.T) {
   107  		assert.Error(t, th.RunCommand(t, "config", "get"))
   108  	})
   109  
   110  	t.Run("Error when more than one config settings are given", func(t *testing.T) {
   111  		assert.Error(t, th.RunCommand(t, "config", "get", "abc", "def"))
   112  	})
   113  
   114  	t.Run("Error when a config setting which is not in the config.json is given", func(t *testing.T) {
   115  		assert.Error(t, th.RunCommand(t, "config", "get", "abc"))
   116  	})
   117  
   118  	t.Run("No Error when a config setting which is in the config.json is given", func(t *testing.T) {
   119  		th.CheckCommand(t, "config", "get", "MessageExportSettings")
   120  		th.CheckCommand(t, "config", "get", "MessageExportSettings.GlobalRelaySettings")
   121  		th.CheckCommand(t, "config", "get", "MessageExportSettings.GlobalRelaySettings.CustomerType")
   122  	})
   123  
   124  	t.Run("check output", func(t *testing.T) {
   125  		output := th.CheckCommand(t, "config", "get", "MessageExportSettings")
   126  
   127  		assert.Contains(t, output, "EnableExport")
   128  		assert.Contains(t, output, "ExportFormat")
   129  		assert.Contains(t, output, "DailyRunTime")
   130  		assert.Contains(t, output, "ExportFromTimestamp")
   131  	})
   132  }
   133  
   134  func TestConfigSet(t *testing.T) {
   135  	th := Setup(t)
   136  	defer th.TearDown()
   137  
   138  	t.Run("Error when no arguments are given", func(t *testing.T) {
   139  		assert.Error(t, th.RunCommand(t, "config", "set"))
   140  	})
   141  
   142  	t.Run("Error when only one argument is given", func(t *testing.T) {
   143  		assert.Error(t, th.RunCommand(t, "config", "set", "test"))
   144  	})
   145  
   146  	t.Run("Error when the wrong key is set", func(t *testing.T) {
   147  		assert.Error(t, th.RunCommand(t, "config", "set", "invalid-key", "value"))
   148  		assert.Error(t, th.RunCommand(t, "config", "get", "invalid-key"))
   149  	})
   150  
   151  	t.Run("Error when the wrong value is set", func(t *testing.T) {
   152  		assert.Error(t, th.RunCommand(t, "config", "set", "EmailSettings.ConnectionSecurity", "invalid-key"))
   153  		output := th.CheckCommand(t, "config", "get", "EmailSettings.ConnectionSecurity")
   154  		assert.NotContains(t, output, "invalid-key")
   155  	})
   156  
   157  	t.Run("Error when the parameter of an unknown plugin is set", func(t *testing.T) {
   158  		output, err := th.RunCommandWithOutput(t, "config", "set", "PluginSettings.Plugins.someplugin", "true")
   159  		assert.Error(t, err)
   160  		assert.NotContains(t, output, "panic")
   161  	})
   162  
   163  	t.Run("Error when the wrong locale is set", func(t *testing.T) {
   164  		th.CheckCommand(t, "config", "set", "LocalizationSettings.DefaultServerLocale", "es")
   165  		assert.Error(t, th.RunCommand(t, "config", "set", "LocalizationSettings.DefaultServerLocale", "invalid-key"))
   166  		output := th.CheckCommand(t, "config", "get", "LocalizationSettings.DefaultServerLocale")
   167  		assert.NotContains(t, output, "invalid-key")
   168  		assert.NotContains(t, output, "\"en\"")
   169  	})
   170  
   171  	t.Run("Success when a valid value is set", func(t *testing.T) {
   172  		assert.NoError(t, th.RunCommand(t, "config", "set", "EmailSettings.ConnectionSecurity", "TLS"))
   173  		output := th.CheckCommand(t, "config", "get", "EmailSettings.ConnectionSecurity")
   174  		assert.Contains(t, output, "TLS")
   175  	})
   176  
   177  	t.Run("Success when a valid locale is set", func(t *testing.T) {
   178  		assert.NoError(t, th.RunCommand(t, "config", "set", "LocalizationSettings.DefaultServerLocale", "es"))
   179  		output := th.CheckCommand(t, "config", "get", "LocalizationSettings.DefaultServerLocale")
   180  		assert.Contains(t, output, "\"es\"")
   181  	})
   182  }
   183  
   184  func TestConfigReset(t *testing.T) {
   185  	th := Setup(t)
   186  	defer th.TearDown()
   187  
   188  	t.Run("No Error when no arguments are given (reset all the configurations)", func(t *testing.T) {
   189  		assert.NoError(t, th.RunCommand(t, "config", "reset"))
   190  	})
   191  
   192  	t.Run("No Error when a configuration section is given", func(t *testing.T) {
   193  		assert.NoError(t, th.RunCommand(t, "config", "reset", "JobSettings"))
   194  	})
   195  
   196  	t.Run("No Error when a configuration setting is given", func(t *testing.T) {
   197  		assert.NoError(t, th.RunCommand(t, "config", "reset", "JobSettings.RunJobs"))
   198  	})
   199  
   200  	t.Run("Error when the wrong configuration section is given", func(t *testing.T) {
   201  		assert.Error(t, th.RunCommand(t, "config", "reset", "InvalidSettings"))
   202  	})
   203  
   204  	t.Run("Error when the wrong configuration setting is given", func(t *testing.T) {
   205  		assert.Error(t, th.RunCommand(t, "config", "reset", "JobSettings.InvalidConfiguration"))
   206  	})
   207  
   208  	t.Run("Success when the confirm boolean flag is given", func(t *testing.T) {
   209  		assert.NoError(t, th.RunCommand(t, "config", "set", "JobSettings.RunJobs", "false"))
   210  		assert.NoError(t, th.RunCommand(t, "config", "set", "PrivacySettings.ShowFullName", "false"))
   211  		assert.NoError(t, th.RunCommand(t, "config", "reset", "--confirm"))
   212  		output1 := th.CheckCommand(t, "config", "get", "JobSettings.RunJobs")
   213  		output2 := th.CheckCommand(t, "config", "get", "PrivacySettings.ShowFullName")
   214  		assert.Contains(t, output1, "true")
   215  		assert.Contains(t, output2, "true")
   216  	})
   217  
   218  	t.Run("Success when a configuration section is given", func(t *testing.T) {
   219  		assert.NoError(t, th.RunCommand(t, "config", "set", "JobSettings.RunJobs", "false"))
   220  		assert.NoError(t, th.RunCommand(t, "config", "set", "JobSettings.RunScheduler", "false"))
   221  		assert.NoError(t, th.RunCommand(t, "config", "set", "PrivacySettings.ShowFullName", "false"))
   222  		assert.NoError(t, th.RunCommand(t, "config", "reset", "JobSettings"))
   223  		output1 := th.CheckCommand(t, "config", "get", "JobSettings.RunJobs")
   224  		output2 := th.CheckCommand(t, "config", "get", "JobSettings.RunScheduler")
   225  		output3 := th.CheckCommand(t, "config", "get", "PrivacySettings.ShowFullName")
   226  		assert.Contains(t, output1, "true")
   227  		assert.Contains(t, output2, "true")
   228  		assert.Contains(t, output3, "false")
   229  	})
   230  
   231  	t.Run("Success when a configuration setting is given", func(t *testing.T) {
   232  		assert.NoError(t, th.RunCommand(t, "config", "set", "JobSettings.RunJobs", "false"))
   233  		assert.NoError(t, th.RunCommand(t, "config", "set", "JobSettings.RunScheduler", "false"))
   234  		assert.NoError(t, th.RunCommand(t, "config", "reset", "JobSettings.RunJobs"))
   235  		output1 := th.CheckCommand(t, "config", "get", "JobSettings.RunJobs")
   236  		output2 := th.CheckCommand(t, "config", "get", "JobSettings.RunScheduler")
   237  		assert.Contains(t, output1, "true")
   238  		assert.Contains(t, output2, "false")
   239  	})
   240  }
   241  
   242  func TestConfigToMap(t *testing.T) {
   243  	// This test is almost the same as TestStructToMap, but I have it here for the sake of completions
   244  	cases := []struct {
   245  		Name     string
   246  		Input    interface{}
   247  		Expected map[string]interface{}
   248  	}{
   249  		{
   250  			Name: "Struct with one string field",
   251  			Input: struct {
   252  				Test string
   253  			}{
   254  				Test: "test",
   255  			},
   256  			Expected: map[string]interface{}{
   257  				"Test": "test",
   258  			},
   259  		},
   260  		{
   261  			Name: "String with multiple fields of different ",
   262  			Input: struct {
   263  				Test1 string
   264  				Test2 int
   265  				Test3 string
   266  				Test4 bool
   267  			}{
   268  				Test1: "test1",
   269  				Test2: 21,
   270  				Test3: "test2",
   271  				Test4: false,
   272  			},
   273  			Expected: map[string]interface{}{
   274  				"Test1": "test1",
   275  				"Test2": 21,
   276  				"Test3": "test2",
   277  				"Test4": false,
   278  			},
   279  		},
   280  		{
   281  			Name: "Nested fields",
   282  			Input: TestConfig{
   283  				TestServiceSettings{"abc", "def", "ghi"},
   284  				TestTeamSettings{"abc", 1},
   285  				TestClientRequirements{"abc", "def", "ghi"},
   286  				TestMessageExportSettings{true, "abc", TestGlobalRelaySettings{"abc", "def", "ghi"}},
   287  			},
   288  			Expected: map[string]interface{}{
   289  				"TestServiceSettings": map[string]interface{}{
   290  					"Siteurl":               "abc",
   291  					"Websocketurl":          "def",
   292  					"Licensedfieldlocation": "ghi",
   293  				},
   294  				"TestTeamSettings": map[string]interface{}{
   295  					"Sitename":       "abc",
   296  					"Maxuserperteam": 1,
   297  				},
   298  				"TestClientRequirements": map[string]interface{}{
   299  					"Androidlatestversion": "abc",
   300  					"Androidminversion":    "def",
   301  					"Desktoplatestversion": "ghi",
   302  				},
   303  				"TestMessageExportSettings": map[string]interface{}{
   304  					"Enableexport": true,
   305  					"Exportformat": "abc",
   306  					"TestGlobalRelaySettings": map[string]interface{}{
   307  						"Customertype": "abc",
   308  						"Smtpusername": "def",
   309  						"Smtppassword": "ghi",
   310  					},
   311  				},
   312  			},
   313  		},
   314  	}
   315  
   316  	for _, test := range cases {
   317  		t.Run(test.Name, func(t *testing.T) {
   318  			res := configToMap(test.Input)
   319  
   320  			if !reflect.DeepEqual(res, test.Expected) {
   321  				t.Errorf("got %v want %v ", res, test.Expected)
   322  			}
   323  		})
   324  	}
   325  }
   326  
   327  func TestPrintConfigValues(t *testing.T) {
   328  	outputs := []string{
   329  		"Siteurl: \"abc\"\nWebsocketurl: \"def\"\nLicensedfieldlocation: \"ghi\"\n",
   330  		"Sitename: \"abc\"\nMaxuserperteam: \"1\"\n",
   331  		"Androidlatestversion: \"abc\"\nAndroidminversion: \"def\"\nDesktoplatestversion: \"ghi\"\n",
   332  		"Enableexport: \"true\"\nExportformat: \"abc\"\nTestGlobalRelaySettings:\n\tCustomertype: \"abc\"\n\tSmtpusername: \"def\"\n\tSmtppassword: \"ghi\"\n",
   333  		"Customertype: \"abc\"\nSmtpusername: \"def\"\nSmtppassword: \"ghi\"\n",
   334  	}
   335  
   336  	commands := []string{
   337  		"TestServiceSettings",
   338  		"TestTeamSettings",
   339  		"TestClientRequirements",
   340  		"TestMessageExportSettings",
   341  		"TestMessageExportSettings.TestGlobalRelaySettings",
   342  	}
   343  
   344  	input := TestConfig{
   345  		TestServiceSettings{"abc", "def", "ghi"},
   346  		TestTeamSettings{"abc", 1},
   347  		TestClientRequirements{"abc", "def", "ghi"},
   348  		TestMessageExportSettings{true, "abc", TestGlobalRelaySettings{"abc", "def", "ghi"}},
   349  	}
   350  
   351  	configMap := structToMap(input)
   352  
   353  	cases := []struct {
   354  		Name     string
   355  		Command  string
   356  		Expected string
   357  	}{
   358  		{
   359  			Name:     "First test",
   360  			Command:  commands[0],
   361  			Expected: outputs[0],
   362  		},
   363  		{
   364  			Name:     "Second test",
   365  			Command:  commands[1],
   366  			Expected: outputs[1],
   367  		},
   368  		{
   369  			Name:     "third test",
   370  			Command:  commands[2],
   371  			Expected: outputs[2],
   372  		},
   373  		{
   374  			Name:     "fourth test",
   375  			Command:  commands[3],
   376  			Expected: outputs[3],
   377  		},
   378  		{
   379  			Name:     "fifth test",
   380  			Command:  commands[4],
   381  			Expected: outputs[4],
   382  		},
   383  	}
   384  
   385  	for _, test := range cases {
   386  		t.Run(test.Name, func(t *testing.T) {
   387  			res, _ := printConfigValues(configMap, strings.Split(test.Command, "."), test.Command)
   388  
   389  			// create two slice of string formed by splitting our strings on \n
   390  			slice1 := strings.Split(res, "\n")
   391  			slice2 := strings.Split(test.Expected, "\n")
   392  
   393  			sort.Strings(slice1)
   394  			sort.Strings(slice2)
   395  
   396  			if !reflect.DeepEqual(slice1, slice2) {
   397  				t.Errorf("got '%#v' want '%#v", slice1, slice2)
   398  			}
   399  		})
   400  	}
   401  }
   402  
   403  func TestConfigShow(t *testing.T) {
   404  	th := Setup(t)
   405  	defer th.TearDown()
   406  
   407  	t.Run("error with unknown subcommand", func(t *testing.T) {
   408  		assert.Error(t, th.RunCommand(t, "config", "show", "abc"))
   409  	})
   410  
   411  	t.Run("successfully dumping config", func(t *testing.T) {
   412  		output := th.CheckCommand(t, "config", "show")
   413  		assert.Contains(t, output, "SqlSettings")
   414  		assert.Contains(t, output, "MessageExportSettings")
   415  		assert.Contains(t, output, "AnnouncementSettings")
   416  	})
   417  
   418  	t.Run("successfully dumping config as json", func(t *testing.T) {
   419  		output, err := th.RunCommandWithOutput(t, "config", "show", "--json")
   420  		require.Nil(t, err)
   421  
   422  		// Filter out the test headers
   423  		var filteredOutput []string
   424  		for _, line := range strings.Split(output, "\n") {
   425  			if strings.HasPrefix(line, "---") || strings.HasPrefix(line, "===") || strings.HasPrefix(line, "PASS") || strings.HasPrefix(line, "coverage:") {
   426  				continue
   427  			}
   428  
   429  			filteredOutput = append(filteredOutput, line)
   430  		}
   431  
   432  		output = strings.Join(filteredOutput, "")
   433  
   434  		var config model.Config
   435  		err = json.Unmarshal([]byte(output), &config)
   436  		require.Nil(t, err)
   437  	})
   438  }
   439  
   440  func TestSetConfig(t *testing.T) {
   441  	th := Setup(t)
   442  	defer th.TearDown()
   443  
   444  	// Error when no argument is given
   445  	assert.Error(t, th.RunCommand(t, "config", "set"))
   446  
   447  	// No Error when more than one argument is given
   448  	th.CheckCommand(t, "config", "set", "ThemeSettings.AllowedThemes", "hello", "World")
   449  
   450  	// No Error when two arguments are given
   451  	th.CheckCommand(t, "config", "set", "ThemeSettings.AllowedThemes", "hello")
   452  
   453  	// Error when only one argument is given
   454  	assert.Error(t, th.RunCommand(t, "config", "set", "ThemeSettings.AllowedThemes"))
   455  
   456  	// Error when config settings not in the config file are given
   457  	assert.Error(t, th.RunCommand(t, "config", "set", "Abc"))
   458  }
   459  
   460  func TestUpdateMap(t *testing.T) {
   461  	// create a config to make changes
   462  	config := TestNewConfig{
   463  		TestNewServiceSettings{
   464  			SiteUrl:                  model.NewString("abc.def"),
   465  			UseLetsEncrypt:           model.NewBool(false),
   466  			TLSStrictTransportMaxAge: model.NewInt64(36),
   467  			AllowedThemes:            []string{"Hello", "World"},
   468  		},
   469  		TestNewTeamSettings{
   470  			SiteName:       model.NewString("def.ghi"),
   471  			MaxUserPerTeam: model.NewInt(12),
   472  		},
   473  	}
   474  
   475  	// create a map of type map[string]interface
   476  	configMap := configToMap(config)
   477  
   478  	cases := []struct {
   479  		Name           string
   480  		configSettings []string
   481  		newVal         []string
   482  		expected       interface{}
   483  	}{
   484  		{
   485  			Name:           "check for Map and string",
   486  			configSettings: []string{"TestNewServiceSettings", "SiteUrl"},
   487  			newVal:         []string{"siteurl"},
   488  			expected:       "siteurl",
   489  		},
   490  		{
   491  			Name:           "check for Map and bool",
   492  			configSettings: []string{"TestNewServiceSettings", "UseLetsEncrypt"},
   493  			newVal:         []string{"true"},
   494  			expected:       true,
   495  		},
   496  		{
   497  			Name:           "check for Map and int64",
   498  			configSettings: []string{"TestNewServiceSettings", "TLSStrictTransportMaxAge"},
   499  			newVal:         []string{"56"},
   500  			expected:       int64(56),
   501  		},
   502  		{
   503  			Name:           "check for Map and string Slice",
   504  			configSettings: []string{"TestNewServiceSettings", "AllowedThemes"},
   505  			newVal:         []string{"hello1", "world1"},
   506  			expected:       []string{"hello1", "world1"},
   507  		},
   508  		{
   509  			Name:           "Map and string",
   510  			configSettings: []string{"TestNewTeamSettings", "SiteName"},
   511  			newVal:         []string{"jkl.mno"},
   512  			expected:       "jkl.mno",
   513  		},
   514  		{
   515  			Name:           "Map and int",
   516  			configSettings: []string{"TestNewTeamSettings", "MaxUserPerTeam"},
   517  			newVal:         []string{"18"},
   518  			expected:       18,
   519  		},
   520  	}
   521  
   522  	for _, test := range cases {
   523  
   524  		t.Run(test.Name, func(t *testing.T) {
   525  			err := UpdateMap(configMap, test.configSettings, test.newVal)
   526  
   527  			require.Nil(t, err, "Wasn't expecting an error")
   528  
   529  			if !contains(configMap, test.expected, test.configSettings) {
   530  				t.Error("update didn't happen")
   531  			}
   532  
   533  		})
   534  	}
   535  }
   536  
   537  func TestConfigMigrate(t *testing.T) {
   538  	th := Setup(t)
   539  	defer th.TearDown()
   540  
   541  	sqlSettings := mainHelper.GetSQLSettings()
   542  	sqlDSN := getDsn(*sqlSettings.DriverName, *sqlSettings.DataSource)
   543  	fileDSN := "config.json"
   544  
   545  	ds, err := config.NewStore(sqlDSN, false)
   546  	require.NoError(t, err)
   547  	fs, err := config.NewStore(fileDSN, false)
   548  	require.NoError(t, err)
   549  
   550  	defer ds.Close()
   551  	defer fs.Close()
   552  
   553  	t.Run("Should error with too few parameters", func(t *testing.T) {
   554  		assert.Error(t, th.RunCommand(t, "config", "migrate", fileDSN))
   555  	})
   556  
   557  	t.Run("Should error with too many parameters", func(t *testing.T) {
   558  		assert.Error(t, th.RunCommand(t, "config", "migrate", fileDSN, sqlDSN, "reallyfast"))
   559  	})
   560  
   561  	t.Run("Should work passing two parameters", func(t *testing.T) {
   562  		assert.NoError(t, th.RunCommand(t, "config", "migrate", fileDSN, sqlDSN))
   563  	})
   564  
   565  	t.Run("Should fail passing an invalid target", func(t *testing.T) {
   566  		assert.Error(t, th.RunCommand(t, "config", "migrate", fileDSN, "mysql://asd"))
   567  	})
   568  
   569  	t.Run("Should fail passing an invalid source", func(t *testing.T) {
   570  		assert.Error(t, th.RunCommand(t, "config", "migrate", "invalid/path", sqlDSN))
   571  	})
   572  }
   573  
   574  func contains(configMap map[string]interface{}, v interface{}, configSettings []string) bool {
   575  	res := configMap[configSettings[0]]
   576  
   577  	value := reflect.ValueOf(res)
   578  
   579  	switch value.Kind() {
   580  	case reflect.Map:
   581  		return contains(res.(map[string]interface{}), v, configSettings[1:])
   582  	case reflect.Slice:
   583  		return reflect.DeepEqual(value.Interface(), v)
   584  	case reflect.Int64:
   585  		return value.Interface() == v.(int64)
   586  	default:
   587  		return value.Interface() == v
   588  	}
   589  }
   590  
   591  func TestPluginConfigs(t *testing.T) {
   592  	pluginConfig := TestPluginSettings{
   593  		Enable:    model.NewBool(true),
   594  		Directory: model.NewString("dir"),
   595  		Plugins: map[string]map[string]interface{}{
   596  			"antivirus": {
   597  				"clamavhostport":     "localhost:3310",
   598  				"scantimeoutseconds": 12,
   599  			},
   600  			"com.mattermost.demo-plugin": {
   601  				"channelname":       "demo_plugin",
   602  				"customsetting":     "7",
   603  				"enablementionuser": false,
   604  				"lastname":          "Plugin User",
   605  				"mentionuser":       "demo_plugin",
   606  				"randomsecret":      "random secret",
   607  				"secretmessage":     "Changed value.",
   608  				"textstyle":         "",
   609  				"username":          "demo_plugin",
   610  			},
   611  			"com.mattermost.webex": {
   612  				"sitehost": "praptishrestha.my.webex.com",
   613  			},
   614  			"jira": {
   615  				"enablejiraui":                         true,
   616  				"groupsallowedtoeditjirasubscriptions": "",
   617  				"rolesallowedtoeditjirasubscriptions":  "system_admin",
   618  				"secret":                               "some secret",
   619  			},
   620  			"mattermost-autolink": {
   621  				"enableadmincommand": false,
   622  			},
   623  		},
   624  		PluginStates: map[string]*model.PluginState{
   625  			"antivirus": {
   626  				Enable: false,
   627  			},
   628  			"com.github.manland.mattermost-plugin-gitlab": {
   629  				Enable: true,
   630  			},
   631  		},
   632  		SignaturePublicKeyFiles: []string{"Hello", "World"},
   633  	}
   634  
   635  	configMap := configToMap(pluginConfig)
   636  	err := UpdateMap(configMap, []string{"Enable"}, []string{"false"})
   637  	require.Nil(t, err, "Wasn't expecting an error")
   638  	assert.Equal(t, false, configMap["Enable"].(bool))
   639  
   640  	err = UpdateMap(configMap, []string{"Plugins", "antivirus", "clamavhostport"}, []string{"some text"})
   641  	require.Nil(t, err, "Wasn't expecting an error")
   642  	assert.Equal(t, "some text", configMap["Plugins"].(map[string]map[string]interface{})["antivirus"]["clamavhostport"].(string))
   643  
   644  	err = UpdateMap(configMap, []string{"Plugins", "mattermost-autolink", "enableadmincommand"}, []string{"true"})
   645  	require.Nil(t, err, "Wasn't expecting an error")
   646  	assert.Equal(t, true, configMap["Plugins"].(map[string]map[string]interface{})["mattermost-autolink"]["enableadmincommand"].(bool))
   647  
   648  	err = UpdateMap(configMap, []string{"PluginStates", "antivirus", "Enable"}, []string{"true"})
   649  	require.Nil(t, err, "Wasn't expecting an error")
   650  	assert.Equal(t, true, configMap["PluginStates"].(map[string]*model.PluginState)["antivirus"].Enable)
   651  }