1// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2// See LICENSE.txt for license information. 3 4package config 5 6import ( 7 "testing" 8 9 "github.com/mattermost/mattermost-server/v6/model" 10 11 "github.com/stretchr/testify/require" 12) 13 14func defaultConfigGen() *model.Config { 15 cfg := &model.Config{} 16 cfg.SetDefaults() 17 return cfg 18} 19 20func BenchmarkDiff(b *testing.B) { 21 b.Run("equal empty", func(b *testing.B) { 22 baseCfg := &model.Config{} 23 actualCfg := &model.Config{} 24 b.ResetTimer() 25 for i := 0; i < b.N; i++ { 26 _, _ = Diff(baseCfg, actualCfg) 27 } 28 }) 29 30 b.Run("equal with defaults", func(b *testing.B) { 31 baseCfg := defaultConfigGen() 32 actualCfg := defaultConfigGen() 33 b.ResetTimer() 34 for i := 0; i < b.N; i++ { 35 _, _ = Diff(baseCfg, actualCfg) 36 } 37 }) 38 39 b.Run("actual empty", func(b *testing.B) { 40 baseCfg := defaultConfigGen() 41 actualCfg := &model.Config{} 42 b.ResetTimer() 43 for i := 0; i < b.N; i++ { 44 _, _ = Diff(baseCfg, actualCfg) 45 } 46 }) 47 48 b.Run("base empty", func(b *testing.B) { 49 baseCfg := &model.Config{} 50 actualCfg := defaultConfigGen() 51 b.ResetTimer() 52 for i := 0; i < b.N; i++ { 53 _, _ = Diff(baseCfg, actualCfg) 54 } 55 }) 56 57 b.Run("some diffs", func(b *testing.B) { 58 baseCfg := defaultConfigGen() 59 actualCfg := defaultConfigGen() 60 baseCfg.ServiceSettings.SiteURL = model.NewString("http://localhost") 61 baseCfg.ServiceSettings.ReadTimeout = model.NewInt(300) 62 baseCfg.SqlSettings.QueryTimeout = model.NewInt(0) 63 actualCfg.PluginSettings.EnableUploads = nil 64 actualCfg.TeamSettings.MaxChannelsPerTeam = model.NewInt64(100000) 65 actualCfg.FeatureFlags = nil 66 actualCfg.SqlSettings.DataSourceReplicas = []string{ 67 "ds0", 68 "ds1", 69 "ds2", 70 } 71 b.ResetTimer() 72 for i := 0; i < b.N; i++ { 73 _, _ = Diff(baseCfg, actualCfg) 74 } 75 }) 76} 77 78func TestDiff(t *testing.T) { 79 tcs := []struct { 80 name string 81 base *model.Config 82 actual *model.Config 83 diffs ConfigDiffs 84 err string 85 }{ 86 { 87 "nil", 88 nil, 89 nil, 90 nil, 91 "input configs should not be nil", 92 }, 93 { 94 "empty", 95 &model.Config{}, 96 &model.Config{}, 97 nil, 98 "", 99 }, 100 { 101 "defaults", 102 defaultConfigGen(), 103 defaultConfigGen(), 104 nil, 105 "", 106 }, 107 { 108 "default base, actual empty", 109 defaultConfigGen(), 110 &model.Config{}, 111 ConfigDiffs{ 112 { 113 "", 114 *defaultConfigGen(), 115 model.Config{}, 116 }, 117 }, 118 "", 119 }, 120 { 121 "empty base, actual default", 122 &model.Config{}, 123 defaultConfigGen(), 124 ConfigDiffs{ 125 { 126 "", 127 model.Config{}, 128 *defaultConfigGen(), 129 }, 130 }, 131 "", 132 }, 133 { 134 "string change", 135 defaultConfigGen(), 136 func() *model.Config { 137 cfg := defaultConfigGen() 138 cfg.ServiceSettings.SiteURL = model.NewString("http://changed") 139 return cfg 140 }(), 141 ConfigDiffs{ 142 { 143 Path: "ServiceSettings.SiteURL", 144 BaseVal: *defaultConfigGen().ServiceSettings.SiteURL, 145 ActualVal: "http://changed", 146 }, 147 }, 148 "", 149 }, 150 { 151 "string nil", 152 defaultConfigGen(), 153 func() *model.Config { 154 cfg := defaultConfigGen() 155 cfg.ServiceSettings.SiteURL = nil 156 return cfg 157 }(), 158 ConfigDiffs{ 159 { 160 Path: "ServiceSettings.SiteURL", 161 BaseVal: defaultConfigGen().ServiceSettings.SiteURL, 162 ActualVal: func() *string { 163 return nil 164 }(), 165 }, 166 }, 167 "", 168 }, 169 { 170 "bool change", 171 defaultConfigGen(), 172 func() *model.Config { 173 cfg := defaultConfigGen() 174 cfg.PluginSettings.Enable = model.NewBool(!*cfg.PluginSettings.Enable) 175 return cfg 176 }(), 177 ConfigDiffs{ 178 { 179 Path: "PluginSettings.Enable", 180 BaseVal: true, 181 ActualVal: false, 182 }, 183 }, 184 "", 185 }, 186 { 187 "bool nil", 188 defaultConfigGen(), 189 func() *model.Config { 190 cfg := defaultConfigGen() 191 cfg.PluginSettings.Enable = nil 192 return cfg 193 }(), 194 ConfigDiffs{ 195 { 196 Path: "PluginSettings.Enable", 197 BaseVal: defaultConfigGen().PluginSettings.Enable, 198 ActualVal: func() *bool { 199 return nil 200 }(), 201 }, 202 }, 203 "", 204 }, 205 { 206 "int change", 207 defaultConfigGen(), 208 func() *model.Config { 209 cfg := defaultConfigGen() 210 cfg.ServiceSettings.ReadTimeout = model.NewInt(0) 211 return cfg 212 }(), 213 ConfigDiffs{ 214 { 215 Path: "ServiceSettings.ReadTimeout", 216 BaseVal: *defaultConfigGen().ServiceSettings.ReadTimeout, 217 ActualVal: 0, 218 }, 219 }, 220 "", 221 }, 222 { 223 "int nil", 224 defaultConfigGen(), 225 func() *model.Config { 226 cfg := defaultConfigGen() 227 cfg.ServiceSettings.ReadTimeout = nil 228 return cfg 229 }(), 230 ConfigDiffs{ 231 { 232 Path: "ServiceSettings.ReadTimeout", 233 BaseVal: defaultConfigGen().ServiceSettings.ReadTimeout, 234 ActualVal: func() *int { 235 return nil 236 }(), 237 }, 238 }, 239 "", 240 }, 241 { 242 "slice addition", 243 defaultConfigGen(), 244 func() *model.Config { 245 cfg := defaultConfigGen() 246 cfg.SqlSettings.DataSourceReplicas = []string{ 247 "ds0", 248 "ds1", 249 } 250 return cfg 251 }(), 252 ConfigDiffs{ 253 { 254 Path: "SqlSettings.DataSourceReplicas", 255 BaseVal: defaultConfigGen().SqlSettings.DataSourceReplicas, 256 ActualVal: []string{ 257 "ds0", 258 "ds1", 259 }, 260 }, 261 }, 262 "", 263 }, 264 { 265 "slice deletion", 266 func() *model.Config { 267 cfg := defaultConfigGen() 268 cfg.SqlSettings.DataSourceReplicas = []string{ 269 "ds0", 270 "ds1", 271 } 272 return cfg 273 }(), 274 func() *model.Config { 275 cfg := defaultConfigGen() 276 cfg.SqlSettings.DataSourceReplicas = []string{ 277 "ds0", 278 } 279 return cfg 280 }(), 281 ConfigDiffs{ 282 { 283 Path: "SqlSettings.DataSourceReplicas", 284 BaseVal: []string{ 285 "ds0", 286 "ds1", 287 }, 288 ActualVal: []string{ 289 "ds0", 290 }, 291 }, 292 }, 293 "", 294 }, 295 { 296 "slice nil", 297 func() *model.Config { 298 cfg := defaultConfigGen() 299 cfg.SqlSettings.DataSourceReplicas = []string{ 300 "ds0", 301 "ds1", 302 } 303 return cfg 304 }(), 305 func() *model.Config { 306 cfg := defaultConfigGen() 307 cfg.SqlSettings.DataSourceReplicas = nil 308 return cfg 309 }(), 310 ConfigDiffs{ 311 { 312 Path: "SqlSettings.DataSourceReplicas", 313 BaseVal: []string{ 314 "ds0", 315 "ds1", 316 }, 317 ActualVal: func() []string { 318 return nil 319 }(), 320 }, 321 }, 322 "", 323 }, 324 { 325 "map change", 326 defaultConfigGen(), 327 func() *model.Config { 328 cfg := defaultConfigGen() 329 cfg.PluginSettings.PluginStates["com.mattermost.nps"] = &model.PluginState{ 330 Enable: !cfg.PluginSettings.PluginStates["com.mattermost.nps"].Enable, 331 } 332 return cfg 333 }(), 334 ConfigDiffs{ 335 { 336 Path: "PluginSettings.PluginStates", 337 BaseVal: defaultConfigGen().PluginSettings.PluginStates, 338 ActualVal: map[string]*model.PluginState{ 339 "com.mattermost.nps": { 340 Enable: !defaultConfigGen().PluginSettings.PluginStates["com.mattermost.nps"].Enable, 341 }, 342 "focalboard": { 343 Enable: true, 344 }, 345 "playbooks": { 346 Enable: true, 347 }, 348 }, 349 }, 350 }, 351 "", 352 }, 353 { 354 "map addition", 355 defaultConfigGen(), 356 func() *model.Config { 357 cfg := defaultConfigGen() 358 cfg.PluginSettings.PluginStates["com.mattermost.newplugin"] = &model.PluginState{ 359 Enable: true, 360 } 361 return cfg 362 }(), 363 ConfigDiffs{ 364 { 365 Path: "PluginSettings.PluginStates", 366 BaseVal: defaultConfigGen().PluginSettings.PluginStates, 367 ActualVal: map[string]*model.PluginState{ 368 "com.mattermost.nps": { 369 Enable: defaultConfigGen().PluginSettings.PluginStates["com.mattermost.nps"].Enable, 370 }, 371 "com.mattermost.newplugin": { 372 Enable: true, 373 }, 374 "focalboard": { 375 Enable: true, 376 }, 377 "playbooks": { 378 Enable: true, 379 }, 380 }, 381 }, 382 }, 383 "", 384 }, 385 { 386 "map deletion", 387 defaultConfigGen(), 388 func() *model.Config { 389 cfg := defaultConfigGen() 390 delete(cfg.PluginSettings.PluginStates, "com.mattermost.nps") 391 return cfg 392 }(), 393 ConfigDiffs{ 394 { 395 Path: "PluginSettings.PluginStates", 396 BaseVal: defaultConfigGen().PluginSettings.PluginStates, 397 ActualVal: map[string]*model.PluginState{ 398 "focalboard": { 399 Enable: true, 400 }, 401 "playbooks": { 402 Enable: true, 403 }, 404 }, 405 }, 406 }, 407 "", 408 }, 409 { 410 "map nil", 411 defaultConfigGen(), 412 func() *model.Config { 413 cfg := defaultConfigGen() 414 cfg.PluginSettings.PluginStates = nil 415 return cfg 416 }(), 417 ConfigDiffs{ 418 { 419 Path: "PluginSettings.PluginStates", 420 BaseVal: defaultConfigGen().PluginSettings.PluginStates, 421 ActualVal: func() map[string]*model.PluginState { 422 return nil 423 }(), 424 }, 425 }, 426 "", 427 }, 428 { 429 "map type change", 430 func() *model.Config { 431 cfg := defaultConfigGen() 432 cfg.PluginSettings.Plugins = map[string]map[string]interface{}{ 433 "com.mattermost.newplugin": { 434 "key": true, 435 }, 436 } 437 return cfg 438 }(), 439 func() *model.Config { 440 cfg := defaultConfigGen() 441 cfg.PluginSettings.Plugins = map[string]map[string]interface{}{ 442 "com.mattermost.newplugin": { 443 "key": "string", 444 }, 445 } 446 return cfg 447 }(), 448 ConfigDiffs{ 449 { 450 Path: "PluginSettings.Plugins", 451 BaseVal: func() interface{} { 452 return map[string]map[string]interface{}{ 453 "com.mattermost.newplugin": { 454 "key": true, 455 }, 456 } 457 }(), 458 ActualVal: func() interface{} { 459 return map[string]map[string]interface{}{ 460 "com.mattermost.newplugin": { 461 "key": "string", 462 }, 463 } 464 }(), 465 }, 466 }, 467 "", 468 }, 469 } 470 471 for _, tc := range tcs { 472 t.Run(tc.name, func(t *testing.T) { 473 diffs, err := Diff(tc.base, tc.actual) 474 if tc.err != "" { 475 require.EqualError(t, err, tc.err) 476 require.Nil(t, diffs) 477 } else { 478 require.NoError(t, err) 479 } 480 require.Equal(t, tc.diffs, diffs) 481 }) 482 } 483} 484