github.com/google/go-github/v57@v57.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 "Valid creation": { 32 data: `{"type":"creation"}`, 33 want: NewCreationRule(), 34 }, 35 "Valid deletion": { 36 data: `{"type":"deletion"}`, 37 want: &RepositoryRule{ 38 Type: "deletion", 39 Parameters: nil, 40 }, 41 }, 42 "Valid required_linear_history": { 43 data: `{"type":"required_linear_history"}`, 44 want: &RepositoryRule{ 45 Type: "required_linear_history", 46 Parameters: nil, 47 }, 48 }, 49 "Valid required_signatures": { 50 data: `{"type":"required_signatures"}`, 51 want: &RepositoryRule{ 52 Type: "required_signatures", 53 Parameters: nil, 54 }, 55 }, 56 "Valid non_fast_forward": { 57 data: `{"type":"non_fast_forward"}`, 58 want: &RepositoryRule{ 59 Type: "non_fast_forward", 60 Parameters: nil, 61 }, 62 }, 63 "Valid update params": { 64 data: `{"type":"update","parameters":{"update_allows_fetch_and_merge":true}}`, 65 want: NewUpdateRule(&UpdateAllowsFetchAndMergeRuleParameters{UpdateAllowsFetchAndMerge: true}), 66 }, 67 "Invalid update params": { 68 data: `{"type":"update","parameters":{"update_allows_fetch_and_merge":"true"}}`, 69 want: &RepositoryRule{ 70 Type: "update", 71 Parameters: nil, 72 }, 73 wantErr: true, 74 }, 75 "Valid required_deployments params": { 76 data: `{"type":"required_deployments","parameters":{"required_deployment_environments":["test"]}}`, 77 want: NewRequiredDeploymentsRule(&RequiredDeploymentEnvironmentsRuleParameters{ 78 RequiredDeploymentEnvironments: []string{"test"}, 79 }), 80 }, 81 "Invalid required_deployments params": { 82 data: `{"type":"required_deployments","parameters":{"required_deployment_environments":true}}`, 83 want: &RepositoryRule{ 84 Type: "required_deployments", 85 Parameters: nil, 86 }, 87 wantErr: true, 88 }, 89 "Valid commit_message_pattern params": { 90 data: `{"type":"commit_message_pattern","parameters":{"operator":"starts_with","pattern":"github"}}`, 91 want: NewCommitMessagePatternRule(&RulePatternParameters{ 92 Operator: "starts_with", 93 Pattern: "github", 94 }), 95 }, 96 "Invalid commit_message_pattern params": { 97 data: `{"type":"commit_message_pattern","parameters":{"operator":"starts_with","pattern":1}}`, 98 want: &RepositoryRule{ 99 Type: "commit_message_pattern", 100 Parameters: nil, 101 }, 102 wantErr: true, 103 }, 104 "Valid commit_author_email_pattern params": { 105 data: `{"type":"commit_author_email_pattern","parameters":{"operator":"starts_with","pattern":"github"}}`, 106 want: NewCommitAuthorEmailPatternRule(&RulePatternParameters{ 107 Operator: "starts_with", 108 Pattern: "github", 109 }), 110 }, 111 "Invalid commit_author_email_pattern params": { 112 data: `{"type":"commit_author_email_pattern","parameters":{"operator":"starts_with","pattern":1}}`, 113 want: &RepositoryRule{ 114 Type: "commit_author_email_pattern", 115 Parameters: nil, 116 }, 117 wantErr: true, 118 }, 119 "Valid committer_email_pattern params": { 120 data: `{"type":"committer_email_pattern","parameters":{"operator":"starts_with","pattern":"github"}}`, 121 want: NewCommitterEmailPatternRule(&RulePatternParameters{ 122 Operator: "starts_with", 123 Pattern: "github", 124 }), 125 }, 126 "Invalid committer_email_pattern params": { 127 data: `{"type":"committer_email_pattern","parameters":{"operator":"starts_with","pattern":1}}`, 128 want: &RepositoryRule{ 129 Type: "committer_email_pattern", 130 Parameters: nil, 131 }, 132 wantErr: true, 133 }, 134 "Valid branch_name_pattern params": { 135 data: `{"type":"branch_name_pattern","parameters":{"operator":"starts_with","pattern":"github"}}`, 136 want: NewBranchNamePatternRule(&RulePatternParameters{ 137 Operator: "starts_with", 138 Pattern: "github", 139 }), 140 }, 141 "Invalid branch_name_pattern params": { 142 data: `{"type":"branch_name_pattern","parameters":{"operator":"starts_with","pattern":1}}`, 143 want: &RepositoryRule{ 144 Type: "branch_name_pattern", 145 Parameters: nil, 146 }, 147 wantErr: true, 148 }, 149 "Valid tag_name_pattern params": { 150 data: `{"type":"tag_name_pattern","parameters":{"operator":"starts_with","pattern":"github"}}`, 151 want: NewTagNamePatternRule(&RulePatternParameters{ 152 Operator: "starts_with", 153 Pattern: "github", 154 }), 155 }, 156 "Invalid tag_name_pattern params": { 157 data: `{"type":"tag_name_pattern","parameters":{"operator":"starts_with","pattern":1}}`, 158 want: &RepositoryRule{ 159 Type: "tag_name_pattern", 160 Parameters: nil, 161 }, 162 wantErr: true, 163 }, 164 "Valid pull_request params": { 165 data: `{ 166 "type":"pull_request", 167 "parameters":{ 168 "dismiss_stale_reviews_on_push": true, 169 "require_code_owner_review": true, 170 "require_last_push_approval": true, 171 "required_approving_review_count": 1, 172 "required_review_thread_resolution":true 173 } 174 }`, 175 want: NewPullRequestRule(&PullRequestRuleParameters{ 176 DismissStaleReviewsOnPush: true, 177 RequireCodeOwnerReview: true, 178 RequireLastPushApproval: true, 179 RequiredApprovingReviewCount: 1, 180 RequiredReviewThreadResolution: true, 181 }), 182 }, 183 "Invalid pull_request params": { 184 data: `{"type":"pull_request","parameters": {"dismiss_stale_reviews_on_push":"true"}}`, 185 want: &RepositoryRule{ 186 Type: "pull_request", 187 Parameters: nil, 188 }, 189 wantErr: true, 190 }, 191 "Valid required_status_checks params": { 192 data: `{"type":"required_status_checks","parameters":{"required_status_checks":[{"context":"test","integration_id":1}],"strict_required_status_checks_policy":true}}`, 193 want: NewRequiredStatusChecksRule(&RequiredStatusChecksRuleParameters{ 194 RequiredStatusChecks: []RuleRequiredStatusChecks{ 195 { 196 Context: "test", 197 IntegrationID: Int64(1), 198 }, 199 }, 200 StrictRequiredStatusChecksPolicy: true, 201 }), 202 }, 203 "Invalid required_status_checks params": { 204 data: `{"type":"required_status_checks", 205 "parameters": { 206 "required_status_checks": [ 207 { 208 "context": 1 209 } 210 ] 211 }}`, 212 want: &RepositoryRule{ 213 Type: "required_status_checks", 214 Parameters: nil, 215 }, 216 wantErr: true, 217 }, 218 "Required workflows params": { 219 data: `{"type":"workflows","parameters":{"workflows":[{"path": ".github/workflows/test.yml", "repository_id": 1}]}}`, 220 want: NewRequiredWorkflowsRule(&RequiredWorkflowsRuleParameters{ 221 RequiredWorkflows: []*RuleRequiredWorkflow{ 222 { 223 Path: ".github/workflows/test.yml", 224 RepositoryID: Int64(1), 225 }, 226 }, 227 }), 228 }, 229 "Invalid type": { 230 data: `{"type":"unknown"}`, 231 want: &RepositoryRule{ 232 Type: "", 233 Parameters: nil, 234 }, 235 wantErr: true, 236 }, 237 } 238 239 for name, tc := range tests { 240 rule := &RepositoryRule{} 241 242 t.Run(name, func(t *testing.T) { 243 err := rule.UnmarshalJSON([]byte(tc.data)) 244 if err == nil && tc.wantErr { 245 t.Errorf("RepositoryRule.UnmarshalJSON returned nil instead of an error") 246 } 247 if err != nil && !tc.wantErr { 248 t.Errorf("RepositoryRule.UnmarshalJSON returned an unexpected error: %+v", err) 249 } 250 if !cmp.Equal(tc.want, rule) { 251 t.Errorf("RepositoryRule.UnmarshalJSON expected rule %+v, got %+v", tc.want, rule) 252 } 253 }) 254 } 255 } 256 257 func TestRepositoriesService_GetRulesForBranch(t *testing.T) { 258 client, mux, _, teardown := setup() 259 defer teardown() 260 261 mux.HandleFunc("/repos/o/repo/rules/branches/branch", func(w http.ResponseWriter, r *http.Request) { 262 testMethod(t, r, "GET") 263 fmt.Fprint(w, `[ 264 { 265 "type": "creation" 266 }, 267 { 268 "type": "update", 269 "parameters": { 270 "update_allows_fetch_and_merge": true 271 } 272 } 273 ]`) 274 }) 275 276 ctx := context.Background() 277 rules, _, err := client.Repositories.GetRulesForBranch(ctx, "o", "repo", "branch") 278 if err != nil { 279 t.Errorf("Repositories.GetRulesForBranch returned error: %v", err) 280 } 281 282 creationRule := NewCreationRule() 283 updateRule := NewUpdateRule(&UpdateAllowsFetchAndMergeRuleParameters{ 284 UpdateAllowsFetchAndMerge: true, 285 }) 286 287 want := []*RepositoryRule{ 288 creationRule, 289 updateRule, 290 } 291 if !cmp.Equal(rules, want) { 292 t.Errorf("Repositories.GetRulesForBranch returned %+v, want %+v", rules, want) 293 } 294 295 const methodName = "GetRulesForBranch" 296 297 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 298 got, resp, err := client.Repositories.GetRulesForBranch(ctx, "o", "repo", "branch") 299 if got != nil { 300 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 301 } 302 return resp, err 303 }) 304 } 305 306 func TestRepositoriesService_GetRulesForBranchEmptyUpdateRule(t *testing.T) { 307 client, mux, _, teardown := setup() 308 defer teardown() 309 310 mux.HandleFunc("/repos/o/repo/rules/branches/branch", func(w http.ResponseWriter, r *http.Request) { 311 testMethod(t, r, "GET") 312 fmt.Fprint(w, `[ 313 { 314 "type": "update" 315 } 316 ]`) 317 }) 318 319 ctx := context.Background() 320 rules, _, err := client.Repositories.GetRulesForBranch(ctx, "o", "repo", "branch") 321 if err != nil { 322 t.Errorf("Repositories.GetRulesForBranch returned error: %v", err) 323 } 324 325 updateRule := NewUpdateRule(nil) 326 327 want := []*RepositoryRule{ 328 updateRule, 329 } 330 if !cmp.Equal(rules, want) { 331 t.Errorf("Repositories.GetRulesForBranch returned %+v, want %+v", Stringify(rules), Stringify(want)) 332 } 333 334 const methodName = "GetRulesForBranch" 335 336 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 337 got, resp, err := client.Repositories.GetRulesForBranch(ctx, "o", "repo", "branch") 338 if got != nil { 339 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 340 } 341 return resp, err 342 }) 343 } 344 345 func TestRepositoriesService_GetAllRulesets(t *testing.T) { 346 client, mux, _, teardown := setup() 347 defer teardown() 348 349 mux.HandleFunc("/repos/o/repo/rulesets", func(w http.ResponseWriter, r *http.Request) { 350 testMethod(t, r, "GET") 351 fmt.Fprint(w, `[ 352 { 353 "id": 42, 354 "name": "ruleset", 355 "source_type": "Repository", 356 "source": "o/repo", 357 "enforcement": "enabled" 358 }, 359 { 360 "id": 314, 361 "name": "Another ruleset", 362 "source_type": "Repository", 363 "source": "o/repo", 364 "enforcement": "enabled" 365 } 366 ]`) 367 }) 368 369 ctx := context.Background() 370 ruleSet, _, err := client.Repositories.GetAllRulesets(ctx, "o", "repo", false) 371 if err != nil { 372 t.Errorf("Repositories.GetAllRulesets returned error: %v", err) 373 } 374 375 want := []*Ruleset{ 376 { 377 ID: Int64(42), 378 Name: "ruleset", 379 SourceType: String("Repository"), 380 Source: "o/repo", 381 Enforcement: "enabled", 382 }, 383 { 384 ID: Int64(314), 385 Name: "Another ruleset", 386 SourceType: String("Repository"), 387 Source: "o/repo", 388 Enforcement: "enabled", 389 }, 390 } 391 if !cmp.Equal(ruleSet, want) { 392 t.Errorf("Repositories.GetAllRulesets returned %+v, want %+v", ruleSet, want) 393 } 394 395 const methodName = "GetAllRulesets" 396 397 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 398 got, resp, err := client.Repositories.GetAllRulesets(ctx, "o", "repo", false) 399 if got != nil { 400 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 401 } 402 return resp, err 403 }) 404 } 405 406 func TestRepositoriesService_CreateRuleset(t *testing.T) { 407 client, mux, _, teardown := setup() 408 defer teardown() 409 410 mux.HandleFunc("/repos/o/repo/rulesets", func(w http.ResponseWriter, r *http.Request) { 411 testMethod(t, r, "POST") 412 fmt.Fprint(w, `{ 413 "id": 42, 414 "name": "ruleset", 415 "source_type": "Repository", 416 "source": "o/repo", 417 "enforcement": "enabled" 418 }`) 419 }) 420 421 ctx := context.Background() 422 ruleSet, _, err := client.Repositories.CreateRuleset(ctx, "o", "repo", &Ruleset{ 423 Name: "ruleset", 424 Enforcement: "enabled", 425 }) 426 if err != nil { 427 t.Errorf("Repositories.CreateRuleset returned error: %v", err) 428 } 429 430 want := &Ruleset{ 431 ID: Int64(42), 432 Name: "ruleset", 433 SourceType: String("Repository"), 434 Source: "o/repo", 435 Enforcement: "enabled", 436 } 437 if !cmp.Equal(ruleSet, want) { 438 t.Errorf("Repositories.CreateRuleset returned %+v, want %+v", ruleSet, want) 439 } 440 441 const methodName = "CreateRuleset" 442 443 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 444 got, resp, err := client.Repositories.CreateRuleset(ctx, "o", "repo", &Ruleset{}) 445 if got != nil { 446 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 447 } 448 return resp, err 449 }) 450 } 451 452 func TestRepositoriesService_GetRuleset(t *testing.T) { 453 client, mux, _, teardown := setup() 454 defer teardown() 455 456 mux.HandleFunc("/repos/o/repo/rulesets/42", func(w http.ResponseWriter, r *http.Request) { 457 testMethod(t, r, "GET") 458 fmt.Fprint(w, `{ 459 "id": 42, 460 "name": "ruleset", 461 "source_type": "Organization", 462 "source": "o", 463 "enforcement": "enabled" 464 }`) 465 }) 466 467 ctx := context.Background() 468 ruleSet, _, err := client.Repositories.GetRuleset(ctx, "o", "repo", 42, true) 469 if err != nil { 470 t.Errorf("Repositories.GetRuleset returned error: %v", err) 471 } 472 473 want := &Ruleset{ 474 ID: Int64(42), 475 Name: "ruleset", 476 SourceType: String("Organization"), 477 Source: "o", 478 Enforcement: "enabled", 479 } 480 if !cmp.Equal(ruleSet, want) { 481 t.Errorf("Repositories.GetRuleset returned %+v, want %+v", ruleSet, want) 482 } 483 484 const methodName = "GetRuleset" 485 486 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 487 got, resp, err := client.Repositories.GetRuleset(ctx, "o", "repo", 42, true) 488 if got != nil { 489 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 490 } 491 return resp, err 492 }) 493 } 494 495 func TestRepositoriesService_UpdateRuleset(t *testing.T) { 496 client, mux, _, teardown := setup() 497 defer teardown() 498 499 mux.HandleFunc("/repos/o/repo/rulesets/42", func(w http.ResponseWriter, r *http.Request) { 500 testMethod(t, r, "PUT") 501 fmt.Fprint(w, `{ 502 "id": 42, 503 "name": "ruleset", 504 "source_type": "Repository", 505 "source": "o/repo", 506 "enforcement": "enabled" 507 }`) 508 }) 509 510 ctx := context.Background() 511 ruleSet, _, err := client.Repositories.UpdateRuleset(ctx, "o", "repo", 42, &Ruleset{ 512 Name: "ruleset", 513 Enforcement: "enabled", 514 }) 515 if err != nil { 516 t.Errorf("Repositories.UpdateRuleset returned error: %v", err) 517 } 518 519 want := &Ruleset{ 520 ID: Int64(42), 521 Name: "ruleset", 522 SourceType: String("Repository"), 523 Source: "o/repo", 524 Enforcement: "enabled", 525 } 526 if !cmp.Equal(ruleSet, want) { 527 t.Errorf("Repositories.UpdateRuleset returned %+v, want %+v", ruleSet, want) 528 } 529 530 const methodName = "UpdateRuleset" 531 532 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 533 got, resp, err := client.Repositories.UpdateRuleset(ctx, "o", "repo", 42, nil) 534 if got != nil { 535 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 536 } 537 return resp, err 538 }) 539 } 540 541 func TestRepositoriesService_DeleteRuleset(t *testing.T) { 542 client, mux, _, teardown := setup() 543 defer teardown() 544 545 mux.HandleFunc("/repos/o/repo/rulesets/42", func(w http.ResponseWriter, r *http.Request) { 546 testMethod(t, r, "DELETE") 547 }) 548 549 ctx := context.Background() 550 _, err := client.Repositories.DeleteRuleset(ctx, "o", "repo", 42) 551 if err != nil { 552 t.Errorf("Repositories.DeleteRuleset returned error: %v", err) 553 } 554 555 const methodName = "DeleteRuleset" 556 557 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 558 return client.Repositories.DeleteRuleset(ctx, "o", "repo", 42) 559 }) 560 }