Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions github/github-accessors.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions github/github-accessors_test.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 34 additions & 0 deletions github/rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -308,6 +309,7 @@ type RepositoryRulesetRules struct {
TagNamePattern *PatternRuleParameters
Workflows *WorkflowsRuleParameters
CodeScanning *CodeScanningRuleParameters
CodeCoverage *CodeCoverageRuleParameters
CopilotCodeReview *CopilotCodeReviewRuleParameters

// Push target rules.
Expand Down Expand Up @@ -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"`
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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{}

Expand Down Expand Up @@ -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{}
Expand Down
88 changes: 88 additions & 0 deletions github/rules_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Loading