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 }