github.com/yrj2011/jx-test-infra@v0.0.0-20190529031832-7a2065ee98eb/prow/config/tide_test.go (about) 1 /* 2 Copyright 2017 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 "strings" 22 "testing" 23 "time" 24 25 "k8s.io/apimachinery/pkg/util/sets" 26 "k8s.io/test-infra/prow/github" 27 ) 28 29 var testQuery = TideQuery{ 30 Orgs: []string{"org"}, 31 Repos: []string{"k/k", "k/t-i"}, 32 Labels: []string{"lgtm", "approved"}, 33 MissingLabels: []string{"foo"}, 34 Milestone: "milestone", 35 ReviewApprovedRequired: true, 36 } 37 38 func TestTideQuery(t *testing.T) { 39 q := " " + testQuery.Query() + " " 40 checkTok := func(tok string) { 41 if !strings.Contains(q, " "+tok+" ") { 42 t.Errorf("Expected query to contain \"%s\", got \"%s\"", tok, q) 43 } 44 } 45 46 checkTok("is:pr") 47 checkTok("state:open") 48 checkTok("org:\"org\"") 49 checkTok("repo:\"k/k\"") 50 checkTok("repo:\"k/t-i\"") 51 checkTok("label:\"lgtm\"") 52 checkTok("label:\"approved\"") 53 checkTok("-label:\"foo\"") 54 checkTok("milestone:\"milestone\"") 55 checkTok("review:approved") 56 } 57 58 func TestAllPRsSince(t *testing.T) { 59 testTime, err := time.Parse(time.UnixDate, "Sat Mar 7 11:06:39 PST 2015") 60 if err != nil { 61 t.Fatalf("Error parsing test time string: %v.", err) 62 } 63 testTimeOld, err := time.Parse(time.UnixDate, "Sat Mar 7 11:06:39 PST 1915") 64 if err != nil { 65 t.Fatalf("Error parsing test time string: %v.", err) 66 } 67 var q string 68 checkTok := func(tok string, shouldExist bool) { 69 if shouldExist == strings.Contains(q, " "+tok+" ") { 70 return 71 } else if shouldExist { 72 t.Errorf("Expected query to contain \"%s\", got \"%s\"", tok, q) 73 } else { 74 t.Errorf("Expected query to not contain \"%s\", got \"%s\"", tok, q) 75 76 } 77 } 78 79 queries := TideQueries([]TideQuery{ 80 testQuery, 81 { 82 Orgs: []string{"foo"}, 83 Repos: []string{"k/foo"}, 84 Labels: []string{"lgtm", "mergeable"}, 85 }, 86 }) 87 q = " " + queries.AllPRsSince(testTime) + " " 88 checkTok("is:pr", true) 89 checkTok("state:open", true) 90 checkTok("org:\"org\"", true) 91 checkTok("org:\"foo\"", true) 92 checkTok("repo:\"k/k\"", true) 93 checkTok("repo:\"k/t-i\"", true) 94 checkTok("repo:\"k/foo\"", true) 95 checkTok("label:\"lgtm\"", false) 96 checkTok("label:\"approved\"", false) 97 checkTok("label:\"mergeable\"", false) 98 checkTok("-label:\"foo\"", false) 99 checkTok("milestone:\"milestone\"", false) 100 checkTok("review:approved", false) 101 checkTok("updated:>=2015-03-07T11:06:39Z", true) 102 103 // Test that if time is the zero time value, the token is not included. 104 q = " " + queries.AllPRsSince(time.Time{}) + " " 105 checkTok("updated:>=0001-01-01T00:00:00Z", false) 106 // Test that if time is before 1970, the token is not included. 107 q = " " + queries.AllPRsSince(testTimeOld) + " " 108 checkTok("updated:>=1915-03-07T11:06:39Z", false) 109 } 110 111 func TestMergeMethod(t *testing.T) { 112 ti := &Tide{ 113 MergeType: map[string]github.PullRequestMergeType{ 114 "kubernetes/kops": github.MergeRebase, 115 "kubernetes/charts": github.MergeSquash, 116 "helm/charts": github.MergeSquash, 117 "kubernetes-helm": github.MergeSquash, 118 "kubernetes-helm/chartmuseum": github.MergeMerge, 119 }, 120 } 121 122 var testcases = []struct { 123 org string 124 repo string 125 expected github.PullRequestMergeType 126 }{ 127 { 128 "kubernetes", 129 "kubernetes", 130 github.MergeMerge, 131 }, 132 { 133 "kubernetes", 134 "kops", 135 github.MergeRebase, 136 }, 137 { 138 "kubernetes", 139 "charts", 140 github.MergeSquash, 141 }, 142 { 143 "kubernetes-helm", 144 "monocular", 145 github.MergeSquash, 146 }, 147 { 148 "kubernetes-helm", 149 "chartmuseum", 150 github.MergeMerge, 151 }, 152 } 153 154 for _, test := range testcases { 155 if ti.MergeMethod(test.org, test.repo) != test.expected { 156 t.Errorf("Expected merge method %q but got %q for %s/%s", test.expected, ti.MergeMethod(test.org, test.repo), test.org, test.repo) 157 } 158 } 159 } 160 161 func TestParseTideContextPolicyOptions(t *testing.T) { 162 yes := true 163 no := false 164 org, repo, branch := "org", "repo", "branch" 165 testCases := []struct { 166 name string 167 config TideContextPolicyOptions 168 expected TideContextPolicy 169 }{ 170 { 171 name: "empty", 172 }, 173 { 174 name: "global config", 175 config: TideContextPolicyOptions{ 176 TideContextPolicy: TideContextPolicy{ 177 FromBranchProtection: &yes, 178 SkipUnknownContexts: &yes, 179 RequiredContexts: []string{"r1"}, 180 OptionalContexts: []string{"o1"}, 181 }, 182 }, 183 expected: TideContextPolicy{ 184 SkipUnknownContexts: &yes, 185 RequiredContexts: []string{"r1"}, 186 OptionalContexts: []string{"o1"}, 187 FromBranchProtection: &yes, 188 }, 189 }, 190 { 191 name: "org config", 192 config: TideContextPolicyOptions{ 193 TideContextPolicy: TideContextPolicy{ 194 RequiredContexts: []string{"r1"}, 195 OptionalContexts: []string{"o1"}, 196 FromBranchProtection: &no, 197 }, 198 Orgs: map[string]TideOrgContextPolicy{ 199 "org": { 200 TideContextPolicy: TideContextPolicy{ 201 SkipUnknownContexts: &yes, 202 RequiredContexts: []string{"r2"}, 203 OptionalContexts: []string{"o2"}, 204 FromBranchProtection: &yes, 205 }, 206 }, 207 }, 208 }, 209 expected: TideContextPolicy{ 210 SkipUnknownContexts: &yes, 211 RequiredContexts: []string{"r1", "r2"}, 212 OptionalContexts: []string{"o1", "o2"}, 213 FromBranchProtection: &yes, 214 }, 215 }, 216 { 217 name: "repo config", 218 config: TideContextPolicyOptions{ 219 TideContextPolicy: TideContextPolicy{ 220 RequiredContexts: []string{"r1"}, 221 OptionalContexts: []string{"o1"}, 222 FromBranchProtection: &no, 223 }, 224 Orgs: map[string]TideOrgContextPolicy{ 225 "org": { 226 TideContextPolicy: TideContextPolicy{ 227 SkipUnknownContexts: &no, 228 RequiredContexts: []string{"r2"}, 229 OptionalContexts: []string{"o2"}, 230 FromBranchProtection: &no, 231 }, 232 Repos: map[string]TideRepoContextPolicy{ 233 "repo": { 234 TideContextPolicy: TideContextPolicy{ 235 SkipUnknownContexts: &yes, 236 RequiredContexts: []string{"r3"}, 237 OptionalContexts: []string{"o3"}, 238 FromBranchProtection: &yes, 239 }, 240 }, 241 }, 242 }, 243 }, 244 }, 245 expected: TideContextPolicy{ 246 SkipUnknownContexts: &yes, 247 RequiredContexts: []string{"r1", "r2", "r3"}, 248 OptionalContexts: []string{"o1", "o2", "o3"}, 249 FromBranchProtection: &yes, 250 }, 251 }, 252 { 253 name: "branch config", 254 config: TideContextPolicyOptions{ 255 TideContextPolicy: TideContextPolicy{ 256 RequiredContexts: []string{"r1"}, 257 OptionalContexts: []string{"o1"}, 258 }, 259 Orgs: map[string]TideOrgContextPolicy{ 260 "org": { 261 TideContextPolicy: TideContextPolicy{ 262 RequiredContexts: []string{"r2"}, 263 OptionalContexts: []string{"o2"}, 264 }, 265 Repos: map[string]TideRepoContextPolicy{ 266 "repo": { 267 TideContextPolicy: TideContextPolicy{ 268 RequiredContexts: []string{"r3"}, 269 OptionalContexts: []string{"o3"}, 270 }, 271 Branches: map[string]TideContextPolicy{ 272 "branch": { 273 RequiredContexts: []string{"r4"}, 274 OptionalContexts: []string{"o4"}, 275 }, 276 }, 277 }, 278 }, 279 }, 280 }, 281 }, 282 expected: TideContextPolicy{ 283 RequiredContexts: []string{"r1", "r2", "r3", "r4"}, 284 OptionalContexts: []string{"o1", "o2", "o3", "o4"}, 285 }, 286 }, 287 } 288 for _, tc := range testCases { 289 policy := parseTideContextPolicyOptions(org, repo, branch, tc.config) 290 if !reflect.DeepEqual(policy, tc.expected) { 291 t.Errorf("%s - expected %v got %v", tc.name, tc.expected, policy) 292 } 293 } 294 } 295 296 func TestConfigGetTideContextPolicy(t *testing.T) { 297 yes := true 298 no := false 299 org, repo, branch := "org", "repo", "branch" 300 testCases := []struct { 301 name string 302 config Config 303 expected TideContextPolicy 304 error string 305 }{ 306 { 307 name: "no policy - use prow jobs", 308 config: Config{ 309 ProwConfig: ProwConfig{ 310 BranchProtection: BranchProtection{ 311 Policy: Policy{ 312 Protect: &yes, 313 RequiredStatusChecks: &ContextPolicy{ 314 Contexts: []string{"r1", "r2"}, 315 }, 316 }, 317 }, 318 }, 319 JobConfig: JobConfig{ 320 Presubmits: map[string][]Presubmit{ 321 "org/repo": { 322 Presubmit{ 323 Context: "pr1", 324 AlwaysRun: true, 325 }, 326 Presubmit{ 327 Context: "po1", 328 AlwaysRun: true, 329 Optional: true, 330 }, 331 }, 332 }, 333 }, 334 }, 335 expected: TideContextPolicy{ 336 RequiredContexts: []string{"pr1"}, 337 OptionalContexts: []string{"po1"}, 338 }, 339 }, 340 { 341 name: "no policy no prow jobs defined - empty", 342 config: Config{ 343 ProwConfig: ProwConfig{ 344 BranchProtection: BranchProtection{ 345 Policy: Policy{ 346 Protect: &yes, 347 RequiredStatusChecks: &ContextPolicy{ 348 Contexts: []string{"r1", "r2"}, 349 }, 350 }, 351 }, 352 }, 353 }, 354 expected: TideContextPolicy{ 355 RequiredContexts: []string{}, 356 OptionalContexts: []string{}, 357 }, 358 }, 359 { 360 name: "no branch protection", 361 config: Config{ 362 ProwConfig: ProwConfig{ 363 Tide: Tide{ 364 ContextOptions: TideContextPolicyOptions{ 365 TideContextPolicy: TideContextPolicy{ 366 FromBranchProtection: &yes, 367 }, 368 }, 369 }, 370 }, 371 }, 372 expected: TideContextPolicy{ 373 RequiredContexts: []string{}, 374 OptionalContexts: []string{}, 375 }, 376 }, 377 { 378 name: "invalid branch protection", 379 config: Config{ 380 ProwConfig: ProwConfig{ 381 BranchProtection: BranchProtection{ 382 Orgs: map[string]Org{ 383 "org": { 384 Policy: Policy{ 385 Protect: &no, 386 }, 387 }, 388 }, 389 }, 390 Tide: Tide{ 391 ContextOptions: TideContextPolicyOptions{ 392 TideContextPolicy: TideContextPolicy{ 393 FromBranchProtection: &yes, 394 }, 395 }, 396 }, 397 }, 398 }, 399 expected: TideContextPolicy{ 400 RequiredContexts: []string{}, 401 OptionalContexts: []string{}, 402 }, 403 }, 404 { 405 name: "manually defined policy", 406 config: Config{ 407 ProwConfig: ProwConfig{ 408 Tide: Tide{ 409 ContextOptions: TideContextPolicyOptions{ 410 TideContextPolicy: TideContextPolicy{ 411 RequiredContexts: []string{"r1"}, 412 OptionalContexts: []string{"o1"}, 413 SkipUnknownContexts: &yes, 414 }, 415 }, 416 }, 417 }, 418 }, 419 expected: TideContextPolicy{ 420 RequiredContexts: []string{"r1"}, 421 OptionalContexts: []string{"o1"}, 422 SkipUnknownContexts: &yes, 423 }, 424 }, 425 } 426 427 for _, tc := range testCases { 428 p, err := tc.config.GetTideContextPolicy(org, repo, branch) 429 if !reflect.DeepEqual(p, &tc.expected) { 430 t.Errorf("%s - expected contexts %v got %v", tc.name, &tc.expected, p) 431 } 432 if err != nil { 433 if err.Error() != tc.error { 434 t.Errorf("%s - expected error %v got %v", tc.name, tc.error, err.Error()) 435 } 436 } else if tc.error != "" { 437 t.Errorf("%s - expected error %v got nil", tc.name, tc.error) 438 } 439 } 440 } 441 442 func TestMergeTideContextPolicyConfig(t *testing.T) { 443 yes := true 444 no := false 445 testCases := []struct { 446 name string 447 a, b, c TideContextPolicy 448 }{ 449 { 450 name: "all empty", 451 }, 452 { 453 name: "empty a", 454 b: TideContextPolicy{ 455 SkipUnknownContexts: &yes, 456 FromBranchProtection: &no, 457 RequiredContexts: []string{"r1"}, 458 OptionalContexts: []string{"o1"}, 459 }, 460 c: TideContextPolicy{ 461 SkipUnknownContexts: &yes, 462 FromBranchProtection: &no, 463 RequiredContexts: []string{"r1"}, 464 OptionalContexts: []string{"o1"}, 465 }, 466 }, 467 { 468 name: "empty b", 469 a: TideContextPolicy{ 470 SkipUnknownContexts: &yes, 471 FromBranchProtection: &no, 472 RequiredContexts: []string{"r1"}, 473 OptionalContexts: []string{"o1"}, 474 }, 475 c: TideContextPolicy{ 476 SkipUnknownContexts: &yes, 477 FromBranchProtection: &no, 478 RequiredContexts: []string{"r1"}, 479 OptionalContexts: []string{"o1"}, 480 }, 481 }, 482 { 483 name: "merging unset boolean", 484 a: TideContextPolicy{ 485 FromBranchProtection: &no, 486 RequiredContexts: []string{"r1"}, 487 OptionalContexts: []string{"o1"}, 488 }, 489 b: TideContextPolicy{ 490 SkipUnknownContexts: &yes, 491 RequiredContexts: []string{"r2"}, 492 OptionalContexts: []string{"o2"}, 493 }, 494 c: TideContextPolicy{ 495 SkipUnknownContexts: &yes, 496 FromBranchProtection: &no, 497 RequiredContexts: []string{"r1", "r2"}, 498 OptionalContexts: []string{"o1", "o2"}, 499 }, 500 }, 501 { 502 name: "merging unset contexts in a", 503 a: TideContextPolicy{ 504 FromBranchProtection: &no, 505 SkipUnknownContexts: &yes, 506 }, 507 b: TideContextPolicy{ 508 FromBranchProtection: &yes, 509 SkipUnknownContexts: &no, 510 RequiredContexts: []string{"r1"}, 511 OptionalContexts: []string{"o1"}, 512 }, 513 c: TideContextPolicy{ 514 FromBranchProtection: &yes, 515 SkipUnknownContexts: &no, 516 RequiredContexts: []string{"r1"}, 517 OptionalContexts: []string{"o1"}, 518 }, 519 }, 520 { 521 name: "merging unset contexts in b", 522 a: TideContextPolicy{ 523 FromBranchProtection: &yes, 524 SkipUnknownContexts: &no, 525 RequiredContexts: []string{"r1"}, 526 OptionalContexts: []string{"o1"}, 527 }, 528 b: TideContextPolicy{ 529 FromBranchProtection: &no, 530 SkipUnknownContexts: &yes, 531 }, 532 c: TideContextPolicy{ 533 FromBranchProtection: &no, 534 SkipUnknownContexts: &yes, 535 RequiredContexts: []string{"r1"}, 536 OptionalContexts: []string{"o1"}, 537 }, 538 }, 539 } 540 541 for _, tc := range testCases { 542 c := mergeTideContextPolicy(tc.a, tc.b) 543 if !reflect.DeepEqual(c, tc.c) { 544 t.Errorf("%s - expected %v got %v", tc.name, tc.c, c) 545 } 546 } 547 } 548 549 func TestTideQuery_Validate(t *testing.T) { 550 testCases := []struct { 551 name string 552 t TideContextPolicy 553 failed bool 554 }{ 555 { 556 name: "good policy", 557 t: TideContextPolicy{ 558 OptionalContexts: []string{"o1"}, 559 RequiredContexts: []string{"r1"}, 560 }, 561 }, 562 { 563 name: "good policy", 564 t: TideContextPolicy{ 565 OptionalContexts: []string{"c1"}, 566 RequiredContexts: []string{"c1"}, 567 }, 568 failed: true, 569 }, 570 { 571 name: "good policy", 572 t: TideContextPolicy{ 573 OptionalContexts: []string{"c1", "c2", "c3", "c4"}, 574 RequiredContexts: []string{"c1", "c4"}, 575 }, 576 failed: true, 577 }, 578 } 579 for _, tc := range testCases { 580 err := tc.t.Validate() 581 failed := err != nil 582 if failed != tc.failed { 583 t.Errorf("%s - expected %v got %v", tc.name, tc.failed, err) 584 } 585 } 586 } 587 588 func TestTideContextPolicy_IsOptional(t *testing.T) { 589 testCases := []struct { 590 name string 591 skipUnknownContexts bool 592 required, optional []string 593 contexts []string 594 results []bool 595 }{ 596 { 597 name: "only optional contexts registered - skipUnknownContexts false", 598 contexts: []string{"c1", "o1", "o2"}, 599 optional: []string{"o1", "o2"}, 600 results: []bool{false, true, true}, 601 }, 602 { 603 name: "no contexts registered - skipUnknownContexts false", 604 contexts: []string{"t2"}, 605 results: []bool{false}, 606 }, 607 { 608 name: "only required contexts registered - skipUnknownContexts false", 609 required: []string{"c1", "c2", "c3"}, 610 contexts: []string{"c1", "c2", "c3", "t1"}, 611 results: []bool{false, false, false, false}, 612 }, 613 { 614 name: "optional and required contexts registered - skipUnknownContexts false", 615 optional: []string{"o1", "o2"}, 616 required: []string{"c1", "c2", "c3"}, 617 contexts: []string{"o1", "o2", "c1", "c2", "c3", "t1"}, 618 results: []bool{true, true, false, false, false, false}, 619 }, 620 { 621 name: "only optional contexts registered - skipUnknownContexts true", 622 contexts: []string{"c1", "o1", "o2"}, 623 optional: []string{"o1", "o2"}, 624 skipUnknownContexts: true, 625 results: []bool{true, true, true}, 626 }, 627 { 628 name: "no contexts registered - skipUnknownContexts true", 629 contexts: []string{"t2"}, 630 skipUnknownContexts: true, 631 results: []bool{true}, 632 }, 633 { 634 name: "only required contexts registered - skipUnknownContexts true", 635 required: []string{"c1", "c2", "c3"}, 636 contexts: []string{"c1", "c2", "c3", "t1"}, 637 skipUnknownContexts: true, 638 results: []bool{false, false, false, true}, 639 }, 640 { 641 name: "optional and required contexts registered - skipUnknownContexts true", 642 optional: []string{"o1", "o2"}, 643 required: []string{"c1", "c2", "c3"}, 644 contexts: []string{"o1", "o2", "c1", "c2", "c3", "t1"}, 645 skipUnknownContexts: true, 646 results: []bool{true, true, false, false, false, true}, 647 }, 648 } 649 650 for _, tc := range testCases { 651 cp := TideContextPolicy{ 652 SkipUnknownContexts: &tc.skipUnknownContexts, 653 RequiredContexts: tc.required, 654 OptionalContexts: tc.optional, 655 } 656 for i, c := range tc.contexts { 657 if cp.IsOptional(c) != tc.results[i] { 658 t.Errorf("%s - IsOptional for %s should return %t", tc.name, c, tc.results[i]) 659 } 660 } 661 } 662 } 663 664 func TestTideContextPolicy_MissingRequiredContexts(t *testing.T) { 665 testCases := []struct { 666 name string 667 skipUnknownContexts bool 668 required, optional []string 669 existingContexts, expectedContexts []string 670 }{ 671 { 672 name: "no contexts registered", 673 existingContexts: []string{"c1", "c2"}, 674 }, 675 { 676 name: "optional contexts registered / no missing contexts", 677 optional: []string{"o1", "o2", "o3"}, 678 existingContexts: []string{"c1", "c2"}, 679 }, 680 { 681 name: "required contexts registered / missing contexts", 682 required: []string{"c1", "c2", "c3"}, 683 existingContexts: []string{"c1", "c2"}, 684 expectedContexts: []string{"c3"}, 685 }, 686 { 687 name: "required contexts registered / no missing contexts", 688 required: []string{"c1", "c2", "c3"}, 689 existingContexts: []string{"c1", "c2", "c3"}, 690 }, 691 { 692 name: "optional and required contexts registered / missing contexts", 693 optional: []string{"o1", "o2", "o3"}, 694 required: []string{"c1", "c2", "c3"}, 695 existingContexts: []string{"c1", "c2"}, 696 expectedContexts: []string{"c3"}, 697 }, 698 { 699 name: "optional and required contexts registered / no missing contexts", 700 optional: []string{"o1", "o2", "o3"}, 701 required: []string{"c1", "c2"}, 702 existingContexts: []string{"c1", "c2", "c4"}, 703 }, 704 } 705 706 for _, tc := range testCases { 707 cp := TideContextPolicy{ 708 SkipUnknownContexts: &tc.skipUnknownContexts, 709 RequiredContexts: tc.required, 710 OptionalContexts: tc.optional, 711 } 712 missingContexts := cp.MissingRequiredContexts(tc.existingContexts) 713 if !sets.NewString(missingContexts...).Equal(sets.NewString(tc.expectedContexts...)) { 714 t.Errorf("%s - expected %v got %v", tc.name, tc.expectedContexts, missingContexts) 715 } 716 } 717 }