github.com/GoogleCloudPlatform/testgrid@v0.0.174/config/converge_test.go (about)

     1  /*
     2  Copyright 2021 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package config
    18  
    19  import (
    20  	"reflect"
    21  	"testing"
    22  
    23  	configpb "github.com/GoogleCloudPlatform/testgrid/pb/config"
    24  	"github.com/golang/protobuf/proto"
    25  )
    26  
    27  func TestConverge(t *testing.T) {
    28  	cases := []struct {
    29  		name     string
    30  		inputs   map[string]*configpb.Configuration
    31  		expected *configpb.Configuration
    32  	}{
    33  		{
    34  			name: "nil input; throws error",
    35  		},
    36  		{
    37  			name: "Merge with no conflicts; no name changes",
    38  			inputs: map[string]*configpb.Configuration{
    39  				"halloween": {
    40  					TestGroups: []*configpb.TestGroup{
    41  						aValidTestGroupNamed("pumpkin-decorator"),
    42  						aValidTestGroupNamed("cobweb-decorator"),
    43  						aValidTestGroupNamed("candy-corn-emitter"),
    44  					},
    45  					Dashboards: []*configpb.Dashboard{
    46  						{
    47  							Name: "Decorations",
    48  							DashboardTab: []*configpb.DashboardTab{
    49  								{
    50  									Name:          "Jack-O-Lanterns",
    51  									TestGroupName: "pumpkin-decorator",
    52  								},
    53  								{
    54  									Name:          "Spooky Cave",
    55  									TestGroupName: "cobweb-decorator",
    56  								},
    57  							},
    58  						},
    59  						{
    60  							Name: "Treats",
    61  							DashboardTab: []*configpb.DashboardTab{
    62  								{
    63  									Name:          "Candy Corn Only (sorry)",
    64  									TestGroupName: "candy-corn-emitter",
    65  								},
    66  							},
    67  						},
    68  					},
    69  					DashboardGroups: []*configpb.DashboardGroup{
    70  						{
    71  							Name:           "Halloween",
    72  							DashboardNames: []string{"Treats", "Decorations"},
    73  						},
    74  					},
    75  				},
    76  				"thanksgiving": {
    77  					TestGroups: []*configpb.TestGroup{
    78  						aValidTestGroupNamed("meal-preparer"),
    79  					},
    80  					Dashboards: []*configpb.Dashboard{
    81  						{
    82  							Name: "Thanksgiving",
    83  							DashboardTab: []*configpb.DashboardTab{
    84  								{
    85  									Name:          "Meal Prep",
    86  									TestGroupName: "meal-preparer",
    87  								},
    88  							},
    89  						},
    90  					},
    91  				},
    92  			},
    93  			expected: &configpb.Configuration{
    94  				TestGroups: []*configpb.TestGroup{
    95  					aValidTestGroupNamed("pumpkin-decorator"),
    96  					aValidTestGroupNamed("cobweb-decorator"),
    97  					aValidTestGroupNamed("candy-corn-emitter"),
    98  					aValidTestGroupNamed("meal-preparer"),
    99  				},
   100  				Dashboards: []*configpb.Dashboard{
   101  					{
   102  						Name: "Decorations",
   103  						DashboardTab: []*configpb.DashboardTab{
   104  							{
   105  								Name:          "Jack-O-Lanterns",
   106  								TestGroupName: "pumpkin-decorator",
   107  							},
   108  							{
   109  								Name:          "Spooky Cave",
   110  								TestGroupName: "cobweb-decorator",
   111  							},
   112  						},
   113  					},
   114  					{
   115  						Name: "Treats",
   116  						DashboardTab: []*configpb.DashboardTab{
   117  							{
   118  								Name:          "Candy Corn Only (sorry)",
   119  								TestGroupName: "candy-corn-emitter",
   120  							},
   121  						},
   122  					},
   123  					{
   124  						Name: "Thanksgiving",
   125  						DashboardTab: []*configpb.DashboardTab{
   126  							{
   127  								Name:          "Meal Prep",
   128  								TestGroupName: "meal-preparer",
   129  							},
   130  						},
   131  					},
   132  				},
   133  				DashboardGroups: []*configpb.DashboardGroup{
   134  					{
   135  						Name:           "Halloween",
   136  						DashboardNames: []string{"Treats", "Decorations"},
   137  					},
   138  				},
   139  			},
   140  		},
   141  		{
   142  			name: "Merge with conflicts; renames with key-prefix",
   143  			inputs: map[string]*configpb.Configuration{
   144  				"halloween": {
   145  					TestGroups: []*configpb.TestGroup{
   146  						aValidTestGroupNamed("pumpkin-decorator"),
   147  						aValidTestGroupNamed("candy-emitter"),
   148  					},
   149  					Dashboards: []*configpb.Dashboard{
   150  						{
   151  							Name: "Decorations",
   152  							DashboardTab: []*configpb.DashboardTab{
   153  								{
   154  									Name:          "Living Room",
   155  									TestGroupName: "pumpkin-decorator",
   156  								},
   157  							},
   158  						},
   159  						{
   160  							Name: "Treats",
   161  							DashboardTab: []*configpb.DashboardTab{
   162  								{
   163  									Name:          "Candy",
   164  									TestGroupName: "candy-emitter",
   165  								},
   166  							},
   167  						},
   168  					},
   169  					DashboardGroups: []*configpb.DashboardGroup{
   170  						{
   171  							Name:           "To-Do",
   172  							DashboardNames: []string{"Treats", "Decorations"},
   173  						},
   174  					},
   175  				},
   176  				"crimbo": {
   177  					TestGroups: []*configpb.TestGroup{
   178  						aValidTestGroupNamed("tree-decorator"),
   179  						aValidTestGroupNamed("candy-emitter"),
   180  					},
   181  					Dashboards: []*configpb.Dashboard{
   182  						{
   183  							Name: "Decorations",
   184  							DashboardTab: []*configpb.DashboardTab{
   185  								{
   186  									Name:          "Living Room",
   187  									TestGroupName: "tree-decorator",
   188  								},
   189  							},
   190  						},
   191  						{
   192  							Name: "Treats",
   193  							DashboardTab: []*configpb.DashboardTab{
   194  								{
   195  									Name:          "Candy",
   196  									TestGroupName: "candy-emitter",
   197  								},
   198  							},
   199  						},
   200  					},
   201  					DashboardGroups: []*configpb.DashboardGroup{
   202  						{
   203  							Name:           "To-Do",
   204  							DashboardNames: []string{"Treats", "Decorations"},
   205  						},
   206  					},
   207  				},
   208  			},
   209  			expected: &configpb.Configuration{
   210  				TestGroups: []*configpb.TestGroup{
   211  					aValidTestGroupNamed("tree-decorator"),
   212  					aValidTestGroupNamed("candy-emitter"),
   213  					aValidTestGroupNamed("pumpkin-decorator"),
   214  					aValidTestGroupNamed("halloween-candy-emitter"),
   215  				},
   216  				Dashboards: []*configpb.Dashboard{
   217  					{
   218  						Name: "Decorations",
   219  						DashboardTab: []*configpb.DashboardTab{
   220  							{
   221  								Name:          "Living Room",
   222  								TestGroupName: "tree-decorator",
   223  							},
   224  						},
   225  					},
   226  					{
   227  						Name: "Treats",
   228  						DashboardTab: []*configpb.DashboardTab{
   229  							{
   230  								Name:          "Candy",
   231  								TestGroupName: "candy-emitter",
   232  							},
   233  						},
   234  					},
   235  					{
   236  						Name: "halloween-Decorations",
   237  						DashboardTab: []*configpb.DashboardTab{
   238  							{
   239  								Name:          "Living Room",
   240  								TestGroupName: "pumpkin-decorator",
   241  							},
   242  						},
   243  					},
   244  					{
   245  						Name: "halloween-Treats",
   246  						DashboardTab: []*configpb.DashboardTab{
   247  							{
   248  								Name:          "Candy",
   249  								TestGroupName: "halloween-candy-emitter",
   250  							},
   251  						},
   252  					},
   253  				},
   254  				DashboardGroups: []*configpb.DashboardGroup{
   255  					{
   256  						Name:           "To-Do",
   257  						DashboardNames: []string{"Treats", "Decorations"},
   258  					},
   259  					{
   260  						Name:           "halloween-To-Do",
   261  						DashboardNames: []string{"halloween-Treats", "halloween-Decorations"},
   262  					},
   263  				},
   264  			},
   265  		},
   266  	}
   267  
   268  	for _, testcase := range cases {
   269  		t.Run(testcase.name, func(t *testing.T) {
   270  			for iname, input := range testcase.inputs {
   271  				err := Validate(input)
   272  				if err != nil {
   273  					t.Logf("Warning! Input %s doesn't validate; the result might not either.", iname)
   274  					t.Logf("Validation error: %v", err)
   275  				}
   276  			}
   277  
   278  			result, err := Converge(testcase.inputs)
   279  
   280  			if testcase.expected != nil {
   281  				if err := Validate(result); err != nil {
   282  					t.Errorf("Result doesn't validate: %v", result)
   283  					t.Errorf("Validation error: %v", err)
   284  				}
   285  
   286  				if !proto.Equal(testcase.expected, result) {
   287  					t.Errorf("Expected %v, but got %v", testcase.expected, result)
   288  				}
   289  			} else {
   290  				if err == nil {
   291  					t.Errorf("Expected an error, but got none.")
   292  				}
   293  			}
   294  
   295  		})
   296  	}
   297  }
   298  
   299  func TestRenameTestGroup(t *testing.T) {
   300  	cases := []struct {
   301  		name     string
   302  		old      string
   303  		new      string
   304  		input    *configpb.Configuration
   305  		expected *configpb.Configuration
   306  	}{
   307  		{
   308  			name: "Old string isn't in TestGroup; no change",
   309  			old:  "foo",
   310  			new:  "bar",
   311  			input: &configpb.Configuration{
   312  				TestGroups: []*configpb.TestGroup{
   313  					{
   314  						Name: "foo-group",
   315  					},
   316  				},
   317  			},
   318  			expected: &configpb.Configuration{
   319  				TestGroups: []*configpb.TestGroup{
   320  					{
   321  						Name: "foo-group",
   322  					},
   323  				},
   324  			},
   325  		},
   326  		{
   327  			name: "Changes Dashboard Tab references",
   328  			old:  "foo",
   329  			new:  "bar",
   330  			input: &configpb.Configuration{
   331  				TestGroups: []*configpb.TestGroup{
   332  					{
   333  						Name: "foo",
   334  					},
   335  				},
   336  				Dashboards: []*configpb.Dashboard{
   337  					{
   338  						Name: "foo",
   339  						DashboardTab: []*configpb.DashboardTab{
   340  							{
   341  								Name:          "foo",
   342  								TestGroupName: "foo",
   343  							},
   344  						},
   345  					},
   346  				},
   347  			},
   348  			expected: &configpb.Configuration{
   349  				TestGroups: []*configpb.TestGroup{
   350  					{
   351  						Name: "bar",
   352  					},
   353  				},
   354  				Dashboards: []*configpb.Dashboard{
   355  					{
   356  						Name: "foo",
   357  						DashboardTab: []*configpb.DashboardTab{
   358  							{
   359  								Name:          "foo",
   360  								TestGroupName: "bar",
   361  							},
   362  						},
   363  					},
   364  				},
   365  			},
   366  		},
   367  	}
   368  
   369  	for _, testcase := range cases {
   370  		t.Run(testcase.name, func(t *testing.T) {
   371  			result := RenameTestGroup(testcase.old, testcase.new, testcase.input)
   372  
   373  			if !proto.Equal(testcase.expected, result) {
   374  				t.Errorf("Expected %v, but got %v", testcase.expected, result)
   375  			}
   376  		})
   377  	}
   378  }
   379  
   380  func TestRenameDashboard(t *testing.T) {
   381  	cases := []struct {
   382  		name     string
   383  		old      string
   384  		new      string
   385  		input    *configpb.Configuration
   386  		expected *configpb.Configuration
   387  	}{
   388  		{
   389  			name: "Old string isn't in Dashboard; do nothing",
   390  			old:  "foo",
   391  			new:  "bar",
   392  			input: &configpb.Configuration{
   393  				Dashboards: []*configpb.Dashboard{
   394  					{
   395  						Name: "foo-group",
   396  					},
   397  				},
   398  			},
   399  			expected: &configpb.Configuration{
   400  				Dashboards: []*configpb.Dashboard{
   401  					{
   402  						Name: "foo-group",
   403  					},
   404  				},
   405  			},
   406  		},
   407  		{
   408  			name: "Changes Dashboard Group reference",
   409  			old:  "foo",
   410  			new:  "bar",
   411  			input: &configpb.Configuration{
   412  				Dashboards: []*configpb.Dashboard{
   413  					{
   414  						Name: "foo",
   415  					},
   416  				},
   417  				DashboardGroups: []*configpb.DashboardGroup{
   418  					{
   419  						Name:           "foo",
   420  						DashboardNames: []string{"foo"},
   421  					},
   422  				},
   423  			},
   424  			expected: &configpb.Configuration{
   425  				Dashboards: []*configpb.Dashboard{
   426  					{
   427  						Name: "bar",
   428  					},
   429  				},
   430  				DashboardGroups: []*configpb.DashboardGroup{
   431  					{
   432  						Name:           "foo",
   433  						DashboardNames: []string{"bar"},
   434  					},
   435  				},
   436  			},
   437  		},
   438  	}
   439  
   440  	for _, testcase := range cases {
   441  		t.Run(testcase.name, func(t *testing.T) {
   442  			result := RenameDashboard(testcase.old, testcase.new, testcase.input)
   443  
   444  			if !proto.Equal(testcase.expected, result) {
   445  				t.Errorf("Expected %v, but got %v", testcase.expected, result)
   446  			}
   447  		})
   448  	}
   449  }
   450  
   451  func TestRenameDashboardGroup(t *testing.T) {
   452  	cases := []struct {
   453  		name     string
   454  		old      string
   455  		new      string
   456  		input    *configpb.Configuration
   457  		expected *configpb.Configuration
   458  	}{
   459  		{
   460  			name: "Old string isn't in Dashboard Group; do nothing",
   461  			old:  "foo",
   462  			new:  "bar",
   463  			input: &configpb.Configuration{
   464  				DashboardGroups: []*configpb.DashboardGroup{
   465  					{Name: "foo-foo"},
   466  				},
   467  			},
   468  			expected: &configpb.Configuration{
   469  				DashboardGroups: []*configpb.DashboardGroup{
   470  					{Name: "foo-foo"},
   471  				},
   472  			},
   473  		},
   474  		{
   475  			name: "Renames Dashboard Group",
   476  			old:  "foo",
   477  			new:  "bar",
   478  			input: &configpb.Configuration{
   479  				DashboardGroups: []*configpb.DashboardGroup{
   480  					{Name: "foo"},
   481  				},
   482  			},
   483  			expected: &configpb.Configuration{
   484  				DashboardGroups: []*configpb.DashboardGroup{
   485  					{Name: "bar"},
   486  				},
   487  			},
   488  		},
   489  	}
   490  
   491  	for _, testcase := range cases {
   492  		t.Run(testcase.name, func(t *testing.T) {
   493  			result := RenameDashboardGroup(testcase.old, testcase.new, testcase.input)
   494  
   495  			if !proto.Equal(testcase.expected, result) {
   496  				t.Errorf("Expected %v, but got %v", testcase.expected, result)
   497  			}
   498  		})
   499  	}
   500  }
   501  
   502  func TestNegotiateConversions(t *testing.T) {
   503  	cases := []struct {
   504  		Name     string
   505  		original []string
   506  		new      []string
   507  		expected map[string]string
   508  	}{
   509  		{
   510  			Name: "No sets; no conflicts",
   511  		},
   512  		{
   513  			Name:     "Sets with no conflict; no conflicts",
   514  			original: []string{"one", "two", "three"},
   515  			new:      []string{"un", "deux", "trois"},
   516  			expected: map[string]string{},
   517  		},
   518  		{
   519  			Name:     "Sets with conflicts; conflicts returned",
   520  			original: []string{"one", "two", "three"},
   521  			new:      []string{"three", "one", "four"},
   522  			expected: map[string]string{
   523  				"one":   "prefix-one",
   524  				"three": "prefix-three",
   525  			},
   526  		},
   527  		{
   528  			Name:     "Sets with conflicts with prefix; modified prefix returned",
   529  			original: []string{"ichi", "ni", "prefix-ichi"},
   530  			new:      []string{"ichi", "ni", "prefix-ni"},
   531  			expected: map[string]string{
   532  				"ichi": "prefix-2-ichi",
   533  				"ni":   "prefix-2-ni",
   534  			},
   535  		},
   536  	}
   537  
   538  	for _, test := range cases {
   539  		t.Run(test.Name, func(t *testing.T) {
   540  			originalMap := make(map[string]void)
   541  			for _, a := range test.original {
   542  				originalMap[a] = insert
   543  			}
   544  
   545  			newMap := make(map[string]void)
   546  			for _, a := range test.new {
   547  				newMap[a] = insert
   548  			}
   549  
   550  			result := negotiateConversions("prefix", originalMap, newMap)
   551  
   552  			if !reflect.DeepEqual(test.expected, result) {
   553  				t.Errorf("Expected %v, but got %v", test.expected, result)
   554  			}
   555  		})
   556  	}
   557  }
   558  
   559  func aValidTestGroupNamed(name string) *configpb.TestGroup {
   560  	return &configpb.TestGroup{
   561  		Name:             name,
   562  		GcsPrefix:        "gcs://some/path",
   563  		DaysOfResults:    1,
   564  		NumColumnsRecent: 3,
   565  	}
   566  }