diff --git a/github/github-accessors.go b/github/github-accessors.go index d6eb81ebf72..5d99b57f566 100644 --- a/github/github-accessors.go +++ b/github/github-accessors.go @@ -5166,6 +5166,22 @@ func (c *ClusterStatusNodeServiceItem) GetStatus() string { return *c.Status } +// GetMaxCoverageDrop returns the MaxCoverageDrop field if it's non-nil, zero value otherwise. +func (c *CodeCoverageRuleParameters) GetMaxCoverageDrop() float64 { + if c == nil || c.MaxCoverageDrop == nil { + return 0 + } + return *c.MaxCoverageDrop +} + +// GetMinimumCoverage returns the MinimumCoverage field if it's non-nil, zero value otherwise. +func (c *CodeCoverageRuleParameters) GetMinimumCoverage() float64 { + if c == nil || c.MinimumCoverage == nil { + return 0 + } + return *c.MinimumCoverage +} + // GetBody returns the Body field if it's non-nil, zero value otherwise. func (c *CodeOfConduct) GetBody() string { if c == nil || c.Body == nil { @@ -38886,6 +38902,14 @@ func (r *RepositoryRulesetRules) GetBranchNamePattern() *PatternRuleParameters { return r.BranchNamePattern } +// GetCodeCoverage returns the CodeCoverage field. +func (r *RepositoryRulesetRules) GetCodeCoverage() *CodeCoverageRuleParameters { + if r == nil { + return nil + } + return r.CodeCoverage +} + // GetCodeScanning returns the CodeScanning field. func (r *RepositoryRulesetRules) GetCodeScanning() *CodeScanningRuleParameters { if r == nil { diff --git a/github/github-accessors_test.go b/github/github-accessors_test.go index 8d0098a8944..a3f013d31b0 100644 --- a/github/github-accessors_test.go +++ b/github/github-accessors_test.go @@ -6550,6 +6550,28 @@ func TestClusterStatusNodeServiceItem_GetStatus(tt *testing.T) { c.GetStatus() } +func TestCodeCoverageRuleParameters_GetMaxCoverageDrop(tt *testing.T) { + tt.Parallel() + var zeroValue float64 + c := &CodeCoverageRuleParameters{MaxCoverageDrop: &zeroValue} + c.GetMaxCoverageDrop() + c = &CodeCoverageRuleParameters{} + c.GetMaxCoverageDrop() + c = nil + c.GetMaxCoverageDrop() +} + +func TestCodeCoverageRuleParameters_GetMinimumCoverage(tt *testing.T) { + tt.Parallel() + var zeroValue float64 + c := &CodeCoverageRuleParameters{MinimumCoverage: &zeroValue} + c.GetMinimumCoverage() + c = &CodeCoverageRuleParameters{} + c.GetMinimumCoverage() + c = nil + c.GetMinimumCoverage() +} + func TestCodeOfConduct_GetBody(tt *testing.T) { tt.Parallel() var zeroValue string @@ -48538,6 +48560,14 @@ func TestRepositoryRulesetRules_GetBranchNamePattern(tt *testing.T) { r.GetBranchNamePattern() } +func TestRepositoryRulesetRules_GetCodeCoverage(tt *testing.T) { + tt.Parallel() + r := &RepositoryRulesetRules{} + r.GetCodeCoverage() + r = nil + r.GetCodeCoverage() +} + func TestRepositoryRulesetRules_GetCodeScanning(tt *testing.T) { tt.Parallel() r := &RepositoryRulesetRules{} diff --git a/github/rules.go b/github/rules.go index 9d2230a5a2c..c041c8454a5 100644 --- a/github/rules.go +++ b/github/rules.go @@ -72,6 +72,7 @@ type RepositoryRuleType string const ( // Branch or tag target rules. RulesetRuleTypeBranchNamePattern RepositoryRuleType = "branch_name_pattern" + RulesetRuleTypeCodeCoverage RepositoryRuleType = "code_coverage" RulesetRuleTypeCodeScanning RepositoryRuleType = "code_scanning" RulesetRuleTypeCommitAuthorEmailPattern RepositoryRuleType = "commit_author_email_pattern" RulesetRuleTypeCommitMessagePattern RepositoryRuleType = "commit_message_pattern" @@ -308,6 +309,7 @@ type RepositoryRulesetRules struct { TagNamePattern *PatternRuleParameters Workflows *WorkflowsRuleParameters CodeScanning *CodeScanningRuleParameters + CodeCoverage *CodeCoverageRuleParameters CopilotCodeReview *CopilotCodeReviewRuleParameters // Push target rules. @@ -579,6 +581,12 @@ type CodeScanningRuleParameters struct { CodeScanningTools []*RuleCodeScanningTool `json:"code_scanning_tools"` } +// CodeCoverageRuleParameters represents the code_coverage rule parameters. +type CodeCoverageRuleParameters struct { + MaxCoverageDrop *float64 `json:"max_coverage_drop,omitempty"` + MinimumCoverage *float64 `json:"minimum_coverage,omitempty"` +} + // CopilotCodeReviewRuleParameters represents the copilot_code_review rule parameters. type CopilotCodeReviewRuleParameters struct { ReviewOnPush bool `json:"review_on_push"` @@ -783,6 +791,14 @@ func (r RepositoryRulesetRules) MarshalJSON() ([]byte, error) { rawRules = append(rawRules, json.RawMessage(bytes)) } + if r.CodeCoverage != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeCodeCoverage, r.CodeCoverage) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + if r.CopilotCodeReview != nil { bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeCopilotCodeReview, r.CopilotCodeReview) if err != nil { @@ -1022,6 +1038,14 @@ func (r *RepositoryRulesetRules) UnmarshalJSON(data []byte) error { return err } } + case RulesetRuleTypeCodeCoverage: + r.CodeCoverage = &CodeCoverageRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.CodeCoverage); err != nil { + return err + } + } case RulesetRuleTypeCopilotCodeReview: r.CopilotCodeReview = &CopilotCodeReviewRuleParameters{} @@ -1404,6 +1428,16 @@ func (r *RepositoryRule) UnmarshalJSON(data []byte) error { } } + r.Parameters = p + case RulesetRuleTypeCodeCoverage: + p := &CodeCoverageRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + r.Parameters = p case RulesetRuleTypeCopilotCodeReview: p := &CopilotCodeReviewRuleParameters{} diff --git a/github/rules_test.go b/github/rules_test.go index 56ad4077313..69472003beb 100644 --- a/github/rules_test.go +++ b/github/rules_test.go @@ -7,11 +7,99 @@ package github import ( "encoding/json" + "math" "testing" "github.com/google/go-cmp/cmp" ) +func TestCodeCoverageRuleRoundTrip(t *testing.T) { + t.Parallel() + tests := []struct { + name string + params CodeCoverageRuleParameters + json string + }{ + { + "fractional_thresholds", + CodeCoverageRuleParameters{MaxCoverageDrop: new(2.5), MinimumCoverage: new(80.5)}, + `{"max_coverage_drop":2.5,"minimum_coverage":80.5}`, + }, + { + "minimum_only", + CodeCoverageRuleParameters{MinimumCoverage: new(80.5)}, + `{"minimum_coverage":80.5}`, + }, + { + "no_coverage_drop", + CodeCoverageRuleParameters{MaxCoverageDrop: new(0.0)}, + `{"max_coverage_drop":0}`, + }, + { + "zero_thresholds", + CodeCoverageRuleParameters{MaxCoverageDrop: new(0.0), MinimumCoverage: new(0.0)}, + `{"max_coverage_drop":0,"minimum_coverage":0}`, + }, + { + "empty_parameters", + CodeCoverageRuleParameters{}, + `{}`, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + ruleJSON := `{"type":"code_coverage","parameters":` + tt.json + `}` + testJSONMarshal(t, &RepositoryRule{ + Type: RulesetRuleTypeCodeCoverage, + Parameters: &tt.params, + }, ruleJSON) + testJSONMarshal(t, &RepositoryRulesetRules{CodeCoverage: &tt.params}, `[`+ruleJSON+`]`) + }) + } + + t.Run("missing_parameters", func(t *testing.T) { + t.Parallel() + testJSONUnmarshalOnly(t, &RepositoryRule{ + Type: RulesetRuleTypeCodeCoverage, + Parameters: &CodeCoverageRuleParameters{}, + }, `{"type":"code_coverage"}`) + testJSONUnmarshalOnly(t, &RepositoryRulesetRules{ + CodeCoverage: &CodeCoverageRuleParameters{}, + }, `[{"type":"code_coverage"}]`) + }) +} + +func TestCodeCoverageRuleInvalidParameters(t *testing.T) { + t.Parallel() + for _, params := range []string{ + `{"max_coverage_drop":"invalid"}`, + `{"minimum_coverage":"invalid"}`, + `"not_an_object"`, + } { + t.Run(params, func(t *testing.T) { + t.Parallel() + data := `{"type":"code_coverage","parameters":` + params + `}` + if err := json.Unmarshal([]byte(data), &RepositoryRule{}); err == nil { + t.Errorf("Expected error unmarshaling %q, got nil", data) + } + if err := json.Unmarshal([]byte(`[`+data+`]`), &RepositoryRulesetRules{}); err == nil { + t.Errorf("Expected error unmarshaling [%v], got nil", data) + } + }) + } +} + +func TestRepositoryRulesetRules_CodeCoverageMarshalError(t *testing.T) { + t.Parallel() + rules := &RepositoryRulesetRules{ + CodeCoverage: &CodeCoverageRuleParameters{MinimumCoverage: new(math.NaN())}, + } + if _, err := json.Marshal(rules); err == nil { + t.Error("Expected error marshaling a NaN coverage threshold, got nil") + } +} + func TestRepositoryRulesetRules(t *testing.T) { t.Parallel() tests := []struct {