github.com/iDevoid/mattermost-server@v5.11.1+incompatible/cmd/mattermost/commands/config_test.go (about)

     1  // Copyright (c) 2017-present Mattermost, Inc. All Rights Reserved.
     2  // See License.txt for license information.
     3  
     4  package commands
     5  
     6  import (
     7  	"io/ioutil"
     8  	"os"
     9  	"reflect"
    10  	"sort"
    11  	"strings"
    12  	"testing"
    13  
    14  	"github.com/stretchr/testify/assert"
    15  	"github.com/stretchr/testify/require"
    16  
    17  	"github.com/mattermost/mattermost-server/model"
    18  )
    19  
    20  type TestConfig struct {
    21  	TestServiceSettings       TestServiceSettings
    22  	TestTeamSettings          TestTeamSettings
    23  	TestClientRequirements    TestClientRequirements
    24  	TestMessageExportSettings TestMessageExportSettings
    25  }
    26  
    27  type TestMessageExportSettings struct {
    28  	Enableexport            bool
    29  	Exportformat            string
    30  	TestGlobalRelaySettings TestGlobalRelaySettings
    31  }
    32  
    33  type TestGlobalRelaySettings struct {
    34  	Customertype string
    35  	Smtpusername string
    36  	Smtppassword string
    37  }
    38  
    39  type TestServiceSettings struct {
    40  	Siteurl               string
    41  	Websocketurl          string
    42  	Licensedfieldlocation string
    43  }
    44  
    45  type TestTeamSettings struct {
    46  	Sitename       string
    47  	Maxuserperteam int
    48  }
    49  
    50  type TestClientRequirements struct {
    51  	Androidlatestversion string
    52  	Androidminversion    string
    53  	Desktoplatestversion string
    54  }
    55  
    56  type TestNewConfig struct {
    57  	TestNewServiceSettings TestNewServiceSettings
    58  	TestNewTeamSettings    TestNewTeamSettings
    59  }
    60  
    61  type TestNewServiceSettings struct {
    62  	SiteUrl                  *string
    63  	UseLetsEncrypt           *bool
    64  	TLSStrictTransportMaxAge *int64
    65  	AllowedThemes            []string
    66  }
    67  
    68  type TestNewTeamSettings struct {
    69  	SiteName       *string
    70  	MaxUserPerTeam *int
    71  }
    72  
    73  func TestConfigValidate(t *testing.T) {
    74  	th := Setup()
    75  	defer th.TearDown()
    76  
    77  	tempFile, err := ioutil.TempFile("", "TestConfigValidate")
    78  	require.NoError(t, err)
    79  	defer os.Remove(tempFile.Name())
    80  
    81  	assert.Error(t, th.RunCommand(t, "--config", tempFile.Name(), "config", "validate"))
    82  	th.CheckCommand(t, "config", "validate")
    83  }
    84  
    85  func TestConfigGet(t *testing.T) {
    86  	th := Setup()
    87  	defer th.TearDown()
    88  
    89  	t.Run("Error when no arguments are given", func(t *testing.T) {
    90  		assert.Error(t, th.RunCommand(t, "config", "get"))
    91  	})
    92  
    93  	t.Run("Error when more than one config settings are given", func(t *testing.T) {
    94  		assert.Error(t, th.RunCommand(t, "config", "get", "abc", "def"))
    95  	})
    96  
    97  	t.Run("Error when a config setting which is not in the config.json is given", func(t *testing.T) {
    98  		assert.Error(t, th.RunCommand(t, "config", "get", "abc"))
    99  	})
   100  
   101  	t.Run("No Error when a config setting which is in the config.json is given", func(t *testing.T) {
   102  		th.CheckCommand(t, "config", "get", "MessageExportSettings")
   103  		th.CheckCommand(t, "config", "get", "MessageExportSettings.GlobalRelaySettings")
   104  		th.CheckCommand(t, "config", "get", "MessageExportSettings.GlobalRelaySettings.CustomerType")
   105  	})
   106  
   107  	t.Run("check output", func(t *testing.T) {
   108  		output := th.CheckCommand(t, "config", "get", "MessageExportSettings")
   109  
   110  		assert.Contains(t, string(output), "EnableExport")
   111  		assert.Contains(t, string(output), "ExportFormat")
   112  		assert.Contains(t, string(output), "DailyRunTime")
   113  		assert.Contains(t, string(output), "ExportFromTimestamp")
   114  	})
   115  }
   116  
   117  func TestConfigSet(t *testing.T) {
   118  	th := Setup()
   119  	defer th.TearDown()
   120  
   121  	t.Run("Error when no arguments are given", func(t *testing.T) {
   122  		assert.Error(t, th.RunCommand(t, "config", "set"))
   123  	})
   124  
   125  	t.Run("Error when only one argument is given", func(t *testing.T) {
   126  		assert.Error(t, th.RunCommand(t, "config", "set", "test"))
   127  	})
   128  
   129  	t.Run("Error when the wrong key is set", func(t *testing.T) {
   130  		assert.Error(t, th.RunCommand(t, "config", "set", "invalid-key", "value"))
   131  		assert.Error(t, th.RunCommand(t, "config", "get", "invalid-key"))
   132  	})
   133  
   134  	t.Run("Error when the wrong value is set", func(t *testing.T) {
   135  		assert.Error(t, th.RunCommand(t, "config", "set", "EmailSettings.ConnectionSecurity", "invalid"))
   136  		output := th.CheckCommand(t, "config", "get", "EmailSettings.ConnectionSecurity")
   137  		assert.NotContains(t, string(output), "invalid")
   138  	})
   139  
   140  	t.Run("Error when the wrong locale is set", func(t *testing.T) {
   141  		th.CheckCommand(t, "config", "set", "LocalizationSettings.DefaultServerLocale", "es")
   142  		assert.Error(t, th.RunCommand(t, "config", "set", "LocalizationSettings.DefaultServerLocale", "invalid"))
   143  		output := th.CheckCommand(t, "config", "get", "LocalizationSettings.DefaultServerLocale")
   144  		assert.NotContains(t, string(output), "invalid")
   145  		assert.NotContains(t, string(output), "\"en\"")
   146  	})
   147  
   148  	t.Run("Success when a valid value is set", func(t *testing.T) {
   149  		assert.NoError(t, th.RunCommand(t, "config", "set", "EmailSettings.ConnectionSecurity", "TLS"))
   150  		output := th.CheckCommand(t, "config", "get", "EmailSettings.ConnectionSecurity")
   151  		assert.Contains(t, string(output), "TLS")
   152  	})
   153  
   154  	t.Run("Success when a valid locale is set", func(t *testing.T) {
   155  		assert.NoError(t, th.RunCommand(t, "config", "set", "LocalizationSettings.DefaultServerLocale", "es"))
   156  		output := th.CheckCommand(t, "config", "get", "LocalizationSettings.DefaultServerLocale")
   157  		assert.Contains(t, string(output), "\"es\"")
   158  	})
   159  }
   160  
   161  func TestConfigToMap(t *testing.T) {
   162  	// This test is almost the same as TestStructToMap, but I have it here for the sake of completions
   163  	cases := []struct {
   164  		Name     string
   165  		Input    interface{}
   166  		Expected map[string]interface{}
   167  	}{
   168  		{
   169  			Name: "Struct with one string field",
   170  			Input: struct {
   171  				Test string
   172  			}{
   173  				Test: "test",
   174  			},
   175  			Expected: map[string]interface{}{
   176  				"Test": "test",
   177  			},
   178  		},
   179  		{
   180  			Name: "String with multiple fields of different ",
   181  			Input: struct {
   182  				Test1 string
   183  				Test2 int
   184  				Test3 string
   185  				Test4 bool
   186  			}{
   187  				Test1: "test1",
   188  				Test2: 21,
   189  				Test3: "test2",
   190  				Test4: false,
   191  			},
   192  			Expected: map[string]interface{}{
   193  				"Test1": "test1",
   194  				"Test2": 21,
   195  				"Test3": "test2",
   196  				"Test4": false,
   197  			},
   198  		},
   199  		{
   200  			Name: "Nested fields",
   201  			Input: TestConfig{
   202  				TestServiceSettings{"abc", "def", "ghi"},
   203  				TestTeamSettings{"abc", 1},
   204  				TestClientRequirements{"abc", "def", "ghi"},
   205  				TestMessageExportSettings{true, "abc", TestGlobalRelaySettings{"abc", "def", "ghi"}},
   206  			},
   207  			Expected: map[string]interface{}{
   208  				"TestServiceSettings": map[string]interface{}{
   209  					"Siteurl":               "abc",
   210  					"Websocketurl":          "def",
   211  					"Licensedfieldlocation": "ghi",
   212  				},
   213  				"TestTeamSettings": map[string]interface{}{
   214  					"Sitename":       "abc",
   215  					"Maxuserperteam": 1,
   216  				},
   217  				"TestClientRequirements": map[string]interface{}{
   218  					"Androidlatestversion": "abc",
   219  					"Androidminversion":    "def",
   220  					"Desktoplatestversion": "ghi",
   221  				},
   222  				"TestMessageExportSettings": map[string]interface{}{
   223  					"Enableexport": true,
   224  					"Exportformat": "abc",
   225  					"TestGlobalRelaySettings": map[string]interface{}{
   226  						"Customertype": "abc",
   227  						"Smtpusername": "def",
   228  						"Smtppassword": "ghi",
   229  					},
   230  				},
   231  			},
   232  		},
   233  	}
   234  
   235  	for _, test := range cases {
   236  		t.Run(test.Name, func(t *testing.T) {
   237  			res := configToMap(test.Input)
   238  
   239  			if !reflect.DeepEqual(res, test.Expected) {
   240  				t.Errorf("got %v want %v ", res, test.Expected)
   241  			}
   242  		})
   243  	}
   244  }
   245  
   246  func TestPrintConfigValues(t *testing.T) {
   247  	outputs := []string{
   248  		"Siteurl: \"abc\"\nWebsocketurl: \"def\"\nLicensedfieldlocation: \"ghi\"\n",
   249  		"Sitename: \"abc\"\nMaxuserperteam: \"1\"\n",
   250  		"Androidlatestversion: \"abc\"\nAndroidminversion: \"def\"\nDesktoplatestversion: \"ghi\"\n",
   251  		"Enableexport: \"true\"\nExportformat: \"abc\"\nTestGlobalRelaySettings:\n\tCustomertype: \"abc\"\n\tSmtpusername: \"def\"\n\tSmtppassword: \"ghi\"\n",
   252  		"Customertype: \"abc\"\nSmtpusername: \"def\"\nSmtppassword: \"ghi\"\n",
   253  	}
   254  
   255  	commands := []string{
   256  		"TestServiceSettings",
   257  		"TestTeamSettings",
   258  		"TestClientRequirements",
   259  		"TestMessageExportSettings",
   260  		"TestMessageExportSettings.TestGlobalRelaySettings",
   261  	}
   262  
   263  	input := TestConfig{
   264  		TestServiceSettings{"abc", "def", "ghi"},
   265  		TestTeamSettings{"abc", 1},
   266  		TestClientRequirements{"abc", "def", "ghi"},
   267  		TestMessageExportSettings{true, "abc", TestGlobalRelaySettings{"abc", "def", "ghi"}},
   268  	}
   269  
   270  	configMap := structToMap(input)
   271  
   272  	cases := []struct {
   273  		Name     string
   274  		Command  string
   275  		Expected string
   276  	}{
   277  		{
   278  			Name:     "First test",
   279  			Command:  commands[0],
   280  			Expected: outputs[0],
   281  		},
   282  		{
   283  			Name:     "Second test",
   284  			Command:  commands[1],
   285  			Expected: outputs[1],
   286  		},
   287  		{
   288  			Name:     "third test",
   289  			Command:  commands[2],
   290  			Expected: outputs[2],
   291  		},
   292  		{
   293  			Name:     "fourth test",
   294  			Command:  commands[3],
   295  			Expected: outputs[3],
   296  		},
   297  		{
   298  			Name:     "fifth test",
   299  			Command:  commands[4],
   300  			Expected: outputs[4],
   301  		},
   302  	}
   303  
   304  	for _, test := range cases {
   305  		t.Run(test.Name, func(t *testing.T) {
   306  			res, _ := printConfigValues(configMap, strings.Split(test.Command, "."), test.Command)
   307  
   308  			// create two slice of string formed by splitting our strings on \n
   309  			slice1 := strings.Split(res, "\n")
   310  			slice2 := strings.Split(test.Expected, "\n")
   311  
   312  			sort.Strings(slice1)
   313  			sort.Strings(slice2)
   314  
   315  			if !reflect.DeepEqual(slice1, slice2) {
   316  				t.Errorf("got '%#v' want '%#v", slice1, slice2)
   317  			}
   318  		})
   319  	}
   320  }
   321  
   322  func TestConfigShow(t *testing.T) {
   323  	th := Setup()
   324  	defer th.TearDown()
   325  
   326  	t.Run("error with unknown subcommand", func(t *testing.T) {
   327  		assert.Error(t, th.RunCommand(t, "config", "show", "abc"))
   328  	})
   329  
   330  	t.Run("successfully dumping config", func(t *testing.T) {
   331  		output := th.CheckCommand(t, "config", "show")
   332  		assert.Contains(t, string(output), "SqlSettings")
   333  		assert.Contains(t, string(output), "MessageExportSettings")
   334  		assert.Contains(t, string(output), "AnnouncementSettings")
   335  	})
   336  }
   337  
   338  func TestSetConfig(t *testing.T) {
   339  	th := Setup()
   340  	defer th.TearDown()
   341  
   342  	// Error when no argument is given
   343  	assert.Error(t, th.RunCommand(t, "config", "set"))
   344  
   345  	// No Error when more than one argument is given
   346  	th.CheckCommand(t, "config", "set", "ThemeSettings.AllowedThemes", "hello", "World")
   347  
   348  	// No Error when two arguments are given
   349  	th.CheckCommand(t, "config", "set", "ThemeSettings.AllowedThemes", "hello")
   350  
   351  	// Error when only one argument is given
   352  	assert.Error(t, th.RunCommand(t, "config", "set", "ThemeSettings.AllowedThemes"))
   353  
   354  	// Error when config settings not in the config file are given
   355  	assert.Error(t, th.RunCommand(t, "config", "set", "Abc"))
   356  }
   357  
   358  func TestUpdateMap(t *testing.T) {
   359  	// create a config to make changes
   360  	config := TestNewConfig{
   361  		TestNewServiceSettings{
   362  			SiteUrl:                  model.NewString("abc.def"),
   363  			UseLetsEncrypt:           model.NewBool(false),
   364  			TLSStrictTransportMaxAge: model.NewInt64(36),
   365  			AllowedThemes:            []string{"Hello", "World"},
   366  		},
   367  		TestNewTeamSettings{
   368  			SiteName:       model.NewString("def.ghi"),
   369  			MaxUserPerTeam: model.NewInt(12),
   370  		},
   371  	}
   372  
   373  	// create a map of type map[string]interface
   374  	configMap := configToMap(config)
   375  
   376  	cases := []struct {
   377  		Name           string
   378  		configSettings []string
   379  		newVal         []string
   380  		expected       interface{}
   381  	}{
   382  		{
   383  			Name:           "check for Map and string",
   384  			configSettings: []string{"TestNewServiceSettings", "SiteUrl"},
   385  			newVal:         []string{"siteurl"},
   386  			expected:       "siteurl",
   387  		},
   388  		{
   389  			Name:           "check for Map and bool",
   390  			configSettings: []string{"TestNewServiceSettings", "UseLetsEncrypt"},
   391  			newVal:         []string{"true"},
   392  			expected:       true,
   393  		},
   394  		{
   395  			Name:           "check for Map and int64",
   396  			configSettings: []string{"TestNewServiceSettings", "TLSStrictTransportMaxAge"},
   397  			newVal:         []string{"56"},
   398  			expected:       int64(56),
   399  		},
   400  		{
   401  			Name:           "check for Map and string Slice",
   402  			configSettings: []string{"TestNewServiceSettings", "AllowedThemes"},
   403  			newVal:         []string{"hello1", "world1"},
   404  			expected:       []string{"hello1", "world1"},
   405  		},
   406  		{
   407  			Name:           "Map and string",
   408  			configSettings: []string{"TestNewTeamSettings", "SiteName"},
   409  			newVal:         []string{"jkl.mno"},
   410  			expected:       "jkl.mno",
   411  		},
   412  		{
   413  			Name:           "Map and int",
   414  			configSettings: []string{"TestNewTeamSettings", "MaxUserPerTeam"},
   415  			newVal:         []string{"18"},
   416  			expected:       18,
   417  		},
   418  	}
   419  
   420  	for _, test := range cases {
   421  
   422  		t.Run(test.Name, func(t *testing.T) {
   423  			err := UpdateMap(configMap, test.configSettings, test.newVal)
   424  
   425  			if err != nil {
   426  				t.Fatal("Wasn't expecting an error: ", err)
   427  			}
   428  
   429  			if !contains(configMap, test.expected, test.configSettings) {
   430  				t.Error("update didn't happen")
   431  			}
   432  
   433  		})
   434  	}
   435  
   436  }
   437  
   438  func contains(configMap map[string]interface{}, v interface{}, configSettings []string) bool {
   439  	res := configMap[configSettings[0]]
   440  
   441  	value := reflect.ValueOf(res)
   442  
   443  	switch value.Kind() {
   444  	case reflect.Map:
   445  		return contains(res.(map[string]interface{}), v, configSettings[1:])
   446  	case reflect.Slice:
   447  		return reflect.DeepEqual(value.Interface(), v)
   448  	case reflect.Int64:
   449  		return value.Interface() == v.(int64)
   450  	default:
   451  		return value.Interface() == v
   452  	}
   453  }