github.com/google/go-github/v64@v64.0.0/github/repos_rules_test.go (about) 1 // Copyright 2023 The go-github AUTHORS. All rights reserved. 2 // 3 // Use of this source code is governed by a BSD-style 4 // license that can be found in the LICENSE file. 5 6 package github 7 8 import ( 9 "context" 10 "fmt" 11 "net/http" 12 "testing" 13 14 "github.com/google/go-cmp/cmp" 15 ) 16 17 func TestRepositoryRule_UnmarshalJSON(t *testing.T) { 18 tests := map[string]struct { 19 data string 20 want *RepositoryRule 21 wantErr bool 22 }{ 23 "Invalid JSON": { 24 data: `{`, 25 want: &RepositoryRule{ 26 Type: "", 27 Parameters: nil, 28 }, 29 wantErr: true, 30 }, 31 "With Metadata": { 32 data: `{ 33 "type": "creation", 34 "ruleset_source_type": "Repository", 35 "ruleset_source": "google", 36 "ruleset_id": 1984 37 }`, 38 want: &RepositoryRule{ 39 RulesetSource: "google", 40 RulesetSourceType: "Repository", 41 RulesetID: 1984, 42 Type: "creation", 43 }, 44 }, 45 "Valid creation": { 46 data: `{"type":"creation"}`, 47 want: NewCreationRule(), 48 }, 49 "Valid deletion": { 50 data: `{"type":"deletion"}`, 51 want: &RepositoryRule{ 52 Type: "deletion", 53 Parameters: nil, 54 }, 55 }, 56 "Valid required_linear_history": { 57 data: `{"type":"required_linear_history"}`, 58 want: &RepositoryRule{ 59 Type: "required_linear_history", 60 Parameters: nil, 61 }, 62 }, 63 "Valid required_signatures": { 64 data: `{"type":"required_signatures"}`, 65 want: &RepositoryRule{ 66 Type: "required_signatures", 67 Parameters: nil, 68 }, 69 }, 70 "Valid merge_queue": { 71 data: `{"type":"merge_queue"}`, 72 want: &RepositoryRule{ 73 Type: "merge_queue", 74 Parameters: nil, 75 }, 76 }, 77 "Valid non_fast_forward": { 78 data: `{"type":"non_fast_forward"}`, 79 want: &RepositoryRule{ 80 Type: "non_fast_forward", 81 Parameters: nil, 82 }, 83 }, 84 "Valid update params": { 85 data: `{"type":"update","parameters":{"update_allows_fetch_and_merge":true}}`, 86 want: NewUpdateRule(&UpdateAllowsFetchAndMergeRuleParameters{UpdateAllowsFetchAndMerge: true}), 87 }, 88 "Invalid update params": { 89 data: `{"type":"update","parameters":{"update_allows_fetch_and_merge":"true"}}`, 90 want: &RepositoryRule{ 91 Type: "update", 92 Parameters: nil, 93 }, 94 wantErr: true, 95 }, 96 "Valid required_deployments params": { 97 data: `{"type":"required_deployments","parameters":{"required_deployment_environments":["test"]}}`, 98 want: NewRequiredDeploymentsRule(&RequiredDeploymentEnvironmentsRuleParameters{ 99 RequiredDeploymentEnvironments: []string{"test"}, 100 }), 101 }, 102 "Invalid required_deployments params": { 103 data: `{"type":"required_deployments","parameters":{"required_deployment_environments":true}}`, 104 want: &RepositoryRule{ 105 Type: "required_deployments", 106 Parameters: nil, 107 }, 108 wantErr: true, 109 }, 110 "Valid commit_message_pattern params": { 111 data: `{"type":"commit_message_pattern","parameters":{"operator":"starts_with","pattern":"github"}}`, 112 want: NewCommitMessagePatternRule(&RulePatternParameters{ 113 Operator: "starts_with", 114 Pattern: "github", 115 }), 116 }, 117 "Invalid commit_message_pattern params": { 118 data: `{"type":"commit_message_pattern","parameters":{"operator":"starts_with","pattern":1}}`, 119 want: &RepositoryRule{ 120 Type: "commit_message_pattern", 121 Parameters: nil, 122 }, 123 wantErr: true, 124 }, 125 "Valid commit_author_email_pattern params": { 126 data: `{"type":"commit_author_email_pattern","parameters":{"operator":"starts_with","pattern":"github"}}`, 127 want: NewCommitAuthorEmailPatternRule(&RulePatternParameters{ 128 Operator: "starts_with", 129 Pattern: "github", 130 }), 131 }, 132 "Invalid commit_author_email_pattern params": { 133 data: `{"type":"commit_author_email_pattern","parameters":{"operator":"starts_with","pattern":1}}`, 134 want: &RepositoryRule{ 135 Type: "commit_author_email_pattern", 136 Parameters: nil, 137 }, 138 wantErr: true, 139 }, 140 "Valid committer_email_pattern params": { 141 data: `{"type":"committer_email_pattern","parameters":{"operator":"starts_with","pattern":"github"}}`, 142 want: NewCommitterEmailPatternRule(&RulePatternParameters{ 143 Operator: "starts_with", 144 Pattern: "github", 145 }), 146 }, 147 "Invalid committer_email_pattern params": { 148 data: `{"type":"committer_email_pattern","parameters":{"operator":"starts_with","pattern":1}}`, 149 want: &RepositoryRule{ 150 Type: "committer_email_pattern", 151 Parameters: nil, 152 }, 153 wantErr: true, 154 }, 155 "Valid branch_name_pattern params": { 156 data: `{"type":"branch_name_pattern","parameters":{"operator":"starts_with","pattern":"github"}}`, 157 want: NewBranchNamePatternRule(&RulePatternParameters{ 158 Operator: "starts_with", 159 Pattern: "github", 160 }), 161 }, 162 "Invalid branch_name_pattern params": { 163 data: `{"type":"branch_name_pattern","parameters":{"operator":"starts_with","pattern":1}}`, 164 want: &RepositoryRule{ 165 Type: "branch_name_pattern", 166 Parameters: nil, 167 }, 168 wantErr: true, 169 }, 170 "Valid tag_name_pattern params": { 171 data: `{"type":"tag_name_pattern","parameters":{"operator":"starts_with","pattern":"github"}}`, 172 want: NewTagNamePatternRule(&RulePatternParameters{ 173 Operator: "starts_with", 174 Pattern: "github", 175 }), 176 }, 177 "Invalid tag_name_pattern params": { 178 data: `{"type":"tag_name_pattern","parameters":{"operator":"starts_with","pattern":1}}`, 179 want: &RepositoryRule{ 180 Type: "tag_name_pattern", 181 Parameters: nil, 182 }, 183 wantErr: true, 184 }, 185 "Valid file_path_restriction params": { 186 data: `{"type":"file_path_restriction","parameters":{"restricted_file_paths":["/a/file"]}}`, 187 want: NewFilePathRestrictionRule(&RuleFileParameters{ 188 RestrictedFilePaths: &[]string{"/a/file"}, 189 }), 190 }, 191 "Invalid file_path_restriction params": { 192 data: `{"type":"file_path_restriction","parameters":{"restricted_file_paths":true}}`, 193 want: &RepositoryRule{ 194 Type: "file_path_restriction", 195 Parameters: nil, 196 }, 197 wantErr: true, 198 }, 199 "Valid pull_request params": { 200 data: `{ 201 "type":"pull_request", 202 "parameters":{ 203 "dismiss_stale_reviews_on_push": true, 204 "require_code_owner_review": true, 205 "require_last_push_approval": true, 206 "required_approving_review_count": 1, 207 "required_review_thread_resolution":true 208 } 209 }`, 210 want: NewPullRequestRule(&PullRequestRuleParameters{ 211 DismissStaleReviewsOnPush: true, 212 RequireCodeOwnerReview: true, 213 RequireLastPushApproval: true, 214 RequiredApprovingReviewCount: 1, 215 RequiredReviewThreadResolution: true, 216 }), 217 }, 218 "Invalid pull_request params": { 219 data: `{"type":"pull_request","parameters": {"dismiss_stale_reviews_on_push":"true"}}`, 220 want: &RepositoryRule{ 221 Type: "pull_request", 222 Parameters: nil, 223 }, 224 wantErr: true, 225 }, 226 "Valid required_status_checks params": { 227 data: `{"type":"required_status_checks","parameters":{"required_status_checks":[{"context":"test","integration_id":1}],"strict_required_status_checks_policy":true}}`, 228 want: NewRequiredStatusChecksRule(&RequiredStatusChecksRuleParameters{ 229 RequiredStatusChecks: []RuleRequiredStatusChecks{ 230 { 231 Context: "test", 232 IntegrationID: Int64(1), 233 }, 234 }, 235 StrictRequiredStatusChecksPolicy: true, 236 }), 237 }, 238 "Invalid required_status_checks params": { 239 data: `{"type":"required_status_checks", 240 "parameters": { 241 "required_status_checks": [ 242 { 243 "context": 1 244 } 245 ] 246 }}`, 247 want: &RepositoryRule{ 248 Type: "required_status_checks", 249 Parameters: nil, 250 }, 251 wantErr: true, 252 }, 253 "Required workflows params": { 254 data: `{"type":"workflows","parameters":{"workflows":[{"path": ".github/workflows/test.yml", "repository_id": 1}]}}`, 255 want: NewRequiredWorkflowsRule(&RequiredWorkflowsRuleParameters{ 256 RequiredWorkflows: []*RuleRequiredWorkflow{ 257 { 258 Path: ".github/workflows/test.yml", 259 RepositoryID: Int64(1), 260 }, 261 }, 262 }), 263 }, 264 "Invalid type": { 265 data: `{"type":"unknown"}`, 266 want: &RepositoryRule{ 267 Type: "", 268 Parameters: nil, 269 }, 270 wantErr: true, 271 }, 272 } 273 274 for name, tc := range tests { 275 rule := &RepositoryRule{} 276 277 t.Run(name, func(t *testing.T) { 278 err := rule.UnmarshalJSON([]byte(tc.data)) 279 if err == nil && tc.wantErr { 280 t.Errorf("RepositoryRule.UnmarshalJSON returned nil instead of an error") 281 } 282 if err != nil && !tc.wantErr { 283 t.Errorf("RepositoryRule.UnmarshalJSON returned an unexpected error: %+v", err) 284 } 285 if !cmp.Equal(tc.want, rule) { 286 t.Errorf("RepositoryRule.UnmarshalJSON expected rule %+v, got %+v", tc.want, rule) 287 } 288 }) 289 } 290 } 291 292 func TestRepositoriesService_GetRulesForBranch(t *testing.T) { 293 client, mux, _, teardown := setup() 294 defer teardown() 295 296 mux.HandleFunc("/repos/o/repo/rules/branches/branch", func(w http.ResponseWriter, r *http.Request) { 297 testMethod(t, r, "GET") 298 fmt.Fprint(w, `[ 299 { 300 "ruleset_id": 42069, 301 "ruleset_source_type": "Repository", 302 "ruleset_source": "google", 303 "type": "creation" 304 }, 305 { 306 "ruleset_id": 42069, 307 "ruleset_source_type": "Organization", 308 "ruleset_source": "google", 309 "type": "update", 310 "parameters": { 311 "update_allows_fetch_and_merge": true 312 } 313 } 314 ]`) 315 }) 316 317 ctx := context.Background() 318 rules, _, err := client.Repositories.GetRulesForBranch(ctx, "o", "repo", "branch") 319 if err != nil { 320 t.Errorf("Repositories.GetRulesForBranch returned error: %v", err) 321 } 322 323 creationRule := NewCreationRule() 324 creationRule.RulesetID = 42069 325 creationRule.RulesetSource = "google" 326 creationRule.RulesetSourceType = "Repository" 327 updateRule := NewUpdateRule(&UpdateAllowsFetchAndMergeRuleParameters{ 328 UpdateAllowsFetchAndMerge: true, 329 }) 330 updateRule.RulesetID = 42069 331 updateRule.RulesetSource = "google" 332 updateRule.RulesetSourceType = "Organization" 333 334 want := []*RepositoryRule{ 335 creationRule, 336 updateRule, 337 } 338 if !cmp.Equal(rules, want) { 339 t.Errorf("Repositories.GetRulesForBranch returned %+v, want %+v", rules, want) 340 } 341 342 const methodName = "GetRulesForBranch" 343 344 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 345 got, resp, err := client.Repositories.GetRulesForBranch(ctx, "o", "repo", "branch") 346 if got != nil { 347 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 348 } 349 return resp, err 350 }) 351 } 352 353 func TestRepositoriesService_GetRulesForBranchEmptyUpdateRule(t *testing.T) { 354 client, mux, _, teardown := setup() 355 defer teardown() 356 357 mux.HandleFunc("/repos/o/repo/rules/branches/branch", func(w http.ResponseWriter, r *http.Request) { 358 testMethod(t, r, "GET") 359 fmt.Fprint(w, `[ 360 { 361 "type": "update" 362 } 363 ]`) 364 }) 365 366 ctx := context.Background() 367 rules, _, err := client.Repositories.GetRulesForBranch(ctx, "o", "repo", "branch") 368 if err != nil { 369 t.Errorf("Repositories.GetRulesForBranch returned error: %v", err) 370 } 371 372 updateRule := NewUpdateRule(nil) 373 374 want := []*RepositoryRule{ 375 updateRule, 376 } 377 if !cmp.Equal(rules, want) { 378 t.Errorf("Repositories.GetRulesForBranch returned %+v, want %+v", Stringify(rules), Stringify(want)) 379 } 380 381 const methodName = "GetRulesForBranch" 382 383 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 384 got, resp, err := client.Repositories.GetRulesForBranch(ctx, "o", "repo", "branch") 385 if got != nil { 386 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 387 } 388 return resp, err 389 }) 390 } 391 392 func TestRepositoriesService_GetAllRulesets(t *testing.T) { 393 client, mux, _, teardown := setup() 394 defer teardown() 395 396 mux.HandleFunc("/repos/o/repo/rulesets", func(w http.ResponseWriter, r *http.Request) { 397 testMethod(t, r, "GET") 398 fmt.Fprint(w, `[ 399 { 400 "id": 42, 401 "name": "ruleset", 402 "source_type": "Repository", 403 "source": "o/repo", 404 "enforcement": "enabled" 405 }, 406 { 407 "id": 314, 408 "name": "Another ruleset", 409 "source_type": "Repository", 410 "source": "o/repo", 411 "enforcement": "enabled" 412 } 413 ]`) 414 }) 415 416 ctx := context.Background() 417 ruleSet, _, err := client.Repositories.GetAllRulesets(ctx, "o", "repo", false) 418 if err != nil { 419 t.Errorf("Repositories.GetAllRulesets returned error: %v", err) 420 } 421 422 want := []*Ruleset{ 423 { 424 ID: Int64(42), 425 Name: "ruleset", 426 SourceType: String("Repository"), 427 Source: "o/repo", 428 Enforcement: "enabled", 429 }, 430 { 431 ID: Int64(314), 432 Name: "Another ruleset", 433 SourceType: String("Repository"), 434 Source: "o/repo", 435 Enforcement: "enabled", 436 }, 437 } 438 if !cmp.Equal(ruleSet, want) { 439 t.Errorf("Repositories.GetAllRulesets returned %+v, want %+v", ruleSet, want) 440 } 441 442 const methodName = "GetAllRulesets" 443 444 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 445 got, resp, err := client.Repositories.GetAllRulesets(ctx, "o", "repo", false) 446 if got != nil { 447 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 448 } 449 return resp, err 450 }) 451 } 452 453 func TestRepositoriesService_CreateRuleset(t *testing.T) { 454 client, mux, _, teardown := setup() 455 defer teardown() 456 457 mux.HandleFunc("/repos/o/repo/rulesets", func(w http.ResponseWriter, r *http.Request) { 458 testMethod(t, r, "POST") 459 fmt.Fprint(w, `{ 460 "id": 42, 461 "name": "ruleset", 462 "source_type": "Repository", 463 "source": "o/repo", 464 "enforcement": "enabled" 465 }`) 466 }) 467 468 ctx := context.Background() 469 ruleSet, _, err := client.Repositories.CreateRuleset(ctx, "o", "repo", &Ruleset{ 470 Name: "ruleset", 471 Enforcement: "enabled", 472 }) 473 if err != nil { 474 t.Errorf("Repositories.CreateRuleset returned error: %v", err) 475 } 476 477 want := &Ruleset{ 478 ID: Int64(42), 479 Name: "ruleset", 480 SourceType: String("Repository"), 481 Source: "o/repo", 482 Enforcement: "enabled", 483 } 484 if !cmp.Equal(ruleSet, want) { 485 t.Errorf("Repositories.CreateRuleset returned %+v, want %+v", ruleSet, want) 486 } 487 488 const methodName = "CreateRuleset" 489 490 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 491 got, resp, err := client.Repositories.CreateRuleset(ctx, "o", "repo", &Ruleset{}) 492 if got != nil { 493 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 494 } 495 return resp, err 496 }) 497 } 498 499 func TestRepositoriesService_GetRuleset(t *testing.T) { 500 client, mux, _, teardown := setup() 501 defer teardown() 502 503 mux.HandleFunc("/repos/o/repo/rulesets/42", func(w http.ResponseWriter, r *http.Request) { 504 testMethod(t, r, "GET") 505 fmt.Fprint(w, `{ 506 "id": 42, 507 "name": "ruleset", 508 "source_type": "Organization", 509 "source": "o", 510 "enforcement": "enabled" 511 }`) 512 }) 513 514 ctx := context.Background() 515 ruleSet, _, err := client.Repositories.GetRuleset(ctx, "o", "repo", 42, true) 516 if err != nil { 517 t.Errorf("Repositories.GetRuleset returned error: %v", err) 518 } 519 520 want := &Ruleset{ 521 ID: Int64(42), 522 Name: "ruleset", 523 SourceType: String("Organization"), 524 Source: "o", 525 Enforcement: "enabled", 526 } 527 if !cmp.Equal(ruleSet, want) { 528 t.Errorf("Repositories.GetRuleset returned %+v, want %+v", ruleSet, want) 529 } 530 531 const methodName = "GetRuleset" 532 533 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 534 got, resp, err := client.Repositories.GetRuleset(ctx, "o", "repo", 42, true) 535 if got != nil { 536 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 537 } 538 return resp, err 539 }) 540 } 541 542 func TestRepositoriesService_UpdateRulesetNoBypassActor(t *testing.T) { 543 client, mux, _, teardown := setup() 544 defer teardown() 545 546 rs := &Ruleset{ 547 Name: "ruleset", 548 Source: "o/repo", 549 Enforcement: "enabled", 550 } 551 552 mux.HandleFunc("/repos/o/repo/rulesets/42", func(w http.ResponseWriter, r *http.Request) { 553 testMethod(t, r, "PUT") 554 fmt.Fprint(w, `{ 555 "id": 42, 556 "name": "ruleset", 557 "source_type": "Repository", 558 "source": "o/repo", 559 "enforcement": "enabled" 560 }`) 561 }) 562 563 ctx := context.Background() 564 565 ruleSet, _, err := client.Repositories.UpdateRulesetNoBypassActor(ctx, "o", "repo", 42, rs) 566 567 if err != nil { 568 t.Errorf("Repositories.UpdateRulesetNoBypassActor returned error: %v \n", err) 569 } 570 571 want := &Ruleset{ 572 ID: Int64(42), 573 Name: "ruleset", 574 SourceType: String("Repository"), 575 Source: "o/repo", 576 Enforcement: "enabled", 577 } 578 579 if !cmp.Equal(ruleSet, want) { 580 t.Errorf("Repositories.UpdateRulesetNoBypassActor returned %+v, want %+v", ruleSet, want) 581 } 582 583 const methodName = "UpdateRulesetNoBypassActor" 584 585 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 586 got, resp, err := client.Repositories.UpdateRulesetNoBypassActor(ctx, "o", "repo", 42, nil) 587 if got != nil { 588 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 589 } 590 return resp, err 591 }) 592 } 593 594 func TestRepositoriesService_UpdateRuleset(t *testing.T) { 595 client, mux, _, teardown := setup() 596 defer teardown() 597 598 mux.HandleFunc("/repos/o/repo/rulesets/42", func(w http.ResponseWriter, r *http.Request) { 599 testMethod(t, r, "PUT") 600 fmt.Fprint(w, `{ 601 "id": 42, 602 "name": "ruleset", 603 "source_type": "Repository", 604 "source": "o/repo", 605 "enforcement": "enabled" 606 }`) 607 }) 608 609 ctx := context.Background() 610 ruleSet, _, err := client.Repositories.UpdateRuleset(ctx, "o", "repo", 42, &Ruleset{ 611 Name: "ruleset", 612 Enforcement: "enabled", 613 }) 614 if err != nil { 615 t.Errorf("Repositories.UpdateRuleset returned error: %v", err) 616 } 617 618 want := &Ruleset{ 619 ID: Int64(42), 620 Name: "ruleset", 621 SourceType: String("Repository"), 622 Source: "o/repo", 623 Enforcement: "enabled", 624 } 625 626 if !cmp.Equal(ruleSet, want) { 627 t.Errorf("Repositories.UpdateRuleset returned %+v, want %+v", ruleSet, want) 628 } 629 630 const methodName = "UpdateRuleset" 631 632 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 633 got, resp, err := client.Repositories.UpdateRuleset(ctx, "o", "repo", 42, nil) 634 if got != nil { 635 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 636 } 637 return resp, err 638 }) 639 } 640 641 func TestRepositoriesService_DeleteRuleset(t *testing.T) { 642 client, mux, _, teardown := setup() 643 defer teardown() 644 645 mux.HandleFunc("/repos/o/repo/rulesets/42", func(w http.ResponseWriter, r *http.Request) { 646 testMethod(t, r, "DELETE") 647 }) 648 649 ctx := context.Background() 650 _, err := client.Repositories.DeleteRuleset(ctx, "o", "repo", 42) 651 if err != nil { 652 t.Errorf("Repositories.DeleteRuleset returned error: %v", err) 653 } 654 655 const methodName = "DeleteRuleset" 656 657 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 658 return client.Repositories.DeleteRuleset(ctx, "o", "repo", 42) 659 }) 660 }