github.com/mattermost/mattermost-server/server/v8@v8.0.0-20230610055354-a6d1d38b273d/config/diff_test.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package config 5 6 import ( 7 "testing" 8 9 "github.com/mattermost/mattermost-server/server/public/model" 10 11 "github.com/stretchr/testify/require" 12 ) 13 14 func defaultConfigGen() *model.Config { 15 cfg := &model.Config{} 16 cfg.SetDefaults() 17 return cfg 18 } 19 20 func 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 78 func TestDiffSanitized(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 Path: "", 114 BaseVal: func() model.Config { 115 cfg := defaultConfigGen() 116 cfg.Sanitize() 117 return *cfg 118 }(), 119 ActualVal: model.Config{}, 120 }, 121 }, 122 "", 123 }, 124 { 125 "empty base, actual default", 126 &model.Config{}, 127 defaultConfigGen(), 128 ConfigDiffs{ 129 { 130 Path: "", 131 BaseVal: model.Config{}, 132 ActualVal: func() model.Config { 133 cfg := defaultConfigGen() 134 cfg.Sanitize() 135 return *cfg 136 }(), 137 }, 138 }, 139 "", 140 }, 141 { 142 "sensitive LdapSettings.BindPassword", 143 func() *model.Config { 144 cfg := defaultConfigGen() 145 cfg.LdapSettings.BindPassword = model.NewString("base") 146 return cfg 147 }(), 148 func() *model.Config { 149 cfg := defaultConfigGen() 150 cfg.LdapSettings.BindPassword = model.NewString("actual") 151 return cfg 152 }(), 153 ConfigDiffs{ 154 { 155 Path: "LdapSettings.BindPassword", 156 BaseVal: model.FakeSetting, 157 ActualVal: model.FakeSetting, 158 }, 159 }, 160 "", 161 }, 162 { 163 "sensitive FileSettings.PublicLinkSalt", 164 func() *model.Config { 165 cfg := defaultConfigGen() 166 cfg.FileSettings.PublicLinkSalt = model.NewString("base") 167 return cfg 168 }(), 169 func() *model.Config { 170 cfg := defaultConfigGen() 171 cfg.FileSettings.PublicLinkSalt = model.NewString("actual") 172 return cfg 173 }(), 174 ConfigDiffs{ 175 { 176 Path: "FileSettings.PublicLinkSalt", 177 BaseVal: model.FakeSetting, 178 ActualVal: model.FakeSetting, 179 }, 180 }, 181 "", 182 }, 183 { 184 "sensitive FileSettings.AmazonS3SecretAccessKey", 185 func() *model.Config { 186 cfg := defaultConfigGen() 187 cfg.FileSettings.AmazonS3SecretAccessKey = model.NewString("base") 188 return cfg 189 }(), 190 func() *model.Config { 191 cfg := defaultConfigGen() 192 cfg.FileSettings.AmazonS3SecretAccessKey = model.NewString("actual") 193 return cfg 194 }(), 195 ConfigDiffs{ 196 { 197 Path: "FileSettings.AmazonS3SecretAccessKey", 198 BaseVal: model.FakeSetting, 199 ActualVal: model.FakeSetting, 200 }, 201 }, 202 "", 203 }, 204 { 205 "sensitive SqlSettings.DataSource", 206 func() *model.Config { 207 cfg := defaultConfigGen() 208 cfg.SqlSettings.DataSource = model.NewString("base") 209 return cfg 210 }(), 211 func() *model.Config { 212 cfg := defaultConfigGen() 213 cfg.SqlSettings.DataSource = model.NewString("actual") 214 return cfg 215 }(), 216 ConfigDiffs{ 217 { 218 Path: "SqlSettings.DataSource", 219 BaseVal: model.FakeSetting, 220 ActualVal: model.FakeSetting, 221 }, 222 }, 223 "", 224 }, 225 { 226 "sensitive SqlSettings.AtRestEncryptKey", 227 func() *model.Config { 228 cfg := defaultConfigGen() 229 cfg.SqlSettings.AtRestEncryptKey = model.NewString("base") 230 return cfg 231 }(), 232 func() *model.Config { 233 cfg := defaultConfigGen() 234 cfg.SqlSettings.AtRestEncryptKey = model.NewString("actual") 235 return cfg 236 }(), 237 ConfigDiffs{ 238 { 239 Path: "SqlSettings.AtRestEncryptKey", 240 BaseVal: model.FakeSetting, 241 ActualVal: model.FakeSetting, 242 }, 243 }, 244 "", 245 }, 246 { 247 "sensitive SqlSettings.DataSourceReplicas", 248 func() *model.Config { 249 cfg := defaultConfigGen() 250 cfg.SqlSettings.DataSourceReplicas = []string{ 251 "ds0", 252 "ds1", 253 } 254 return cfg 255 }(), 256 func() *model.Config { 257 cfg := defaultConfigGen() 258 cfg.SqlSettings.DataSourceReplicas = []string{ 259 "ds0", 260 "ds1", 261 "ds2", 262 } 263 return cfg 264 }(), 265 ConfigDiffs{ 266 { 267 Path: "SqlSettings.DataSourceReplicas", 268 BaseVal: model.FakeSetting, 269 ActualVal: model.FakeSetting, 270 }, 271 }, 272 "", 273 }, 274 { 275 "sensitive SqlSettings.DataSourceSearchReplicas", 276 func() *model.Config { 277 cfg := defaultConfigGen() 278 cfg.SqlSettings.DataSourceSearchReplicas = []string{ 279 "ds0", 280 "ds1", 281 } 282 return cfg 283 }(), 284 func() *model.Config { 285 cfg := defaultConfigGen() 286 cfg.SqlSettings.DataSourceSearchReplicas = []string{ 287 "ds0", 288 "ds1", 289 "ds2", 290 } 291 return cfg 292 }(), 293 ConfigDiffs{ 294 { 295 Path: "SqlSettings.DataSourceSearchReplicas", 296 BaseVal: model.FakeSetting, 297 ActualVal: model.FakeSetting, 298 }, 299 }, 300 "", 301 }, 302 { 303 "sensitive EmailSettings.SMTPPassword", 304 func() *model.Config { 305 cfg := defaultConfigGen() 306 cfg.EmailSettings.SMTPPassword = model.NewString("base") 307 return cfg 308 }(), 309 func() *model.Config { 310 cfg := defaultConfigGen() 311 cfg.EmailSettings.SMTPPassword = model.NewString("actual") 312 return cfg 313 }(), 314 ConfigDiffs{ 315 { 316 Path: "EmailSettings.SMTPPassword", 317 BaseVal: model.FakeSetting, 318 ActualVal: model.FakeSetting, 319 }, 320 }, 321 "", 322 }, 323 { 324 "sensitive GitLabSettings.Secret", 325 func() *model.Config { 326 cfg := defaultConfigGen() 327 cfg.GitLabSettings.Secret = model.NewString("base") 328 return cfg 329 }(), 330 func() *model.Config { 331 cfg := defaultConfigGen() 332 cfg.GitLabSettings.Secret = model.NewString("actual") 333 return cfg 334 }(), 335 ConfigDiffs{ 336 { 337 Path: "GitLabSettings.Secret", 338 BaseVal: model.FakeSetting, 339 ActualVal: model.FakeSetting, 340 }, 341 }, 342 "", 343 }, 344 { 345 "sensitive GoogleSettings.Secret", 346 func() *model.Config { 347 cfg := defaultConfigGen() 348 cfg.GoogleSettings.Secret = model.NewString("base") 349 return cfg 350 }(), 351 func() *model.Config { 352 cfg := defaultConfigGen() 353 cfg.GoogleSettings.Secret = model.NewString("actual") 354 return cfg 355 }(), 356 ConfigDiffs{ 357 { 358 Path: "GoogleSettings.Secret", 359 BaseVal: model.FakeSetting, 360 ActualVal: model.FakeSetting, 361 }, 362 }, 363 "", 364 }, 365 { 366 "sensitive Office365Settings.Secret", 367 func() *model.Config { 368 cfg := defaultConfigGen() 369 cfg.Office365Settings.Secret = model.NewString("base") 370 return cfg 371 }(), 372 func() *model.Config { 373 cfg := defaultConfigGen() 374 cfg.Office365Settings.Secret = model.NewString("actual") 375 return cfg 376 }(), 377 ConfigDiffs{ 378 { 379 Path: "Office365Settings.Secret", 380 BaseVal: model.FakeSetting, 381 ActualVal: model.FakeSetting, 382 }, 383 }, 384 "", 385 }, 386 { 387 "sensitive OpenIdSettings.Secret", 388 func() *model.Config { 389 cfg := defaultConfigGen() 390 cfg.OpenIdSettings.Secret = model.NewString("base") 391 return cfg 392 }(), 393 func() *model.Config { 394 cfg := defaultConfigGen() 395 cfg.OpenIdSettings.Secret = model.NewString("actual") 396 return cfg 397 }(), 398 ConfigDiffs{ 399 { 400 Path: "OpenIdSettings.Secret", 401 BaseVal: model.FakeSetting, 402 ActualVal: model.FakeSetting, 403 }, 404 }, 405 "", 406 }, 407 { 408 "sensitive ElasticsearchSettings.Password", 409 func() *model.Config { 410 cfg := defaultConfigGen() 411 cfg.ElasticsearchSettings.Password = model.NewString("base") 412 return cfg 413 }(), 414 func() *model.Config { 415 cfg := defaultConfigGen() 416 cfg.ElasticsearchSettings.Password = model.NewString("actual") 417 return cfg 418 }(), 419 ConfigDiffs{ 420 { 421 Path: "ElasticsearchSettings.Password", 422 BaseVal: model.FakeSetting, 423 ActualVal: model.FakeSetting, 424 }, 425 }, 426 "", 427 }, 428 { 429 "sensitive MessageExportSettings.GlobalRelaySettings", 430 func() *model.Config { 431 cfg := defaultConfigGen() 432 cfg.MessageExportSettings.GlobalRelaySettings = &model.GlobalRelayMessageExportSettings{ 433 SMTPUsername: model.NewString("base"), 434 SMTPPassword: model.NewString("base"), 435 EmailAddress: model.NewString("base"), 436 } 437 return cfg 438 }(), 439 func() *model.Config { 440 cfg := defaultConfigGen() 441 cfg.MessageExportSettings.GlobalRelaySettings = &model.GlobalRelayMessageExportSettings{ 442 SMTPUsername: model.NewString("actual"), 443 SMTPPassword: model.NewString("actual"), 444 EmailAddress: model.NewString("actual"), 445 } 446 return cfg 447 }(), 448 ConfigDiffs{ 449 { 450 Path: "MessageExportSettings.GlobalRelaySettings.SMTPUsername", 451 BaseVal: model.FakeSetting, 452 ActualVal: model.FakeSetting, 453 }, 454 { 455 Path: "MessageExportSettings.GlobalRelaySettings.SMTPPassword", 456 BaseVal: model.FakeSetting, 457 ActualVal: model.FakeSetting, 458 }, 459 { 460 Path: "MessageExportSettings.GlobalRelaySettings.EmailAddress", 461 BaseVal: model.FakeSetting, 462 ActualVal: model.FakeSetting, 463 }, 464 }, 465 "", 466 }, 467 { 468 "sensitive ServiceSettings.GfycatAPISecret", 469 func() *model.Config { 470 cfg := defaultConfigGen() 471 cfg.ServiceSettings.GfycatAPISecret = model.NewString("base") 472 return cfg 473 }(), 474 func() *model.Config { 475 cfg := defaultConfigGen() 476 cfg.ServiceSettings.GfycatAPISecret = model.NewString("actual") 477 return cfg 478 }(), 479 ConfigDiffs{ 480 { 481 Path: "ServiceSettings.GfycatAPISecret", 482 BaseVal: model.FakeSetting, 483 ActualVal: model.FakeSetting, 484 }, 485 }, 486 "", 487 }, 488 { 489 "sensitive ServiceSettings.SplitKey", 490 func() *model.Config { 491 cfg := defaultConfigGen() 492 cfg.ServiceSettings.SplitKey = model.NewString("base") 493 return cfg 494 }(), 495 func() *model.Config { 496 cfg := defaultConfigGen() 497 cfg.ServiceSettings.SplitKey = model.NewString("actual") 498 return cfg 499 }(), 500 ConfigDiffs{ 501 { 502 Path: "ServiceSettings.SplitKey", 503 BaseVal: model.FakeSetting, 504 ActualVal: model.FakeSetting, 505 }, 506 }, 507 "", 508 }, 509 { 510 "plugin config", 511 defaultConfigGen(), 512 func() *model.Config { 513 cfg := defaultConfigGen() 514 cfg.PluginSettings.Plugins = map[string]map[string]any{ 515 "com.mattermost.newplugin": { 516 "key": true, 517 }, 518 } 519 return cfg 520 }(), 521 ConfigDiffs{ 522 { 523 Path: "PluginSettings.Plugins", 524 BaseVal: model.FakeSetting, 525 ActualVal: model.FakeSetting, 526 }, 527 }, 528 "", 529 }, 530 } 531 532 for _, tc := range tcs { 533 t.Run(tc.name, func(t *testing.T) { 534 diffs, err := Diff(tc.base, tc.actual) 535 if tc.err != "" { 536 require.EqualError(t, err, tc.err) 537 require.Nil(t, diffs) 538 } else { 539 require.NoError(t, err) 540 } 541 require.Equal(t, tc.diffs, diffs.Sanitize()) 542 }) 543 } 544 } 545 546 func TestDiff(t *testing.T) { 547 tcs := []struct { 548 name string 549 base *model.Config 550 actual *model.Config 551 diffs ConfigDiffs 552 err string 553 }{ 554 { 555 "nil", 556 nil, 557 nil, 558 nil, 559 "input configs should not be nil", 560 }, 561 { 562 "empty", 563 &model.Config{}, 564 &model.Config{}, 565 nil, 566 "", 567 }, 568 { 569 "defaults", 570 defaultConfigGen(), 571 defaultConfigGen(), 572 nil, 573 "", 574 }, 575 { 576 "default base, actual empty", 577 defaultConfigGen(), 578 &model.Config{}, 579 ConfigDiffs{ 580 { 581 Path: "", 582 BaseVal: *defaultConfigGen(), 583 ActualVal: model.Config{}, 584 }, 585 }, 586 "", 587 }, 588 { 589 "empty base, actual default", 590 &model.Config{}, 591 defaultConfigGen(), 592 ConfigDiffs{ 593 { 594 Path: "", 595 BaseVal: model.Config{}, 596 ActualVal: *defaultConfigGen(), 597 }, 598 }, 599 "", 600 }, 601 { 602 "string change", 603 defaultConfigGen(), 604 func() *model.Config { 605 cfg := defaultConfigGen() 606 cfg.ServiceSettings.SiteURL = model.NewString("http://changed") 607 return cfg 608 }(), 609 ConfigDiffs{ 610 { 611 Path: "ServiceSettings.SiteURL", 612 BaseVal: *defaultConfigGen().ServiceSettings.SiteURL, 613 ActualVal: "http://changed", 614 }, 615 }, 616 "", 617 }, 618 { 619 "string nil", 620 defaultConfigGen(), 621 func() *model.Config { 622 cfg := defaultConfigGen() 623 cfg.ServiceSettings.SiteURL = nil 624 return cfg 625 }(), 626 ConfigDiffs{ 627 { 628 Path: "ServiceSettings.SiteURL", 629 BaseVal: defaultConfigGen().ServiceSettings.SiteURL, 630 ActualVal: func() *string { 631 return nil 632 }(), 633 }, 634 }, 635 "", 636 }, 637 { 638 "bool change", 639 defaultConfigGen(), 640 func() *model.Config { 641 cfg := defaultConfigGen() 642 cfg.PluginSettings.Enable = model.NewBool(!*cfg.PluginSettings.Enable) 643 return cfg 644 }(), 645 ConfigDiffs{ 646 { 647 Path: "PluginSettings.Enable", 648 BaseVal: true, 649 ActualVal: false, 650 }, 651 }, 652 "", 653 }, 654 { 655 "bool nil", 656 defaultConfigGen(), 657 func() *model.Config { 658 cfg := defaultConfigGen() 659 cfg.PluginSettings.Enable = nil 660 return cfg 661 }(), 662 ConfigDiffs{ 663 { 664 Path: "PluginSettings.Enable", 665 BaseVal: defaultConfigGen().PluginSettings.Enable, 666 ActualVal: func() *bool { 667 return nil 668 }(), 669 }, 670 }, 671 "", 672 }, 673 { 674 "int change", 675 defaultConfigGen(), 676 func() *model.Config { 677 cfg := defaultConfigGen() 678 cfg.ServiceSettings.ReadTimeout = model.NewInt(0) 679 return cfg 680 }(), 681 ConfigDiffs{ 682 { 683 Path: "ServiceSettings.ReadTimeout", 684 BaseVal: *defaultConfigGen().ServiceSettings.ReadTimeout, 685 ActualVal: 0, 686 }, 687 }, 688 "", 689 }, 690 { 691 "int nil", 692 defaultConfigGen(), 693 func() *model.Config { 694 cfg := defaultConfigGen() 695 cfg.ServiceSettings.ReadTimeout = nil 696 return cfg 697 }(), 698 ConfigDiffs{ 699 { 700 Path: "ServiceSettings.ReadTimeout", 701 BaseVal: defaultConfigGen().ServiceSettings.ReadTimeout, 702 ActualVal: func() *int { 703 return nil 704 }(), 705 }, 706 }, 707 "", 708 }, 709 { 710 "slice addition", 711 defaultConfigGen(), 712 func() *model.Config { 713 cfg := defaultConfigGen() 714 cfg.SqlSettings.DataSourceReplicas = []string{ 715 "ds0", 716 "ds1", 717 } 718 return cfg 719 }(), 720 ConfigDiffs{ 721 { 722 Path: "SqlSettings.DataSourceReplicas", 723 BaseVal: defaultConfigGen().SqlSettings.DataSourceReplicas, 724 ActualVal: []string{ 725 "ds0", 726 "ds1", 727 }, 728 }, 729 }, 730 "", 731 }, 732 { 733 "slice deletion", 734 func() *model.Config { 735 cfg := defaultConfigGen() 736 cfg.SqlSettings.DataSourceReplicas = []string{ 737 "ds0", 738 "ds1", 739 } 740 return cfg 741 }(), 742 func() *model.Config { 743 cfg := defaultConfigGen() 744 cfg.SqlSettings.DataSourceReplicas = []string{ 745 "ds0", 746 } 747 return cfg 748 }(), 749 ConfigDiffs{ 750 { 751 Path: "SqlSettings.DataSourceReplicas", 752 BaseVal: []string{ 753 "ds0", 754 "ds1", 755 }, 756 ActualVal: []string{ 757 "ds0", 758 }, 759 }, 760 }, 761 "", 762 }, 763 { 764 "slice nil", 765 func() *model.Config { 766 cfg := defaultConfigGen() 767 cfg.SqlSettings.DataSourceReplicas = []string{ 768 "ds0", 769 "ds1", 770 } 771 return cfg 772 }(), 773 func() *model.Config { 774 cfg := defaultConfigGen() 775 cfg.SqlSettings.DataSourceReplicas = nil 776 return cfg 777 }(), 778 ConfigDiffs{ 779 { 780 Path: "SqlSettings.DataSourceReplicas", 781 BaseVal: []string{ 782 "ds0", 783 "ds1", 784 }, 785 ActualVal: func() []string { 786 return nil 787 }(), 788 }, 789 }, 790 "", 791 }, 792 { 793 "map change", 794 defaultConfigGen(), 795 func() *model.Config { 796 cfg := defaultConfigGen() 797 cfg.PluginSettings.PluginStates["com.mattermost.nps"] = &model.PluginState{ 798 Enable: !cfg.PluginSettings.PluginStates["com.mattermost.nps"].Enable, 799 } 800 return cfg 801 }(), 802 ConfigDiffs{ 803 { 804 Path: "PluginSettings.PluginStates", 805 BaseVal: defaultConfigGen().PluginSettings.PluginStates, 806 ActualVal: map[string]*model.PluginState{ 807 "com.mattermost.nps": { 808 Enable: !defaultConfigGen().PluginSettings.PluginStates["com.mattermost.nps"].Enable, 809 }, 810 "com.mattermost.apps": { 811 Enable: true, 812 }, 813 "com.mattermost.calls": { 814 Enable: true, 815 }, 816 }, 817 }, 818 }, 819 "", 820 }, 821 { 822 "map addition", 823 defaultConfigGen(), 824 func() *model.Config { 825 cfg := defaultConfigGen() 826 cfg.PluginSettings.PluginStates["com.mattermost.newplugin"] = &model.PluginState{ 827 Enable: true, 828 } 829 return cfg 830 }(), 831 ConfigDiffs{ 832 { 833 Path: "PluginSettings.PluginStates", 834 BaseVal: defaultConfigGen().PluginSettings.PluginStates, 835 ActualVal: map[string]*model.PluginState{ 836 "com.mattermost.nps": { 837 Enable: defaultConfigGen().PluginSettings.PluginStates["com.mattermost.nps"].Enable, 838 }, 839 "com.mattermost.newplugin": { 840 Enable: true, 841 }, 842 "com.mattermost.apps": { 843 Enable: true, 844 }, 845 "com.mattermost.calls": { 846 Enable: true, 847 }, 848 }, 849 }, 850 }, 851 "", 852 }, 853 { 854 "map deletion", 855 defaultConfigGen(), 856 func() *model.Config { 857 cfg := defaultConfigGen() 858 delete(cfg.PluginSettings.PluginStates, "com.mattermost.nps") 859 return cfg 860 }(), 861 ConfigDiffs{ 862 { 863 Path: "PluginSettings.PluginStates", 864 BaseVal: defaultConfigGen().PluginSettings.PluginStates, 865 ActualVal: map[string]*model.PluginState{ 866 "com.mattermost.apps": { 867 Enable: true, 868 }, 869 "com.mattermost.calls": { 870 Enable: true, 871 }, 872 }, 873 }, 874 }, 875 "", 876 }, 877 { 878 "map nil", 879 defaultConfigGen(), 880 func() *model.Config { 881 cfg := defaultConfigGen() 882 cfg.PluginSettings.PluginStates = nil 883 return cfg 884 }(), 885 ConfigDiffs{ 886 { 887 Path: "PluginSettings.PluginStates", 888 BaseVal: defaultConfigGen().PluginSettings.PluginStates, 889 ActualVal: func() map[string]*model.PluginState { 890 return nil 891 }(), 892 }, 893 }, 894 "", 895 }, 896 { 897 "map type change", 898 func() *model.Config { 899 cfg := defaultConfigGen() 900 cfg.PluginSettings.Plugins = map[string]map[string]any{ 901 "com.mattermost.newplugin": { 902 "key": true, 903 }, 904 } 905 return cfg 906 }(), 907 func() *model.Config { 908 cfg := defaultConfigGen() 909 cfg.PluginSettings.Plugins = map[string]map[string]any{ 910 "com.mattermost.newplugin": { 911 "key": "string", 912 }, 913 } 914 return cfg 915 }(), 916 ConfigDiffs{ 917 { 918 Path: "PluginSettings.Plugins", 919 BaseVal: func() any { 920 return map[string]map[string]any{ 921 "com.mattermost.newplugin": { 922 "key": true, 923 }, 924 } 925 }(), 926 ActualVal: func() any { 927 return map[string]map[string]any{ 928 "com.mattermost.newplugin": { 929 "key": "string", 930 }, 931 } 932 }(), 933 }, 934 }, 935 "", 936 }, 937 } 938 939 for _, tc := range tcs { 940 t.Run(tc.name, func(t *testing.T) { 941 diffs, err := Diff(tc.base, tc.actual) 942 if tc.err != "" { 943 require.EqualError(t, err, tc.err) 944 require.Nil(t, diffs) 945 } else { 946 require.NoError(t, err) 947 } 948 require.Equal(t, tc.diffs, diffs) 949 }) 950 } 951 }