diff --git a/github/github-accessors.go b/github/github-accessors.go index a9aaee814ab..1a6f2dfb7fd 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -18366,6 +18366,134 @@ func (r *Rule) GetSeverity() string { return *r.Severity } +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (r *RulePatternParameters) GetName() string { + if r == nil || r.Name == nil { + return "" + } + return *r.Name +} + +// GetNegate returns the Negate field if it's non-nil, zero value otherwise. +func (r *RulePatternParameters) GetNegate() bool { + if r == nil || r.Negate == nil { + return false + } + return *r.Negate +} + +// GetIntegrationID returns the IntegrationID field if it's non-nil, zero value otherwise. +func (r *RuleRequiredStatusChecks) GetIntegrationID() int64 { + if r == nil || r.IntegrationID == nil { + return 0 + } + return *r.IntegrationID +} + +// GetBypassActors returns the BypassActors field if it's non-nil, zero value otherwise. +func (r *Ruleset) GetBypassActors() []BypassActor { + if r == nil || r.BypassActors == nil { + return nil + } + return *r.BypassActors +} + +// GetBypassMode returns the BypassMode field if it's non-nil, zero value otherwise. +func (r *Ruleset) GetBypassMode() string { + if r == nil || r.BypassMode == nil { + return "" + } + return *r.BypassMode +} + +// GetConditions returns the Conditions field. +func (r *Ruleset) GetConditions() *RulesetConditions { + if r == nil { + return nil + } + return r.Conditions +} + +// GetLinks returns the Links field. +func (r *Ruleset) GetLinks() *RulesetLinks { + if r == nil { + return nil + } + return r.Links +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (r *Ruleset) GetNodeID() string { + if r == nil || r.NodeID == nil { + return "" + } + return *r.NodeID +} + +// GetRules returns the Rules field if it's non-nil, zero value otherwise. +func (r *Ruleset) GetRules() []RepositoryRule { + if r == nil || r.Rules == nil { + return nil + } + return *r.Rules +} + +// GetSourceType returns the SourceType field if it's non-nil, zero value otherwise. +func (r *Ruleset) GetSourceType() string { + if r == nil || r.SourceType == nil { + return "" + } + return *r.SourceType +} + +// GetTarget returns the Target field if it's non-nil, zero value otherwise. +func (r *Ruleset) GetTarget() string { + if r == nil || r.Target == nil { + return "" + } + return *r.Target +} + +// GetRefName returns the RefName field. +func (r *RulesetConditions) GetRefName() *RulesetRefConditionParameters { + if r == nil { + return nil + } + return r.RefName +} + +// GetRepositoryName returns the RepositoryName field. +func (r *RulesetConditions) GetRepositoryName() *RulesetRepositoryConditionParameters { + if r == nil { + return nil + } + return r.RepositoryName +} + +// GetHRef returns the HRef field if it's non-nil, zero value otherwise. +func (r *RulesetLink) GetHRef() string { + if r == nil || r.HRef == nil { + return "" + } + return *r.HRef +} + +// GetSelf returns the Self field. +func (r *RulesetLinks) GetSelf() *RulesetLink { + if r == nil { + return nil + } + return r.Self +} + +// GetProtected returns the Protected field if it's non-nil, zero value otherwise. +func (r *RulesetRepositoryConditionParameters) GetProtected() bool { + if r == nil || r.Protected == nil { + return false + } + return *r.Protected +} + // GetBusy returns the Busy field if it's non-nil, zero value otherwise. func (r *Runner) GetBusy() bool { if r == nil || r.Busy == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index a29baa0f0c4..a15c41e22ce 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -21404,6 +21404,151 @@ func TestRule_GetSeverity(tt *testing.T) { r.GetSeverity() } +func TestRulePatternParameters_GetName(tt *testing.T) { + var zeroValue string + r := &RulePatternParameters{Name: &zeroValue} + r.GetName() + r = &RulePatternParameters{} + r.GetName() + r = nil + r.GetName() +} + +func TestRulePatternParameters_GetNegate(tt *testing.T) { + var zeroValue bool + r := &RulePatternParameters{Negate: &zeroValue} + r.GetNegate() + r = &RulePatternParameters{} + r.GetNegate() + r = nil + r.GetNegate() +} + +func TestRuleRequiredStatusChecks_GetIntegrationID(tt *testing.T) { + var zeroValue int64 + r := &RuleRequiredStatusChecks{IntegrationID: &zeroValue} + r.GetIntegrationID() + r = &RuleRequiredStatusChecks{} + r.GetIntegrationID() + r = nil + r.GetIntegrationID() +} + +func TestRuleset_GetBypassActors(tt *testing.T) { + var zeroValue []BypassActor + r := &Ruleset{BypassActors: &zeroValue} + r.GetBypassActors() + r = &Ruleset{} + r.GetBypassActors() + r = nil + r.GetBypassActors() +} + +func TestRuleset_GetBypassMode(tt *testing.T) { + var zeroValue string + r := &Ruleset{BypassMode: &zeroValue} + r.GetBypassMode() + r = &Ruleset{} + r.GetBypassMode() + r = nil + r.GetBypassMode() +} + +func TestRuleset_GetConditions(tt *testing.T) { + r := &Ruleset{} + r.GetConditions() + r = nil + r.GetConditions() +} + +func TestRuleset_GetLinks(tt *testing.T) { + r := &Ruleset{} + r.GetLinks() + r = nil + r.GetLinks() +} + +func TestRuleset_GetNodeID(tt *testing.T) { + var zeroValue string + r := &Ruleset{NodeID: &zeroValue} + r.GetNodeID() + r = &Ruleset{} + r.GetNodeID() + r = nil + r.GetNodeID() +} + +func TestRuleset_GetRules(tt *testing.T) { + var zeroValue []RepositoryRule + r := &Ruleset{Rules: &zeroValue} + r.GetRules() + r = &Ruleset{} + r.GetRules() + r = nil + r.GetRules() +} + +func TestRuleset_GetSourceType(tt *testing.T) { + var zeroValue string + r := &Ruleset{SourceType: &zeroValue} + r.GetSourceType() + r = &Ruleset{} + r.GetSourceType() + r = nil + r.GetSourceType() +} + +func TestRuleset_GetTarget(tt *testing.T) { + var zeroValue string + r := &Ruleset{Target: &zeroValue} + r.GetTarget() + r = &Ruleset{} + r.GetTarget() + r = nil + r.GetTarget() +} + +func TestRulesetConditions_GetRefName(tt *testing.T) { + r := &RulesetConditions{} + r.GetRefName() + r = nil + r.GetRefName() +} + +func TestRulesetConditions_GetRepositoryName(tt *testing.T) { + r := &RulesetConditions{} + r.GetRepositoryName() + r = nil + r.GetRepositoryName() +} + +func TestRulesetLink_GetHRef(tt *testing.T) { + var zeroValue string + r := &RulesetLink{HRef: &zeroValue} + r.GetHRef() + r = &RulesetLink{} + r.GetHRef() + r = nil + r.GetHRef() +} + +func TestRulesetLinks_GetSelf(tt *testing.T) { + r := &RulesetLinks{} + r.GetSelf() + r = nil + r.GetSelf() +} + +func TestRulesetRepositoryConditionParameters_GetProtected(tt *testing.T) { + var zeroValue bool + r := &RulesetRepositoryConditionParameters{Protected: &zeroValue} + r.GetProtected() + r = &RulesetRepositoryConditionParameters{} + r.GetProtected() + r = nil + r.GetProtected() +} + func TestRunner_GetBusy(tt *testing.T) { var zeroValue bool r := &Runner{Busy: &zeroValue} diff --git a/github/orgs_rules.go b/github/orgs_rules.go new file mode 100644 index 00000000000..11be725eacd --- /dev/null +++ b/github/orgs_rules.go @@ -0,0 +1,105 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" +) + +// GetAllOrganizationRulesets gets all the rulesets for the specified organization. +// +// GitHub API docs: https://docs.github.com/en/rest/orgs/rules#get-all-organization-repository-rulesets +func (s *OrganizationsService) GetAllOrganizationRulesets(ctx context.Context, org string) ([]*Ruleset, *Response, error) { + u := fmt.Sprintf("orgs/%v/rulesets", org) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var rulesets []*Ruleset + resp, err := s.client.Do(ctx, req, &rulesets) + if err != nil { + return nil, resp, err + } + + return rulesets, resp, nil +} + +// CreateOrganizationRuleset creates a ruleset for the specified organization. +// +// GitHub API docs: https://docs.github.com/en/rest/orgs/rules#create-an-organization-repository-ruleset +func (s *OrganizationsService) CreateOrganizationRuleset(ctx context.Context, org string, rs *Ruleset) (*Ruleset, *Response, error) { + u := fmt.Sprintf("orgs/%v/rulesets", org) + + req, err := s.client.NewRequest("POST", u, rs) + if err != nil { + return nil, nil, err + } + + var ruleset *Ruleset + resp, err := s.client.Do(ctx, req, &ruleset) + if err != nil { + return nil, resp, err + } + + return ruleset, resp, nil +} + +// GetOrganizationRuleset gets a ruleset from the specified organization. +// +// GitHub API docs: https://docs.github.com/en/rest/orgs/rules#get-an-organization-repository-ruleset +func (s *OrganizationsService) GetOrganizationRuleset(ctx context.Context, org string, rulesetID int64) (*Ruleset, *Response, error) { + u := fmt.Sprintf("orgs/%v/rulesets/%v", org, rulesetID) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var rulesets *Ruleset + resp, err := s.client.Do(ctx, req, &rulesets) + if err != nil { + return nil, resp, err + } + + return rulesets, resp, nil +} + +// UpdateOrganizationRuleset updates a ruleset from the specified organization. +// +// GitHub API docs: https://docs.github.com/en/rest/orgs/rules#update-an-organization-repository-ruleset +func (s *OrganizationsService) UpdateOrganizationRuleset(ctx context.Context, org string, rulesetID int64, rs *Ruleset) (*Ruleset, *Response, error) { + u := fmt.Sprintf("orgs/%v/rulesets/%v", org, rulesetID) + + req, err := s.client.NewRequest("PUT", u, rs) + if err != nil { + return nil, nil, err + } + + var rulesets *Ruleset + resp, err := s.client.Do(ctx, req, &rulesets) + if err != nil { + return nil, resp, err + } + + return rulesets, resp, nil +} + +// DeleteOrganizationRuleset deletes a ruleset from the specified organization. +// +// GitHub API docs: https://docs.github.com/en/rest/orgs/rules#delete-an-organization-repository-ruleset +func (s *OrganizationsService) DeleteOrganizationRuleset(ctx context.Context, org string, rulesetID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/rulesets/%v", org, rulesetID) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/github/orgs_rules_test.go b/github/orgs_rules_test.go new file mode 100644 index 00000000000..f7e3fb0b973 --- /dev/null +++ b/github/orgs_rules_test.go @@ -0,0 +1,628 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestOrganizationsService_GetAllOrganizationRulesets(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/rulesets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[{ + "id": 26110, + "name": "test ruleset", + "target": "branch", + "source_type": "Organization", + "source": "o", + "enforcement": "active", + "bypass_mode": "none", + "node_id": "nid", + "_links": { + "self": { + "href": "https://api.github.com/orgs/o/rulesets/26110" + } + } + }]`) + }) + + ctx := context.Background() + rulesets, _, err := client.Organizations.GetAllOrganizationRulesets(ctx, "o") + if err != nil { + t.Errorf("Organizations.GetAllOrganizationRulesets returned error: %v", err) + } + + want := []*Ruleset{{ + ID: 26110, + Name: "test ruleset", + Target: String("branch"), + SourceType: String("Organization"), + Source: "o", + Enforcement: "active", + BypassMode: String("none"), + NodeID: String("nid"), + Links: &RulesetLinks{ + Self: &RulesetLink{HRef: String("https://api.github.com/orgs/o/rulesets/26110")}, + }, + }} + if !cmp.Equal(rulesets, want) { + t.Errorf("Organizations.GetAllOrganizationRulesets returned %+v, want %+v", rulesets, want) + } + + const methodName = "GetAllOrganizationRulesets" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.GetAllOrganizationRulesets(ctx, "o") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_CreateOrganizationRuleset(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/rulesets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{ + "id": 21, + "name": "ruleset", + "target": "branch", + "source_type": "Organization", + "source": "o", + "enforcement": "active", + "bypass_actors": [ + { + "actor_id": 234, + "actor_type": "Team" + } + ], + "conditions": { + "ref_name": { + "include": [ + "refs/heads/main", + "refs/heads/master" + ], + "exclude": [ + "refs/heads/dev*" + ] + }, + "repository_name": { + "include": [ + "important_repository", + "another_important_repository" + ], + "exclude": [ + "unimportant_repository" + ], + "protected": true + } + }, + "rules": [ + { + "type": "creation" + }, + { + "type": "update", + "parameters": { + "update_allows_fetch_and_merge": true + } + }, + { + "type": "deletion" + }, + { + "type": "required_linear_history" + }, + { + "type": "required_deployments", + "parameters": { + "required_deployment_environments": ["test"] + } + }, + { + "type": "required_signatures" + }, + { + "type": "pull_request", + "parameters": { + "dismiss_stale_reviews_on_push": true, + "require_code_owner_review": true, + "require_last_push_approval": true, + "required_approving_review_count": 1, + "required_review_thread_resolution": true + } + }, + { + "type": "required_status_checks", + "parameters": { + "required_status_checks": [ + { + "context": "test", + "integration_id": 1 + } + ], + "strict_required_status_checks_policy": true + } + }, + { + "type": "non_fast_forward" + }, + { + "type": "commit_message_pattern", + "parameters": { + "name": "avoid test commits", + "negate": true, + "operator": "starts_with", + "pattern": "[test]" + } + }, + { + "type": "commit_author_email_pattern", + "parameters": { + "operator": "contains", + "pattern": "github" + } + }, + { + "type": "committer_email_pattern", + "parameters": { + "name": "avoid commit emails", + "negate": true, + "operator": "ends_with", + "pattern": "abc" + } + }, + { + "type": "branch_name_pattern", + "parameters": { + "name": "avoid branch names", + "negate": true, + "operator": "regex", + "pattern": "github$" + } + }, + { + "type": "tag_name_pattern", + "parameters": { + "name": "avoid tag names", + "negate": true, + "operator": "contains", + "pattern": "github" + } + } + ] + }`) + }) + + ctx := context.Background() + ruleset, _, err := client.Organizations.CreateOrganizationRuleset(ctx, "o", &Ruleset{ + ID: 21, + Name: "ruleset", + Target: String("branch"), + SourceType: String("Organization"), + Source: "o", + Enforcement: "active", + BypassActors: &[]BypassActor{ + { + ActorID: 234, + ActorType: "Team", + }, + }, + Conditions: &RulesetConditions{ + RefName: &RulesetRefConditionParameters{ + Include: []string{"refs/heads/main", "refs/heads/master"}, + Exclude: []string{"refs/heads/dev*"}, + }, + RepositoryName: &RulesetRepositoryConditionParameters{ + Include: []string{"important_repository", "another_important_repository"}, + Exclude: []string{"unimportant_repository"}, + Protected: Bool(true), + }, + }, + Rules: &[]RepositoryRule{ + NewCreationRule(), + NewUpdateRule(&UpdateAllowsFetchAndMergeRuleParameters{ + UpdateAllowsFetchAndMerge: true, + }), + NewDeletionRule(), + NewRequiredLinearHistoryRule(), + NewRequiredDeploymentsRule(&RequiredDeploymentEnvironmentsRuleParameters{ + RequiredDeploymentEnvironments: []string{"test"}, + }), + NewRequiredSignaturesRule(), + NewPullRequestRule(&PullRequestRuleParameters{ + RequireCodeOwnerReview: true, + RequireLastPushApproval: true, + RequiredApprovingReviewCount: 1, + RequiredReviewThreadResolution: true, + DismissStaleReviewsOnPush: true, + }), + NewRequiredStatusChecksRule(&RequiredStatusChecksRuleParameters{ + RequiredStatusChecks: []RuleRequiredStatusChecks{ + { + Context: "test", + IntegrationID: Int64(1), + }, + }, + StrictRequiredStatusChecksPolicy: true, + }), + NewNonFastForwardRule(), + NewCommitMessagePatternRule(&RulePatternParameters{ + Name: String("avoid test commits"), + Negate: Bool(true), + Operator: "starts_with", + Pattern: "[test]", + }), + NewCommitAuthorEmailPatternRule(&RulePatternParameters{ + Operator: "contains", + Pattern: "github", + }), + NewCommitterEmailPatternRule(&RulePatternParameters{ + Name: String("avoid commit emails"), + Negate: Bool(true), + Operator: "ends_with", + Pattern: "abc", + }), + NewBranchNamePatternRule(&RulePatternParameters{ + Name: String("avoid branch names"), + Negate: Bool(true), + Operator: "regex", + Pattern: "github$", + }), + NewTagNamePatternRule(&RulePatternParameters{ + Name: String("avoid tag names"), + Negate: Bool(true), + Operator: "contains", + Pattern: "github", + }), + }, + }) + if err != nil { + t.Errorf("Organizations.CreateOrganizationRuleset returned error: %v", err) + } + + want := &Ruleset{ + ID: 21, + Name: "ruleset", + Target: String("branch"), + SourceType: String("Organization"), + Source: "o", + Enforcement: "active", + BypassActors: &[]BypassActor{ + { + ActorID: 234, + ActorType: "Team", + }, + }, + Conditions: &RulesetConditions{ + RefName: &RulesetRefConditionParameters{ + Include: []string{"refs/heads/main", "refs/heads/master"}, + Exclude: []string{"refs/heads/dev*"}, + }, + RepositoryName: &RulesetRepositoryConditionParameters{ + Include: []string{"important_repository", "another_important_repository"}, + Exclude: []string{"unimportant_repository"}, + Protected: Bool(true), + }, + }, + Rules: &[]RepositoryRule{ + NewCreationRule(), + NewUpdateRule(&UpdateAllowsFetchAndMergeRuleParameters{ + UpdateAllowsFetchAndMerge: true, + }), + NewDeletionRule(), + NewRequiredLinearHistoryRule(), + NewRequiredDeploymentsRule(&RequiredDeploymentEnvironmentsRuleParameters{ + RequiredDeploymentEnvironments: []string{"test"}, + }), + NewRequiredSignaturesRule(), + NewPullRequestRule(&PullRequestRuleParameters{ + RequireCodeOwnerReview: true, + RequireLastPushApproval: true, + RequiredApprovingReviewCount: 1, + RequiredReviewThreadResolution: true, + DismissStaleReviewsOnPush: true, + }), + NewRequiredStatusChecksRule(&RequiredStatusChecksRuleParameters{ + RequiredStatusChecks: []RuleRequiredStatusChecks{ + { + Context: "test", + IntegrationID: Int64(1), + }, + }, + StrictRequiredStatusChecksPolicy: true, + }), + NewNonFastForwardRule(), + NewCommitMessagePatternRule(&RulePatternParameters{ + Name: String("avoid test commits"), + Negate: Bool(true), + Operator: "starts_with", + Pattern: "[test]", + }), + NewCommitAuthorEmailPatternRule(&RulePatternParameters{ + Operator: "contains", + Pattern: "github", + }), + NewCommitterEmailPatternRule(&RulePatternParameters{ + Name: String("avoid commit emails"), + Negate: Bool(true), + Operator: "ends_with", + Pattern: "abc", + }), + NewBranchNamePatternRule(&RulePatternParameters{ + Name: String("avoid branch names"), + Negate: Bool(true), + Operator: "regex", + Pattern: "github$", + }), + NewTagNamePatternRule(&RulePatternParameters{ + Name: String("avoid tag names"), + Negate: Bool(true), + Operator: "contains", + Pattern: "github", + }), + }, + } + if !cmp.Equal(ruleset, want) { + t.Errorf("Organizations.CreateOrganizationRuleset returned %+v, want %+v", ruleset, want) + } + + const methodName = "CreateOrganizationRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.CreateOrganizationRuleset(ctx, "o", nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_GetOrganizationRuleset(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/rulesets/26110", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id": 26110, + "name": "test ruleset", + "target": "branch", + "source_type": "Organization", + "source": "o", + "enforcement": "active", + "bypass_mode": "none", + "node_id": "nid", + "_links": { + "self": { + "href": "https://api.github.com/orgs/o/rulesets/26110" + } + }, + "conditions": { + "ref_name": { + "include": [ + "refs/heads/main", + "refs/heads/master" + ], + "exclude": [ + "refs/heads/dev*" + ] + }, + "repository_name": { + "include": [ + "important_repository", + "another_important_repository" + ], + "exclude": [ + "unimportant_repository" + ], + "protected": true + } + }, + "rules": [ + { + "type": "creation" + } + ] + }`) + }) + + ctx := context.Background() + rulesets, _, err := client.Organizations.GetOrganizationRuleset(ctx, "o", 26110) + if err != nil { + t.Errorf("Organizations.GetOrganizationRepositoryRuleset returned error: %v", err) + } + + want := &Ruleset{ + ID: 26110, + Name: "test ruleset", + Target: String("branch"), + SourceType: String("Organization"), + Source: "o", + Enforcement: "active", + BypassMode: String("none"), + NodeID: String("nid"), + Links: &RulesetLinks{ + Self: &RulesetLink{HRef: String("https://api.github.com/orgs/o/rulesets/26110")}, + }, + Conditions: &RulesetConditions{ + RefName: &RulesetRefConditionParameters{ + Include: []string{"refs/heads/main", "refs/heads/master"}, + Exclude: []string{"refs/heads/dev*"}, + }, + RepositoryName: &RulesetRepositoryConditionParameters{ + Include: []string{"important_repository", "another_important_repository"}, + Exclude: []string{"unimportant_repository"}, + Protected: Bool(true), + }, + }, + Rules: &[]RepositoryRule{ + NewCreationRule(), + }, + } + if !cmp.Equal(rulesets, want) { + t.Errorf("Organizations.GetOrganizationRuleset returned %+v, want %+v", rulesets, want) + } + + const methodName = "GetOrganizationRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.GetOrganizationRuleset(ctx, "o", 26110) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_UpdateOrganizationRuleset(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/rulesets/26110", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + fmt.Fprint(w, `{ + "id": 26110, + "name": "test ruleset", + "target": "branch", + "source_type": "Organization", + "source": "o", + "enforcement": "active", + "bypass_mode": "none", + "node_id": "nid", + "_links": { + "self": { + "href": "https://api.github.com/orgs/o/rulesets/26110" + } + }, + "conditions": { + "ref_name": { + "include": [ + "refs/heads/main", + "refs/heads/master" + ], + "exclude": [ + "refs/heads/dev*" + ] + }, + "repository_name": { + "include": [ + "important_repository", + "another_important_repository" + ], + "exclude": [ + "unimportant_repository" + ], + "protected": true + } + }, + "rules": [ + { + "type": "creation" + } + ] + }`) + }) + + ctx := context.Background() + rulesets, _, err := client.Organizations.UpdateOrganizationRuleset(ctx, "o", 26110, &Ruleset{ + Name: "test ruleset", + Target: String("branch"), + Enforcement: "active", + BypassMode: String("none"), + Conditions: &RulesetConditions{ + RefName: &RulesetRefConditionParameters{ + Include: []string{"refs/heads/main", "refs/heads/master"}, + Exclude: []string{"refs/heads/dev*"}, + }, + RepositoryName: &RulesetRepositoryConditionParameters{ + Include: []string{"important_repository", "another_important_repository"}, + Exclude: []string{"unimportant_repository"}, + Protected: Bool(true), + }, + }, + Rules: &[]RepositoryRule{ + NewCreationRule(), + }, + }) + + if err != nil { + t.Errorf("Organizations.UpdateOrganizationRuleset returned error: %v", err) + } + + want := &Ruleset{ + ID: 26110, + Name: "test ruleset", + Target: String("branch"), + SourceType: String("Organization"), + Source: "o", + Enforcement: "active", + BypassMode: String("none"), + NodeID: String("nid"), + Links: &RulesetLinks{ + Self: &RulesetLink{HRef: String("https://api.github.com/orgs/o/rulesets/26110")}, + }, + Conditions: &RulesetConditions{ + RefName: &RulesetRefConditionParameters{ + Include: []string{"refs/heads/main", "refs/heads/master"}, + Exclude: []string{"refs/heads/dev*"}, + }, + RepositoryName: &RulesetRepositoryConditionParameters{ + Include: []string{"important_repository", "another_important_repository"}, + Exclude: []string{"unimportant_repository"}, + Protected: Bool(true), + }, + }, + Rules: &[]RepositoryRule{ + NewCreationRule(), + }, + } + if !cmp.Equal(rulesets, want) { + t.Errorf("Organizations.UpdateOrganizationRuleset returned %+v, want %+v", rulesets, want) + } + + const methodName = "UpdateOrganizationRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Organizations.UpdateOrganizationRuleset(ctx, "o", 26110, nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestOrganizationsService_DeleteOrganizationRuleset(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/orgs/o/rulesets/26110", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := context.Background() + _, err := client.Organizations.DeleteOrganizationRuleset(ctx, "o", 26110) + if err != nil { + t.Errorf("Organizations.DeleteOrganizationRuleset returned error: %v", err) + } + + const methodName = "DeleteOrganizationRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Organizations.DeleteOrganizationRuleset(ctx, "0", 26110) + }) +} diff --git a/github/repos_rules.go b/github/repos_rules.go new file mode 100644 index 00000000000..83d7eacfe21 --- /dev/null +++ b/github/repos_rules.go @@ -0,0 +1,392 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" +) + +// BypassActor represents the bypass actors from a ruleset. +type BypassActor struct { + ActorID int64 `json:"actor_id,omitempty"` + // Possible values for ActorType are: Team, Integration + ActorType string `json:"actor_type,omitempty"` +} + +// RulesetLink represents a single link object from GitHub ruleset request _links. +type RulesetLink struct { + HRef *string `json:"href,omitempty"` +} + +// RulesetLinks represents the "_links" object in a Ruleset. +type RulesetLinks struct { + Self *RulesetLink `json:"self,omitempty"` +} + +// RulesetRefConditionParameters represents the conditions object for ref_names. +type RulesetRefConditionParameters struct { + Include []string `json:"include"` + Exclude []string `json:"exclude"` +} + +// RulesetRepositoryConditionParameters represents the conditions object for repository_names. +type RulesetRepositoryConditionParameters struct { + Include []string `json:"include"` + Exclude []string `json:"exclude"` + Protected *bool `json:"protected,omitempty"` +} + +// RulesetCondition represents the conditions object in a ruleset. +type RulesetConditions struct { + RefName *RulesetRefConditionParameters `json:"ref_name,omitempty"` + RepositoryName *RulesetRepositoryConditionParameters `json:"repository_name,omitempty"` +} + +// RulePatternParameters represents the rule pattern parameters. +type RulePatternParameters struct { + Name *string `json:"name,omitempty"` + // If Negate is true, the rule will fail if the pattern matches. + Negate *bool `json:"negate,omitempty"` + // Possible values for Operator are: starts_with, ends_with, contains, regex + Operator string `json:"operator"` + Pattern string `json:"pattern"` +} + +// UpdateAllowsFetchAndMergeRuleParameters represents the update rule parameters. +type UpdateAllowsFetchAndMergeRuleParameters struct { + UpdateAllowsFetchAndMerge bool `json:"update_allows_fetch_and_merge"` +} + +// RequiredDeploymentEnvironmentsRuleParameters represents the required_deployments rule parameters. +type RequiredDeploymentEnvironmentsRuleParameters struct { + RequiredDeploymentEnvironments []string `json:"required_deployment_environments"` +} + +// PullRequestRuleParameters represents the pull_request rule parameters. +type PullRequestRuleParameters struct { + DismissStaleReviewsOnPush bool `json:"dismiss_stale_reviews_on_push"` + RequireCodeOwnerReview bool `json:"require_code_owner_review"` + RequireLastPushApproval bool `json:"require_last_push_approval"` + RequiredApprovingReviewCount int `json:"required_approving_review_count"` + RequiredReviewThreadResolution bool `json:"required_review_thread_resolution"` +} + +// RuleRequiredStatusChecks represents the RequiredStatusChecks for the RequiredStatusChecksRuleParameters object. +type RuleRequiredStatusChecks struct { + Context string `json:"context"` + IntegrationID *int64 `json:"integration_id,omitempty"` +} + +// RequiredStatusChecksRuleParameters represents the required_status_checks rule parameters. +type RequiredStatusChecksRuleParameters struct { + RequiredStatusChecks []RuleRequiredStatusChecks `json:"required_status_checks"` + StrictRequiredStatusChecksPolicy bool `json:"strict_required_status_checks_policy"` +} + +// RepositoryRule represents a GitHub Rule. +type RepositoryRule struct { + Type string `json:"type"` + Parameters interface{} `json:"parameters,omitempty"` +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +// This helps us handle the fact that RepositoryRule parameter field can be of numerous types. +func (r *RepositoryRule) UnmarshalJSON(data []byte) error { + type rule RepositoryRule + var RepositoryRule rule + if err := json.Unmarshal(data, &RepositoryRule); err != nil { + return err + } + + r.Type = RepositoryRule.Type + + switch RepositoryRule.Type { + case "creation", "deletion", "required_linear_history", "required_signatures", "non_fast_forward": + r.Parameters = nil + case "update": + RepositoryRule.Parameters = &UpdateAllowsFetchAndMergeRuleParameters{} + if err := json.Unmarshal(data, &RepositoryRule); err != nil { + return err + } + r.Parameters = RepositoryRule.Parameters + case "required_deployments": + RepositoryRule.Parameters = &RequiredDeploymentEnvironmentsRuleParameters{} + if err := json.Unmarshal(data, &RepositoryRule); err != nil { + return err + } + r.Parameters = RepositoryRule.Parameters + case "commit_message_pattern", "commit_author_email_pattern", "committer_email_pattern", "branch_name_pattern", "tag_name_pattern": + RepositoryRule.Parameters = &RulePatternParameters{} + if err := json.Unmarshal(data, &RepositoryRule); err != nil { + return err + } + r.Parameters = RepositoryRule.Parameters + case "pull_request": + RepositoryRule.Parameters = &PullRequestRuleParameters{} + if err := json.Unmarshal(data, &RepositoryRule); err != nil { + return err + } + r.Parameters = RepositoryRule.Parameters + case "required_status_checks": + RepositoryRule.Parameters = &RequiredStatusChecksRuleParameters{} + if err := json.Unmarshal(data, &RepositoryRule); err != nil { + return err + } + r.Parameters = RepositoryRule.Parameters + default: + r.Type = "" + r.Parameters = nil + return fmt.Errorf("RepositoryRule.Type %T is not yet implemented, unable to unmarshal", RepositoryRule.Type) + } + + return nil +} + +// NewCreationRule creates a rule to only allow users with bypass permission to create matching refs. +func NewCreationRule() (rule RepositoryRule) { + return RepositoryRule{ + Type: "creation", + } +} + +// NewUpdateRule creates a rule to only allow users with bypass permission to update matching refs. +func NewUpdateRule(params *UpdateAllowsFetchAndMergeRuleParameters) (rule RepositoryRule) { + return RepositoryRule{ + Type: "update", + Parameters: params, + } +} + +// NewDeletionRule creates a rule to only allow users with bypass permissions to delete matching refs. +func NewDeletionRule() (rule RepositoryRule) { + return RepositoryRule{ + Type: "deletion", + } +} + +// NewRequiredLinearHistoryRule creates a rule to prevent merge commits from being pushed to matching branches. +func NewRequiredLinearHistoryRule() (rule RepositoryRule) { + return RepositoryRule{ + Type: "required_linear_history", + } +} + +// NewRequiredDeploymentsRule creates a rule to require environments to be successfully deployed before they can be merged into the matching branches. +func NewRequiredDeploymentsRule(params *RequiredDeploymentEnvironmentsRuleParameters) (rule RepositoryRule) { + return RepositoryRule{ + Type: "required_deployments", + Parameters: params, + } +} + +// NewRequiredSignaturesRule creates a rule a to require commits pushed to matching branches to have verified signatures. +func NewRequiredSignaturesRule() (rule RepositoryRule) { + return RepositoryRule{ + Type: "required_signatures", + } +} + +// NewPullRequestRule creates a rule to require all commits be made to a non-target branch and submitted via a pull request before they can be merged. +func NewPullRequestRule(params *PullRequestRuleParameters) ( + rule RepositoryRule) { + return RepositoryRule{ + Type: "pull_request", + Parameters: params, + } +} + +// NewRequiredStatusChecksRule creates a rule to require which status checks must pass before branches can be merged into a branch rule. +func NewRequiredStatusChecksRule(params *RequiredStatusChecksRuleParameters) (rule RepositoryRule) { + return RepositoryRule{ + Type: "required_status_checks", + Parameters: params, + } +} + +// NewNonFastForwardRule creates a rule as part to prevent users with push access from force pushing to matching branches. +func NewNonFastForwardRule() (rule RepositoryRule) { + return RepositoryRule{ + Type: "non_fast_forward", + } +} + +// NewCommitMessagePatternRule creates a rule to restrict commit message patterns being pushed to matching branches. +func NewCommitMessagePatternRule(pattern *RulePatternParameters) (rule RepositoryRule) { + return RepositoryRule{ + Type: "commit_message_pattern", + Parameters: pattern, + } +} + +// NewCommitAuthorEmailPatternRule creates a rule to restrict commits with author email patterns being merged into matching branches. +func NewCommitAuthorEmailPatternRule(pattern *RulePatternParameters) (rule RepositoryRule) { + return RepositoryRule{ + Type: "commit_author_email_pattern", + Parameters: pattern, + } +} + +// NewCommitterEmailPatternRule creates a rule to restrict commits with committer email patterns being merged into matching branches. +func NewCommitterEmailPatternRule(pattern *RulePatternParameters) (rule RepositoryRule) { + return RepositoryRule{ + Type: "committer_email_pattern", + Parameters: pattern, + } +} + +// NewBranchNamePatternRule creates a rule to restrict branch patterns from being merged into matching branches. +func NewBranchNamePatternRule(pattern *RulePatternParameters) (rule RepositoryRule) { + return RepositoryRule{ + Type: "branch_name_pattern", + Parameters: pattern, + } +} + +// NewTagNamePatternRule creates a rule to restrict tag patterns contained in non-target branches from being merged into matching branches. +func NewTagNamePatternRule(pattern *RulePatternParameters) (rule RepositoryRule) { + return RepositoryRule{ + Type: "tag_name_pattern", + Parameters: pattern, + } +} + +// Ruleset represents a GitHub ruleset object. +type Ruleset struct { + ID int64 `json:"id"` + Name string `json:"name"` + // Possible values for Target are branch, tag + Target *string `json:"target,omitempty"` + // Possible values for SourceType are: Repository, Organization + SourceType *string `json:"source_type,omitempty"` + Source string `json:"source"` + // Possible values for Enforcement are: disabled, active, evaluate + Enforcement string `json:"enforcement"` + // Possible values for BypassMode are: none, repository, organization + BypassMode *string `json:"bypass_mode,omitempty"` + BypassActors *[]BypassActor `json:"bypass_actors,omitempty"` + NodeID *string `json:"node_id,omitempty"` + Links *RulesetLinks `json:"_links,omitempty"` + Conditions *RulesetConditions `json:"conditions,omitempty"` + Rules *[]RepositoryRule `json:"rules,omitempty"` +} + +// GetRulesForBranch gets all the rules that apply to the specified branch. +// +// GitHub API docs: https://docs.github.com/en/rest/repos/rules#get-rules-for-a-branch +func (s *RepositoriesService) GetRulesForBranch(ctx context.Context, owner, repo, branch string) ([]*RepositoryRule, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/rules/branches/%v", owner, repo, branch) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var rules []*RepositoryRule + resp, err := s.client.Do(ctx, req, &rules) + if err != nil { + return nil, resp, err + } + + return rules, resp, nil +} + +// GetAllRulesets gets all the rules that apply to the specified repository. +// If includesParents is true, rulesets configured at the organisation level that apply to the repository will be returned. +// +// GitHub API docs: https://docs.github.com/en/rest/repos/rules#get-all-repository-rulesets +func (s *RepositoriesService) GetAllRulesets(ctx context.Context, owner, repo string, includesParents bool) ([]*Ruleset, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/rulesets?includes_parents=%v", owner, repo, includesParents) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var ruleset []*Ruleset + resp, err := s.client.Do(ctx, req, &ruleset) + if err != nil { + return nil, resp, err + } + + return ruleset, resp, nil +} + +// CreateRuleset creates a ruleset for the specified repository. +// +// GitHub API docs: https://docs.github.com/en/rest/repos/rules#create-a-repository-ruleset +func (s *RepositoriesService) CreateRuleset(ctx context.Context, owner, repo string, rs *Ruleset) (*Ruleset, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/rulesets", owner, repo) + + req, err := s.client.NewRequest("POST", u, rs) + if err != nil { + return nil, nil, err + } + + var ruleset *Ruleset + resp, err := s.client.Do(ctx, req, &ruleset) + if err != nil { + return nil, resp, err + } + + return ruleset, resp, nil +} + +// GetRuleset gets a ruleset for the specified repository. +// If includesParents is true, rulesets configured at the organisation level that apply to the repository will be returned. +// +// GitHub API docs: https://docs.github.com/en/rest/repos/rules#get-a-repository-ruleset +func (s *RepositoriesService) GetRuleset(ctx context.Context, owner, repo string, rulesetID int64, includesParents bool) (*Ruleset, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/rulesets/%v?includes_parents=%v", owner, repo, rulesetID, includesParents) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var ruleset *Ruleset + resp, err := s.client.Do(ctx, req, &ruleset) + if err != nil { + return nil, resp, err + } + + return ruleset, resp, nil +} + +// UpdateRuleset updates a ruleset for the specified repository. +// +// GitHub API docs: https://docs.github.com/en/rest/repos/rules#update-a-repository-ruleset +func (s *RepositoriesService) UpdateRuleset(ctx context.Context, owner, repo string, rulesetID int64, rs *Ruleset) (*Ruleset, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/rulesets/%v", owner, repo, rulesetID) + + req, err := s.client.NewRequest("PUT", u, rs) + if err != nil { + return nil, nil, err + } + + var ruleset *Ruleset + resp, err := s.client.Do(ctx, req, &ruleset) + if err != nil { + return nil, resp, err + } + + return ruleset, resp, nil +} + +// DeleteRuleset deletes a ruleset for the specified repository. +// +// GitHub API docs: https://docs.github.com/en/rest/repos/rules#delete-a-repository-ruleset +func (s *RepositoriesService) DeleteRuleset(ctx context.Context, owner, repo string, rulesetID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/rulesets/%v", owner, repo, rulesetID) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/github/repos_rules_test.go b/github/repos_rules_test.go new file mode 100644 index 00000000000..5c609cafb21 --- /dev/null +++ b/github/repos_rules_test.go @@ -0,0 +1,421 @@ +// Copyright 2023 The go-github AUTHORS. All rights reserved. +// +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package github + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + "testing" + + "github.com/google/go-cmp/cmp" +) + +func TestRepositoryRule_UnmarshalJSON(t *testing.T) { + tests := []struct { + in *RepositoryRule + want string + wantErr bool + }{ + { + in: &RepositoryRule{ + Type: "update", + Parameters: &UpdateAllowsFetchAndMergeRuleParameters{ + UpdateAllowsFetchAndMerge: true, + }, + }, + want: `{`, + wantErr: true, + }, + { + in: &RepositoryRule{ + Type: "required_deployments", + Parameters: &RequiredDeploymentEnvironmentsRuleParameters{ + RequiredDeploymentEnvironments: []string{"test"}, + }, + }, + want: `{`, + wantErr: true, + }, + { + in: &RepositoryRule{ + Type: "commit_message_pattern", + Parameters: &RulePatternParameters{ + Name: String("avoid test commits"), + Negate: Bool(true), + Operator: "starts_with", + Pattern: "[test]", + }, + }, + want: `{`, + wantErr: true, + }, + { + in: &RepositoryRule{ + Type: "commit_author_email_pattern", + Parameters: &RulePatternParameters{ + Operator: "contains", + Pattern: "github", + }, + }, + want: `{`, + wantErr: true, + }, + { + in: &RepositoryRule{ + Type: "committer_email_pattern", + Parameters: &RulePatternParameters{ + Name: String("avoid commit emails"), + Negate: Bool(true), + Operator: "ends_with", + Pattern: "abc", + }, + }, + want: `{`, + wantErr: true, + }, + { + in: &RepositoryRule{ + Type: "branch_name_pattern", + Parameters: &RulePatternParameters{ + Name: String("avoid branch names"), + Negate: Bool(true), + Operator: "regex", + Pattern: "github$", + }, + }, + want: `{`, + wantErr: true, + }, + { + in: &RepositoryRule{ + Type: "tag_name_pattern", + Parameters: &RulePatternParameters{ + Name: String("avoid tag names"), + Negate: Bool(true), + Operator: "contains", + Pattern: "github", + }, + }, + want: `{`, + wantErr: true, + }, + { + in: &RepositoryRule{ + Type: "pull_request", + Parameters: &PullRequestRuleParameters{ + RequireCodeOwnerReview: true, + RequireLastPushApproval: true, + RequiredApprovingReviewCount: 1, + RequiredReviewThreadResolution: true, + DismissStaleReviewsOnPush: true, + }, + }, + want: `{`, + wantErr: true, + }, + { + in: &RepositoryRule{ + Type: "required_status_checks", + Parameters: &RequiredStatusChecksRuleParameters{ + RequiredStatusChecks: []RuleRequiredStatusChecks{ + { + Context: "test", + IntegrationID: Int64(1), + }, + }, + StrictRequiredStatusChecksPolicy: true, + }, + }, + want: `{`, + wantErr: true, + }, + { + in: &RepositoryRule{ + Type: "unknown", + }, + want: `{`, + wantErr: true, + }, + } + + for _, tc := range tests { + err := json.Unmarshal([]byte(tc.want), tc.in) + if err == nil && tc.wantErr { + t.Errorf("RepositoryRule.UnmarshalJSON returned nil instead of an error") + } + if err != nil && !tc.wantErr { + t.Errorf("RepositoryRule.UnmarshalJSON returned an unexpected error: %+v", err) + } + } +} + +func TestRepositoriesService_GetRulesForBranch(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/repo/rules/branches/branch", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[ + { + "type": "creation" + }, + { + "type": "update", + "parameters": { + "update_allows_fetch_and_merge": true + } + } + ]`) + }) + + ctx := context.Background() + rules, _, err := client.Repositories.GetRulesForBranch(ctx, "o", "repo", "branch") + if err != nil { + t.Errorf("Repositories.GetRulesForBranch returned error: %v", err) + } + + creationRule := NewCreationRule() + updateRule := NewUpdateRule(&UpdateAllowsFetchAndMergeRuleParameters{ + UpdateAllowsFetchAndMerge: true, + }) + + want := []*RepositoryRule{ + &creationRule, + &updateRule, + } + if !cmp.Equal(rules, want) { + t.Errorf("Repositories.GetRulesForBranch returned %+v, want %+v", rules, want) + } + + const methodName = "GetRulesForBranch" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetRulesForBranch(ctx, "o", "repo", "branch") + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_GetAllRulesets(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/repo/rulesets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `[ + { + "id": 42, + "name": "ruleset", + "source_type": "Repository", + "source": "o/repo", + "enforcement": "enabled" + }, + { + "id": 314, + "name": "Another ruleset", + "source_type": "Repository", + "source": "o/repo", + "enforcement": "enabled" + } + ]`) + }) + + ctx := context.Background() + ruleSet, _, err := client.Repositories.GetAllRulesets(ctx, "o", "repo", false) + if err != nil { + t.Errorf("Repositories.GetAllRulesets returned error: %v", err) + } + + want := []*Ruleset{ + { + ID: 42, + Name: "ruleset", + SourceType: String("Repository"), + Source: "o/repo", + Enforcement: "enabled", + }, + { + ID: 314, + Name: "Another ruleset", + SourceType: String("Repository"), + Source: "o/repo", + Enforcement: "enabled", + }, + } + if !cmp.Equal(ruleSet, want) { + t.Errorf("Repositories.GetAllRulesets returned %+v, want %+v", ruleSet, want) + } + + const methodName = "GetAllRulesets" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetAllRulesets(ctx, "o", "repo", false) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_CreateRuleset(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/repo/rulesets", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "POST") + fmt.Fprint(w, `{ + "id": 42, + "name": "ruleset", + "source_type": "Repository", + "source": "o/repo", + "enforcement": "enabled" + }`) + }) + + ctx := context.Background() + ruleSet, _, err := client.Repositories.CreateRuleset(ctx, "o", "repo", &Ruleset{ + Name: "ruleset", + Enforcement: "enabled", + }) + if err != nil { + t.Errorf("Repositories.CreateRuleset returned error: %v", err) + } + + want := &Ruleset{ + ID: 42, + Name: "ruleset", + SourceType: String("Repository"), + Source: "o/repo", + Enforcement: "enabled", + } + if !cmp.Equal(ruleSet, want) { + t.Errorf("Repositories.CreateRuleset returned %+v, want %+v", ruleSet, want) + } + + const methodName = "CreateRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.CreateRuleset(ctx, "o", "repo", &Ruleset{}) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_GetRuleset(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/repo/rulesets/42", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "GET") + fmt.Fprint(w, `{ + "id": 42, + "name": "ruleset", + "source_type": "Organization", + "source": "o", + "enforcement": "enabled" + }`) + }) + + ctx := context.Background() + ruleSet, _, err := client.Repositories.GetRuleset(ctx, "o", "repo", 42, true) + if err != nil { + t.Errorf("Repositories.GetRuleset returned error: %v", err) + } + + want := &Ruleset{ + ID: 42, + Name: "ruleset", + SourceType: String("Organization"), + Source: "o", + Enforcement: "enabled", + } + if !cmp.Equal(ruleSet, want) { + t.Errorf("Repositories.GetRuleset returned %+v, want %+v", ruleSet, want) + } + + const methodName = "GetRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.GetRuleset(ctx, "o", "repo", 42, true) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_UpdateRuleset(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/repo/rulesets/42", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "PUT") + fmt.Fprint(w, `{ + "id": 42, + "name": "ruleset", + "source_type": "Repository", + "source": "o/repo", + "enforcement": "enabled" + }`) + }) + + ctx := context.Background() + ruleSet, _, err := client.Repositories.UpdateRuleset(ctx, "o", "repo", 42, &Ruleset{ + Name: "ruleset", + Enforcement: "enabled", + }) + if err != nil { + t.Errorf("Repositories.UpdateRuleset returned error: %v", err) + } + + want := &Ruleset{ + ID: 42, + Name: "ruleset", + SourceType: String("Repository"), + Source: "o/repo", + Enforcement: "enabled", + } + if !cmp.Equal(ruleSet, want) { + t.Errorf("Repositories.UpdateRuleset returned %+v, want %+v", ruleSet, want) + } + + const methodName = "UpdateRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + got, resp, err := client.Repositories.UpdateRuleset(ctx, "o", "repo", 42, nil) + if got != nil { + t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) + } + return resp, err + }) +} + +func TestRepositoriesService_DeleteRuleset(t *testing.T) { + client, mux, _, teardown := setup() + defer teardown() + + mux.HandleFunc("/repos/o/repo/rulesets/42", func(w http.ResponseWriter, r *http.Request) { + testMethod(t, r, "DELETE") + }) + + ctx := context.Background() + _, err := client.Repositories.DeleteRuleset(ctx, "o", "repo", 42) + if err != nil { + t.Errorf("Repositories.DeleteRuleset returned error: %v", err) + } + + const methodName = "DeleteRuleset" + + testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { + return client.Repositories.DeleteRuleset(ctx, "o", "repo", 42) + }) +}