github.com/cjdelisle/matterfoss@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 }