mirror of
https://github.com/google/go-github.git
synced 2026-10-29 00:44:52 +08:00
1466 lines
54 KiB
Go
1466 lines
54 KiB
Go
// Copyright 2025 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 (
|
|
"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 TestCodeQualityRuleRoundTrip(t *testing.T) {
|
|
t.Parallel()
|
|
for _, severity := range []CodeQualitySeverity{
|
|
CodeQualitySeverityErrors,
|
|
CodeQualitySeverityWarnings,
|
|
CodeQualitySeverityNotes,
|
|
CodeQualitySeverityAll,
|
|
} {
|
|
t.Run(string(severity), func(t *testing.T) {
|
|
t.Parallel()
|
|
params := CodeQualityRuleParameters{Severity: severity}
|
|
ruleJSON := `{"type":"code_quality","parameters":{"severity":"` + string(severity) + `"}}`
|
|
testJSONMarshal(t, &RepositoryRule{
|
|
Type: RulesetRuleTypeCodeQuality,
|
|
Parameters: ¶ms,
|
|
}, ruleJSON)
|
|
testJSONMarshal(t, &RepositoryRulesetRules{CodeQuality: ¶ms}, `[`+ruleJSON+`]`)
|
|
})
|
|
}
|
|
|
|
t.Run("missing_parameters", func(t *testing.T) {
|
|
t.Parallel()
|
|
testJSONUnmarshalOnly(t, &RepositoryRule{
|
|
Type: RulesetRuleTypeCodeQuality,
|
|
Parameters: &CodeQualityRuleParameters{},
|
|
}, `{"type":"code_quality"}`)
|
|
testJSONUnmarshalOnly(t, &RepositoryRulesetRules{
|
|
CodeQuality: &CodeQualityRuleParameters{},
|
|
}, `[{"type":"code_quality"}]`)
|
|
testJSONUnmarshalOnly(t, &BranchRules{
|
|
CodeQuality: []*CodeQualityBranchRule{{
|
|
BranchRuleMetadata: BranchRuleMetadata{RulesetSourceType: RulesetSourceTypeRepository, RulesetSource: "test/test", RulesetID: 1},
|
|
}},
|
|
}, `[{"type":"code_quality","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1}]`)
|
|
})
|
|
}
|
|
|
|
func TestBranchRules_CodeQuality(t *testing.T) {
|
|
t.Parallel()
|
|
testJSONUnmarshalOnly(t, &BranchRules{
|
|
CodeQuality: []*CodeQualityBranchRule{{
|
|
BranchRuleMetadata: BranchRuleMetadata{RulesetSourceType: RulesetSourceTypeOrganization, RulesetSource: "test", RulesetID: 2},
|
|
Parameters: CodeQualityRuleParameters{Severity: CodeQualitySeverityErrors},
|
|
}},
|
|
}, `[{"type":"code_quality","ruleset_source_type":"Organization","ruleset_source":"test","ruleset_id":2,"parameters":{"severity":"errors"}}]`)
|
|
}
|
|
|
|
func TestCodeQualityRuleInvalidParameters(t *testing.T) {
|
|
t.Parallel()
|
|
for _, params := range []string{
|
|
`{"severity":1}`,
|
|
`"not_an_object"`,
|
|
} {
|
|
t.Run(params, func(t *testing.T) {
|
|
t.Parallel()
|
|
data := `{"type":"code_quality","parameters":` + params + `}`
|
|
if err := json.Unmarshal([]byte(data), &RepositoryRule{}); err == nil {
|
|
t.Errorf("Expected error unmarshaling %q into RepositoryRule, got nil", data)
|
|
}
|
|
if err := json.Unmarshal([]byte(`[`+data+`]`), &RepositoryRulesetRules{}); err == nil {
|
|
t.Errorf("Expected error unmarshaling [%v] into RepositoryRulesetRules, got nil", data)
|
|
}
|
|
if err := json.Unmarshal([]byte(`[`+data+`]`), &BranchRules{}); err == nil {
|
|
t.Errorf("Expected error unmarshaling [%v] into BranchRules, got nil", data)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestRepositoryRulesetRules(t *testing.T) {
|
|
t.Parallel()
|
|
tests := []struct {
|
|
name string
|
|
rules *RepositoryRulesetRules
|
|
json string
|
|
}{
|
|
{"empty", &RepositoryRulesetRules{}, `[]`},
|
|
{
|
|
"single_rule_with_empty_params",
|
|
&RepositoryRulesetRules{Creation: &EmptyRuleParameters{}},
|
|
`[{"type":"creation"}]`,
|
|
},
|
|
{
|
|
"single_rule_with_required_params",
|
|
&RepositoryRulesetRules{
|
|
RequiredDeployments: &RequiredDeploymentsRuleParameters{
|
|
RequiredDeploymentEnvironments: []string{"test"},
|
|
},
|
|
},
|
|
`[{"type":"required_deployments","parameters":{"required_deployment_environments":["test"]}}]`,
|
|
},
|
|
{
|
|
"all_rules_with_required_params",
|
|
&RepositoryRulesetRules{
|
|
Creation: &EmptyRuleParameters{},
|
|
Update: &UpdateRuleParameters{},
|
|
Deletion: &EmptyRuleParameters{},
|
|
RequiredLinearHistory: &EmptyRuleParameters{},
|
|
MergeQueue: &MergeQueueRuleParameters{
|
|
CheckResponseTimeoutMinutes: 5,
|
|
GroupingStrategy: MergeGroupingStrategyAllGreen,
|
|
MaxEntriesToBuild: 10,
|
|
MaxEntriesToMerge: 20,
|
|
MergeMethod: MergeQueueMergeMethodSquash,
|
|
MinEntriesToMerge: 1,
|
|
MinEntriesToMergeWaitMinutes: 15,
|
|
},
|
|
RequiredDeployments: &RequiredDeploymentsRuleParameters{
|
|
RequiredDeploymentEnvironments: []string{"test1", "test2"},
|
|
},
|
|
RequiredSignatures: &EmptyRuleParameters{},
|
|
PullRequest: &PullRequestRuleParameters{
|
|
DismissStaleReviewsOnPush: true,
|
|
RequireCodeOwnerReview: true,
|
|
RequireExtraApprovalForUnattributedChanges: true,
|
|
RequireLastPushApproval: true,
|
|
RequiredApprovingReviewCount: 2,
|
|
RequiredReviewThreadResolution: true,
|
|
},
|
|
RequiredStatusChecks: &RequiredStatusChecksRuleParameters{
|
|
RequiredStatusChecks: []*RuleStatusCheck{
|
|
{Context: "test1"},
|
|
{Context: "test2"},
|
|
},
|
|
StrictRequiredStatusChecksPolicy: true,
|
|
},
|
|
NonFastForward: &EmptyRuleParameters{},
|
|
CommitMessagePattern: &PatternRuleParameters{
|
|
Operator: PatternRuleOperatorStartsWith,
|
|
Pattern: "test",
|
|
},
|
|
CommitAuthorEmailPattern: &PatternRuleParameters{
|
|
Operator: PatternRuleOperatorStartsWith,
|
|
Pattern: "test",
|
|
},
|
|
CommitterEmailPattern: &PatternRuleParameters{
|
|
Operator: PatternRuleOperatorStartsWith,
|
|
Pattern: "test",
|
|
},
|
|
BranchNamePattern: &PatternRuleParameters{
|
|
Operator: PatternRuleOperatorStartsWith,
|
|
Pattern: "test",
|
|
},
|
|
TagNamePattern: &PatternRuleParameters{
|
|
Operator: PatternRuleOperatorStartsWith,
|
|
Pattern: "test",
|
|
},
|
|
FilePathRestriction: &FilePathRestrictionRuleParameters{
|
|
RestrictedFilePaths: []string{"test1", "test2"},
|
|
},
|
|
MaxFilePathLength: &MaxFilePathLengthRuleParameters{MaxFilePathLength: 512},
|
|
FileExtensionRestriction: &FileExtensionRestrictionRuleParameters{
|
|
RestrictedFileExtensions: []string{".exe", ".pkg"},
|
|
},
|
|
MaxFileSize: &MaxFileSizeRuleParameters{MaxFileSize: 1024},
|
|
Workflows: &WorkflowsRuleParameters{
|
|
Workflows: []*RuleWorkflow{
|
|
{Path: ".github/workflows/test1.yaml"},
|
|
{Path: ".github/workflows/test2.yaml"},
|
|
},
|
|
},
|
|
CodeScanning: &CodeScanningRuleParameters{
|
|
CodeScanningTools: []*RuleCodeScanningTool{
|
|
{
|
|
AlertsThreshold: CodeScanningAlertsThresholdAll,
|
|
SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdAll,
|
|
Tool: "test",
|
|
},
|
|
{
|
|
AlertsThreshold: CodeScanningAlertsThresholdNone,
|
|
SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdNone,
|
|
Tool: "test",
|
|
},
|
|
},
|
|
},
|
|
CopilotCodeReview: &CopilotCodeReviewRuleParameters{
|
|
ReviewOnPush: true,
|
|
ReviewDraftPullRequests: false,
|
|
},
|
|
RepositoryCreate: &EmptyRuleParameters{},
|
|
RepositoryDelete: &EmptyRuleParameters{},
|
|
RepositoryName: &SimplePatternRuleParameters{Pattern: "^test-.+", Negate: false},
|
|
RepositoryTransfer: &EmptyRuleParameters{},
|
|
RepositoryVisibility: &RepositoryVisibilityRuleParameters{Internal: false, Private: false, Public: false},
|
|
},
|
|
`[{"type":"creation"},{"type":"update"},{"type":"deletion"},{"type":"required_linear_history"},{"type":"merge_queue","parameters":{"check_response_timeout_minutes":5,"grouping_strategy":"ALLGREEN","max_entries_to_build":10,"max_entries_to_merge":20,"merge_method":"SQUASH","min_entries_to_merge":1,"min_entries_to_merge_wait_minutes":15}},{"type":"required_deployments","parameters":{"required_deployment_environments":["test1","test2"]}},{"type":"required_signatures"},{"type":"pull_request","parameters":{"dismiss_stale_reviews_on_push":true,"require_code_owner_review":true,"require_extra_approval_for_unattributed_changes":true,"require_last_push_approval":true,"required_approving_review_count":2,"required_review_thread_resolution":true}},{"type":"required_status_checks","parameters":{"required_status_checks":[{"context":"test1"},{"context":"test2"}],"strict_required_status_checks_policy":true}},{"type":"non_fast_forward"},{"type":"commit_message_pattern","parameters":{"operator":"starts_with","pattern":"test"}},{"type":"commit_author_email_pattern","parameters":{"operator":"starts_with","pattern":"test"}},{"type":"committer_email_pattern","parameters":{"operator":"starts_with","pattern":"test"}},{"type":"branch_name_pattern","parameters":{"operator":"starts_with","pattern":"test"}},{"type":"tag_name_pattern","parameters":{"operator":"starts_with","pattern":"test"}},{"type":"file_path_restriction","parameters":{"restricted_file_paths":["test1","test2"]}},{"type":"max_file_path_length","parameters":{"max_file_path_length":512}},{"type":"file_extension_restriction","parameters":{"restricted_file_extensions":[".exe",".pkg"]}},{"type":"max_file_size","parameters":{"max_file_size":1024}},{"type":"workflows","parameters":{"workflows":[{"path":".github/workflows/test1.yaml"},{"path":".github/workflows/test2.yaml"}]}},{"type":"code_scanning","parameters":{"code_scanning_tools":[{"alerts_threshold":"all","security_alerts_threshold":"all","tool":"test"},{"alerts_threshold":"none","security_alerts_threshold":"none","tool":"test"}]}},{"type":"copilot_code_review","parameters":{"review_on_push":true,"review_draft_pull_requests":false}},{"type":"repository_create"},{"type":"repository_delete"},{"type":"repository_name","parameters":{"negate":false,"pattern":"^test-.+"}},{"type":"repository_transfer"},{"type":"repository_visibility","parameters":{"internal":false,"private":false,"public":false}}]`,
|
|
},
|
|
{
|
|
"all_rules_with_all_params",
|
|
&RepositoryRulesetRules{
|
|
Creation: &EmptyRuleParameters{},
|
|
Update: &UpdateRuleParameters{UpdateAllowsFetchAndMerge: true},
|
|
Deletion: &EmptyRuleParameters{},
|
|
RequiredLinearHistory: &EmptyRuleParameters{},
|
|
MergeQueue: &MergeQueueRuleParameters{
|
|
CheckResponseTimeoutMinutes: 5,
|
|
GroupingStrategy: MergeGroupingStrategyAllGreen,
|
|
MaxEntriesToBuild: 10,
|
|
MaxEntriesToMerge: 20,
|
|
MergeMethod: MergeQueueMergeMethodSquash,
|
|
MinEntriesToMerge: 1,
|
|
MinEntriesToMergeWaitMinutes: 15,
|
|
},
|
|
RequiredDeployments: &RequiredDeploymentsRuleParameters{
|
|
RequiredDeploymentEnvironments: []string{"test1", "test2"},
|
|
},
|
|
RequiredSignatures: &EmptyRuleParameters{},
|
|
PullRequest: &PullRequestRuleParameters{
|
|
AllowedMergeMethods: []PullRequestMergeMethod{
|
|
PullRequestMergeMethodSquash,
|
|
PullRequestMergeMethodRebase,
|
|
},
|
|
DismissStaleReviewsOnPush: true,
|
|
RequireCodeOwnerReview: true,
|
|
RequireExtraApprovalForUnattributedChanges: true,
|
|
RequireLastPushApproval: true,
|
|
RequiredApprovingReviewCount: 2,
|
|
RequiredReviewThreadResolution: true,
|
|
},
|
|
RequiredStatusChecks: &RequiredStatusChecksRuleParameters{
|
|
DoNotEnforceOnCreate: new(true),
|
|
RequiredStatusChecks: []*RuleStatusCheck{
|
|
{Context: "test1", IntegrationID: new(int64(1))},
|
|
{Context: "test2", IntegrationID: new(int64(2))},
|
|
},
|
|
StrictRequiredStatusChecksPolicy: true,
|
|
},
|
|
NonFastForward: &EmptyRuleParameters{},
|
|
CommitMessagePattern: &PatternRuleParameters{
|
|
Name: new("cmp"),
|
|
Negate: new(false),
|
|
Operator: PatternRuleOperatorStartsWith,
|
|
Pattern: "test",
|
|
},
|
|
CommitAuthorEmailPattern: &PatternRuleParameters{
|
|
Name: new("caep"),
|
|
Negate: new(false),
|
|
Operator: PatternRuleOperatorStartsWith,
|
|
Pattern: "test",
|
|
},
|
|
CommitterEmailPattern: &PatternRuleParameters{
|
|
Name: new("cep"),
|
|
Negate: new(false),
|
|
Operator: PatternRuleOperatorStartsWith,
|
|
Pattern: "test",
|
|
},
|
|
BranchNamePattern: &PatternRuleParameters{
|
|
Name: new("bp"),
|
|
Negate: new(false),
|
|
Operator: PatternRuleOperatorStartsWith,
|
|
Pattern: "test",
|
|
},
|
|
TagNamePattern: &PatternRuleParameters{
|
|
Name: new("tp"),
|
|
Negate: new(false),
|
|
Operator: PatternRuleOperatorStartsWith,
|
|
Pattern: "test",
|
|
},
|
|
FilePathRestriction: &FilePathRestrictionRuleParameters{
|
|
RestrictedFilePaths: []string{"test1", "test2"},
|
|
},
|
|
MaxFilePathLength: &MaxFilePathLengthRuleParameters{MaxFilePathLength: 512},
|
|
FileExtensionRestriction: &FileExtensionRestrictionRuleParameters{
|
|
RestrictedFileExtensions: []string{".exe", ".pkg"},
|
|
},
|
|
MaxFileSize: &MaxFileSizeRuleParameters{MaxFileSize: 1024},
|
|
Workflows: &WorkflowsRuleParameters{
|
|
DoNotEnforceOnCreate: new(true),
|
|
Workflows: []*RuleWorkflow{
|
|
{
|
|
Path: ".github/workflows/test1.yaml",
|
|
Ref: new("main"),
|
|
RepositoryID: new(int64(1)),
|
|
SHA: new("aaaa"),
|
|
},
|
|
{
|
|
Path: ".github/workflows/test2.yaml",
|
|
Ref: new("main"),
|
|
RepositoryID: new(int64(2)),
|
|
SHA: new("bbbb"),
|
|
},
|
|
},
|
|
},
|
|
CodeScanning: &CodeScanningRuleParameters{
|
|
CodeScanningTools: []*RuleCodeScanningTool{
|
|
{
|
|
AlertsThreshold: CodeScanningAlertsThresholdAll,
|
|
SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdAll,
|
|
Tool: "test",
|
|
},
|
|
{
|
|
AlertsThreshold: CodeScanningAlertsThresholdNone,
|
|
SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdNone,
|
|
Tool: "test",
|
|
},
|
|
},
|
|
},
|
|
CopilotCodeReview: &CopilotCodeReviewRuleParameters{
|
|
ReviewOnPush: true,
|
|
ReviewDraftPullRequests: false,
|
|
},
|
|
RepositoryCreate: &EmptyRuleParameters{},
|
|
RepositoryDelete: &EmptyRuleParameters{},
|
|
RepositoryName: &SimplePatternRuleParameters{Pattern: "^test-.+", Negate: false},
|
|
RepositoryTransfer: &EmptyRuleParameters{},
|
|
RepositoryVisibility: &RepositoryVisibilityRuleParameters{Internal: false, Private: false, Public: false},
|
|
},
|
|
`[{"type":"creation"},{"type":"update","parameters":{"update_allows_fetch_and_merge":true}},{"type":"deletion"},{"type":"required_linear_history"},{"type":"merge_queue","parameters":{"check_response_timeout_minutes":5,"grouping_strategy":"ALLGREEN","max_entries_to_build":10,"max_entries_to_merge":20,"merge_method":"SQUASH","min_entries_to_merge":1,"min_entries_to_merge_wait_minutes":15}},{"type":"required_deployments","parameters":{"required_deployment_environments":["test1","test2"]}},{"type":"required_signatures"},{"type":"pull_request","parameters":{"allowed_merge_methods":["squash","rebase"],"dismiss_stale_reviews_on_push":true,"require_code_owner_review":true,"require_extra_approval_for_unattributed_changes":true,"require_last_push_approval":true,"required_approving_review_count":2,"required_review_thread_resolution":true}},{"type":"required_status_checks","parameters":{"do_not_enforce_on_create":true,"required_status_checks":[{"context":"test1","integration_id":1},{"context":"test2","integration_id":2}],"strict_required_status_checks_policy":true}},{"type":"non_fast_forward"},{"type":"commit_message_pattern","parameters":{"name":"cmp","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"commit_author_email_pattern","parameters":{"name":"caep","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"committer_email_pattern","parameters":{"name":"cep","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"branch_name_pattern","parameters":{"name":"bp","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"tag_name_pattern","parameters":{"name":"tp","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"file_path_restriction","parameters":{"restricted_file_paths":["test1","test2"]}},{"type":"max_file_path_length","parameters":{"max_file_path_length":512}},{"type":"file_extension_restriction","parameters":{"restricted_file_extensions":[".exe",".pkg"]}},{"type":"max_file_size","parameters":{"max_file_size":1024}},{"type":"workflows","parameters":{"do_not_enforce_on_create":true,"workflows":[{"path":".github/workflows/test1.yaml","ref":"main","repository_id":1,"sha":"aaaa"},{"path":".github/workflows/test2.yaml","ref":"main","repository_id":2,"sha":"bbbb"}]}},{"type":"code_scanning","parameters":{"code_scanning_tools":[{"alerts_threshold":"all","security_alerts_threshold":"all","tool":"test"},{"alerts_threshold":"none","security_alerts_threshold":"none","tool":"test"}]}},{"type":"copilot_code_review","parameters":{"review_on_push":true,"review_draft_pull_requests":false}},{"type":"repository_create"},{"type":"repository_delete"},{"type":"repository_name","parameters":{"negate":false,"pattern":"^test-.+"}},{"type":"repository_transfer"},{"type":"repository_visibility","parameters":{"internal":false,"private":false,"public":false}}]`,
|
|
},
|
|
}
|
|
|
|
t.Run("MarshalJSON", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testJSONMarshalOnly(t, test.rules, test.json)
|
|
testJSONMarshalOnly(t, *test.rules, test.json)
|
|
})
|
|
}
|
|
})
|
|
|
|
t.Run("UnmarshalJSON", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got := &RepositoryRulesetRules{}
|
|
err := json.Unmarshal([]byte(test.json), got)
|
|
if err != nil {
|
|
t.Errorf("Unable to unmarshal JSON %v: %v", test.json, err)
|
|
}
|
|
|
|
if diff := cmp.Diff(test.rules, got); diff != "" {
|
|
t.Errorf(
|
|
"json.Unmarshal returned:\n%#v\nwant:\n%#v\ndiff:\n%v",
|
|
got,
|
|
test.rules,
|
|
diff,
|
|
)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
|
|
t.Run("UnmarshalJSON_Error", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
json string
|
|
}{
|
|
{
|
|
"invalid_copilot_code_review_bool",
|
|
`[{"type":"copilot_code_review","parameters":{"review_on_push":"invalid_bool"}}]`,
|
|
},
|
|
{
|
|
"invalid_copilot_code_review_draft_pr",
|
|
`[{"type":"copilot_code_review","parameters":{"review_on_push":true,"review_draft_pull_requests":"not_a_bool"}}]`,
|
|
},
|
|
{
|
|
"invalid_copilot_code_review_parameters",
|
|
`[{"type":"copilot_code_review","parameters":"not_an_object"}]`,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
got := &RepositoryRulesetRules{}
|
|
err := json.Unmarshal([]byte(tt.json), got)
|
|
if err == nil {
|
|
t.Errorf("Expected error unmarshaling %q, got nil", tt.json)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestBranchRules(t *testing.T) {
|
|
t.Parallel()
|
|
tests := []struct {
|
|
name string
|
|
rules *BranchRules
|
|
json string
|
|
}{
|
|
{"empty", &BranchRules{}, `[]`},
|
|
{
|
|
"single_rule_type_single_rule_empty_params",
|
|
&BranchRules{
|
|
Creation: []*BranchRuleMetadata{
|
|
{
|
|
RulesetSourceType: RulesetSourceTypeRepository,
|
|
RulesetSource: "test/test",
|
|
RulesetID: 1,
|
|
},
|
|
},
|
|
},
|
|
`[{"type":"creation","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1}]`,
|
|
},
|
|
{
|
|
"single_rule_type_single_rule_with_params",
|
|
&BranchRules{
|
|
Update: []*UpdateBranchRule{
|
|
{
|
|
BranchRuleMetadata: BranchRuleMetadata{
|
|
RulesetSourceType: RulesetSourceTypeRepository,
|
|
RulesetSource: "test/test",
|
|
RulesetID: 1,
|
|
},
|
|
Parameters: UpdateRuleParameters{UpdateAllowsFetchAndMerge: true},
|
|
},
|
|
},
|
|
},
|
|
`[{"type":"update","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"update_allows_fetch_and_merge":true}}]`,
|
|
},
|
|
{
|
|
"all_rule_types_with_all_parameters",
|
|
&BranchRules{
|
|
Creation: []*BranchRuleMetadata{
|
|
{
|
|
RulesetSourceType: RulesetSourceTypeRepository,
|
|
RulesetSource: "test/test",
|
|
RulesetID: 1,
|
|
},
|
|
},
|
|
Update: []*UpdateBranchRule{
|
|
{
|
|
BranchRuleMetadata: BranchRuleMetadata{
|
|
RulesetSourceType: RulesetSourceTypeRepository,
|
|
RulesetSource: "test/test",
|
|
RulesetID: 1,
|
|
},
|
|
Parameters: UpdateRuleParameters{UpdateAllowsFetchAndMerge: true},
|
|
},
|
|
},
|
|
Deletion: []*BranchRuleMetadata{
|
|
{
|
|
RulesetSourceType: RulesetSourceTypeRepository,
|
|
RulesetSource: "test/test",
|
|
RulesetID: 1,
|
|
},
|
|
},
|
|
RequiredLinearHistory: []*BranchRuleMetadata{
|
|
{
|
|
RulesetSourceType: RulesetSourceTypeRepository,
|
|
RulesetSource: "test/test",
|
|
RulesetID: 1,
|
|
},
|
|
},
|
|
MergeQueue: []*MergeQueueBranchRule{
|
|
{
|
|
BranchRuleMetadata: BranchRuleMetadata{
|
|
RulesetSourceType: RulesetSourceTypeRepository,
|
|
RulesetSource: "test/test",
|
|
RulesetID: 1,
|
|
},
|
|
Parameters: MergeQueueRuleParameters{
|
|
CheckResponseTimeoutMinutes: 5,
|
|
GroupingStrategy: MergeGroupingStrategyAllGreen,
|
|
MaxEntriesToBuild: 10,
|
|
MaxEntriesToMerge: 20,
|
|
MergeMethod: MergeQueueMergeMethodSquash,
|
|
MinEntriesToMerge: 1,
|
|
MinEntriesToMergeWaitMinutes: 15,
|
|
},
|
|
},
|
|
},
|
|
RequiredDeployments: []*RequiredDeploymentsBranchRule{
|
|
{
|
|
BranchRuleMetadata: BranchRuleMetadata{
|
|
RulesetSourceType: RulesetSourceTypeRepository,
|
|
RulesetSource: "test/test",
|
|
RulesetID: 1,
|
|
},
|
|
Parameters: RequiredDeploymentsRuleParameters{
|
|
RequiredDeploymentEnvironments: []string{"test1", "test2"},
|
|
},
|
|
},
|
|
},
|
|
RequiredSignatures: []*BranchRuleMetadata{
|
|
{
|
|
RulesetSourceType: RulesetSourceTypeRepository,
|
|
RulesetSource: "test/test",
|
|
RulesetID: 1,
|
|
},
|
|
},
|
|
PullRequest: []*PullRequestBranchRule{
|
|
{
|
|
BranchRuleMetadata: BranchRuleMetadata{
|
|
RulesetSourceType: RulesetSourceTypeRepository,
|
|
RulesetSource: "test/test",
|
|
RulesetID: 1,
|
|
},
|
|
Parameters: PullRequestRuleParameters{
|
|
AllowedMergeMethods: []PullRequestMergeMethod{
|
|
PullRequestMergeMethodSquash,
|
|
PullRequestMergeMethodRebase,
|
|
},
|
|
DismissStaleReviewsOnPush: true,
|
|
RequireCodeOwnerReview: true,
|
|
RequireExtraApprovalForUnattributedChanges: true,
|
|
RequireLastPushApproval: true,
|
|
RequiredApprovingReviewCount: 2,
|
|
RequiredReviewThreadResolution: true,
|
|
},
|
|
},
|
|
},
|
|
RequiredStatusChecks: []*RequiredStatusChecksBranchRule{
|
|
{
|
|
BranchRuleMetadata: BranchRuleMetadata{
|
|
RulesetSourceType: RulesetSourceTypeRepository,
|
|
RulesetSource: "test/test",
|
|
RulesetID: 1,
|
|
},
|
|
Parameters: RequiredStatusChecksRuleParameters{
|
|
DoNotEnforceOnCreate: new(true),
|
|
RequiredStatusChecks: []*RuleStatusCheck{
|
|
{Context: "test1", IntegrationID: new(int64(1))},
|
|
{Context: "test2", IntegrationID: new(int64(2))},
|
|
},
|
|
StrictRequiredStatusChecksPolicy: true,
|
|
},
|
|
},
|
|
},
|
|
NonFastForward: []*BranchRuleMetadata{
|
|
{
|
|
RulesetSourceType: RulesetSourceTypeRepository,
|
|
RulesetSource: "test/test",
|
|
RulesetID: 1,
|
|
},
|
|
},
|
|
CommitMessagePattern: []*PatternBranchRule{
|
|
{
|
|
BranchRuleMetadata: BranchRuleMetadata{
|
|
RulesetSourceType: RulesetSourceTypeRepository,
|
|
RulesetSource: "test/test",
|
|
RulesetID: 1,
|
|
},
|
|
Parameters: PatternRuleParameters{
|
|
Name: new("cmp"),
|
|
Negate: new(false),
|
|
Operator: PatternRuleOperatorStartsWith,
|
|
Pattern: "test",
|
|
},
|
|
},
|
|
},
|
|
CommitAuthorEmailPattern: []*PatternBranchRule{
|
|
{
|
|
BranchRuleMetadata: BranchRuleMetadata{
|
|
RulesetSourceType: RulesetSourceTypeRepository,
|
|
RulesetSource: "test/test",
|
|
RulesetID: 1,
|
|
},
|
|
Parameters: PatternRuleParameters{
|
|
Name: new("caep"),
|
|
Negate: new(false),
|
|
Operator: PatternRuleOperatorStartsWith,
|
|
Pattern: "test",
|
|
},
|
|
},
|
|
},
|
|
CommitterEmailPattern: []*PatternBranchRule{
|
|
{
|
|
BranchRuleMetadata: BranchRuleMetadata{
|
|
RulesetSourceType: RulesetSourceTypeRepository,
|
|
RulesetSource: "test/test",
|
|
RulesetID: 1,
|
|
},
|
|
Parameters: PatternRuleParameters{
|
|
Name: new("cep"),
|
|
Negate: new(false),
|
|
Operator: PatternRuleOperatorStartsWith,
|
|
Pattern: "test",
|
|
},
|
|
},
|
|
},
|
|
BranchNamePattern: []*PatternBranchRule{
|
|
{
|
|
BranchRuleMetadata: BranchRuleMetadata{
|
|
RulesetSourceType: RulesetSourceTypeRepository,
|
|
RulesetSource: "test/test",
|
|
RulesetID: 1,
|
|
},
|
|
Parameters: PatternRuleParameters{
|
|
Name: new("bp"),
|
|
Negate: new(false),
|
|
Operator: PatternRuleOperatorStartsWith,
|
|
Pattern: "test",
|
|
},
|
|
},
|
|
},
|
|
TagNamePattern: []*PatternBranchRule{
|
|
{
|
|
BranchRuleMetadata: BranchRuleMetadata{
|
|
RulesetSourceType: RulesetSourceTypeRepository,
|
|
RulesetSource: "test/test",
|
|
RulesetID: 1,
|
|
},
|
|
Parameters: PatternRuleParameters{
|
|
Name: new("tp"),
|
|
Negate: new(false),
|
|
Operator: PatternRuleOperatorStartsWith,
|
|
Pattern: "test",
|
|
},
|
|
},
|
|
},
|
|
FilePathRestriction: []*FilePathRestrictionBranchRule{
|
|
{
|
|
BranchRuleMetadata: BranchRuleMetadata{
|
|
RulesetSourceType: RulesetSourceTypeRepository,
|
|
RulesetSource: "test/test",
|
|
RulesetID: 1,
|
|
},
|
|
Parameters: FilePathRestrictionRuleParameters{
|
|
RestrictedFilePaths: []string{"test1", "test2"},
|
|
},
|
|
},
|
|
},
|
|
MaxFilePathLength: []*MaxFilePathLengthBranchRule{
|
|
{
|
|
BranchRuleMetadata: BranchRuleMetadata{
|
|
RulesetSourceType: RulesetSourceTypeRepository,
|
|
RulesetSource: "test/test",
|
|
RulesetID: 1,
|
|
},
|
|
Parameters: MaxFilePathLengthRuleParameters{MaxFilePathLength: 512},
|
|
},
|
|
},
|
|
FileExtensionRestriction: []*FileExtensionRestrictionBranchRule{
|
|
{
|
|
BranchRuleMetadata: BranchRuleMetadata{
|
|
RulesetSourceType: RulesetSourceTypeRepository,
|
|
RulesetSource: "test/test",
|
|
RulesetID: 1,
|
|
},
|
|
Parameters: FileExtensionRestrictionRuleParameters{
|
|
RestrictedFileExtensions: []string{".exe", ".pkg"},
|
|
},
|
|
},
|
|
},
|
|
MaxFileSize: []*MaxFileSizeBranchRule{
|
|
{
|
|
BranchRuleMetadata: BranchRuleMetadata{
|
|
RulesetSourceType: RulesetSourceTypeRepository,
|
|
RulesetSource: "test/test",
|
|
RulesetID: 1,
|
|
},
|
|
Parameters: MaxFileSizeRuleParameters{MaxFileSize: 1024},
|
|
},
|
|
},
|
|
Workflows: []*WorkflowsBranchRule{
|
|
{
|
|
BranchRuleMetadata: BranchRuleMetadata{
|
|
RulesetSourceType: RulesetSourceTypeRepository,
|
|
RulesetSource: "test/test",
|
|
RulesetID: 1,
|
|
},
|
|
Parameters: WorkflowsRuleParameters{
|
|
DoNotEnforceOnCreate: new(true),
|
|
Workflows: []*RuleWorkflow{
|
|
{
|
|
Path: ".github/workflows/test1.yaml",
|
|
Ref: new("main"),
|
|
RepositoryID: new(int64(1)),
|
|
SHA: new("aaaa"),
|
|
},
|
|
{
|
|
Path: ".github/workflows/test2.yaml",
|
|
Ref: new("main"),
|
|
RepositoryID: new(int64(2)),
|
|
SHA: new("bbbb"),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
CodeScanning: []*CodeScanningBranchRule{
|
|
{
|
|
BranchRuleMetadata: BranchRuleMetadata{
|
|
RulesetSourceType: RulesetSourceTypeRepository,
|
|
RulesetSource: "test/test",
|
|
RulesetID: 1,
|
|
},
|
|
Parameters: CodeScanningRuleParameters{
|
|
CodeScanningTools: []*RuleCodeScanningTool{
|
|
{
|
|
AlertsThreshold: CodeScanningAlertsThresholdAll,
|
|
SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdAll,
|
|
Tool: "test",
|
|
},
|
|
{
|
|
AlertsThreshold: CodeScanningAlertsThresholdNone,
|
|
SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdNone,
|
|
Tool: "test",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
CopilotCodeReview: []*CopilotCodeReviewBranchRule{
|
|
{
|
|
BranchRuleMetadata: BranchRuleMetadata{
|
|
RulesetSourceType: RulesetSourceTypeRepository,
|
|
RulesetSource: "test/test",
|
|
RulesetID: 1,
|
|
},
|
|
Parameters: CopilotCodeReviewRuleParameters{
|
|
ReviewOnPush: true,
|
|
ReviewDraftPullRequests: false,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
`[{"type":"creation","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1},{"type":"update","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"update_allows_fetch_and_merge":true}},{"type":"deletion","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1},{"type":"required_linear_history","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1},{"type":"merge_queue","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"check_response_timeout_minutes":5,"grouping_strategy":"ALLGREEN","max_entries_to_build":10,"max_entries_to_merge":20,"merge_method":"SQUASH","min_entries_to_merge":1,"min_entries_to_merge_wait_minutes":15}},{"type":"required_deployments","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"required_deployment_environments":["test1","test2"]}},{"type":"required_signatures","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1},{"type":"pull_request","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"allowed_merge_methods":["squash","rebase"],"dismiss_stale_reviews_on_push":true,"require_code_owner_review":true,"require_extra_approval_for_unattributed_changes":true,"require_last_push_approval":true,"required_approving_review_count":2,"required_review_thread_resolution":true}},{"type":"required_status_checks","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"do_not_enforce_on_create":true,"required_status_checks":[{"context":"test1","integration_id":1},{"context":"test2","integration_id":2}],"strict_required_status_checks_policy":true}},{"type":"non_fast_forward","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1},{"type":"commit_message_pattern","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"name":"cmp","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"commit_author_email_pattern","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"name":"caep","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"committer_email_pattern","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"name":"cep","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"branch_name_pattern","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"name":"bp","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"tag_name_pattern","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"name":"tp","negate":false,"operator":"starts_with","pattern":"test"}},{"type":"file_path_restriction","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"restricted_file_paths":["test1","test2"]}},{"type":"max_file_path_length","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"max_file_path_length":512}},{"type":"file_extension_restriction","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"restricted_file_extensions":[".exe",".pkg"]}},{"type":"max_file_size","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"max_file_size":1024}},{"type":"workflows","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"do_not_enforce_on_create":true,"workflows":[{"path":".github/workflows/test1.yaml","ref":"main","repository_id":1,"sha":"aaaa"},{"path":".github/workflows/test2.yaml","ref":"main","repository_id":2,"sha":"bbbb"}]}},{"type":"code_scanning","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"code_scanning_tools":[{"alerts_threshold":"all","security_alerts_threshold":"all","tool":"test"},{"alerts_threshold":"none","security_alerts_threshold":"none","tool":"test"}]}},{"type":"copilot_code_review","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":{"review_on_push":true,"review_draft_pull_requests":false}}]`,
|
|
},
|
|
}
|
|
|
|
t.Run("UnmarshalJSON", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got := &BranchRules{}
|
|
err := json.Unmarshal([]byte(test.json), got)
|
|
if err != nil {
|
|
t.Errorf("Unable to unmarshal JSON %v: %v", test.json, err)
|
|
}
|
|
|
|
if diff := cmp.Diff(test.rules, got); diff != "" {
|
|
t.Errorf(
|
|
"json.Unmarshal returned:\n%#v\nwant:\n%#v\ndiff:\n%v",
|
|
got,
|
|
test.rules,
|
|
diff,
|
|
)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
|
|
t.Run("UnmarshalJSON_Error", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
json string
|
|
}{
|
|
{
|
|
"invalid_copilot_code_review_parameters",
|
|
`[{"type":"copilot_code_review","ruleset_source_type":"Repository","ruleset_source":"test/test","ruleset_id":1,"parameters":"not_an_object"}]`,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
got := &BranchRules{}
|
|
err := json.Unmarshal([]byte(tt.json), got)
|
|
if err == nil {
|
|
t.Errorf("Expected error unmarshaling %q, got nil", tt.json)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestRepositoryRule(t *testing.T) {
|
|
t.Parallel()
|
|
tests := []struct {
|
|
name string
|
|
rule *RepositoryRule
|
|
json string
|
|
}{
|
|
{
|
|
"empty",
|
|
&RepositoryRule{},
|
|
`{}`,
|
|
},
|
|
{
|
|
"creation",
|
|
&RepositoryRule{Type: RulesetRuleTypeCreation, Parameters: nil},
|
|
`{"type":"creation"}`,
|
|
},
|
|
{
|
|
"update",
|
|
&RepositoryRule{Type: RulesetRuleTypeUpdate, Parameters: &UpdateRuleParameters{}},
|
|
`{"type":"update"}`,
|
|
},
|
|
{
|
|
"update_params_empty",
|
|
&RepositoryRule{Type: RulesetRuleTypeUpdate, Parameters: &UpdateRuleParameters{}},
|
|
`{"type":"update","parameters":{}}`,
|
|
},
|
|
{
|
|
"update_params_set",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypeUpdate,
|
|
Parameters: &UpdateRuleParameters{UpdateAllowsFetchAndMerge: true},
|
|
},
|
|
`{"type":"update","parameters":{"update_allows_fetch_and_merge":true}}`,
|
|
},
|
|
{
|
|
"deletion",
|
|
&RepositoryRule{Type: RulesetRuleTypeDeletion, Parameters: nil},
|
|
`{"type":"deletion"}`,
|
|
},
|
|
{
|
|
"required_linear_history",
|
|
&RepositoryRule{Type: RulesetRuleTypeRequiredLinearHistory, Parameters: nil},
|
|
`{"type":"required_linear_history"}`,
|
|
},
|
|
{
|
|
"merge_queue",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypeMergeQueue,
|
|
Parameters: &MergeQueueRuleParameters{
|
|
CheckResponseTimeoutMinutes: 5,
|
|
GroupingStrategy: MergeGroupingStrategyAllGreen,
|
|
MaxEntriesToBuild: 10,
|
|
MaxEntriesToMerge: 20,
|
|
MergeMethod: MergeQueueMergeMethodSquash,
|
|
MinEntriesToMerge: 1,
|
|
MinEntriesToMergeWaitMinutes: 15,
|
|
},
|
|
},
|
|
`{"type":"merge_queue","parameters":{"check_response_timeout_minutes":5,"grouping_strategy":"ALLGREEN","max_entries_to_build":10,"max_entries_to_merge":20,"merge_method":"SQUASH","min_entries_to_merge":1,"min_entries_to_merge_wait_minutes":15}}`,
|
|
},
|
|
{
|
|
"required_deployments",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypeRequiredDeployments,
|
|
Parameters: &RequiredDeploymentsRuleParameters{
|
|
RequiredDeploymentEnvironments: []string{"test1", "test2"},
|
|
},
|
|
},
|
|
`{"type":"required_deployments","parameters":{"required_deployment_environments":["test1","test2"]}}`,
|
|
},
|
|
{
|
|
"required_signatures",
|
|
&RepositoryRule{Type: RulesetRuleTypeRequiredSignatures, Parameters: nil},
|
|
`{"type":"required_signatures"}`,
|
|
},
|
|
{
|
|
"pull_request",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypePullRequest,
|
|
Parameters: &PullRequestRuleParameters{
|
|
AllowedMergeMethods: []PullRequestMergeMethod{
|
|
PullRequestMergeMethodSquash,
|
|
PullRequestMergeMethodRebase,
|
|
},
|
|
DismissStaleReviewsOnPush: true,
|
|
RequireCodeOwnerReview: true,
|
|
RequireExtraApprovalForUnattributedChanges: true,
|
|
RequireLastPushApproval: true,
|
|
RequiredApprovingReviewCount: 2,
|
|
RequiredReviewThreadResolution: true,
|
|
},
|
|
},
|
|
`{"type":"pull_request","parameters":{"allowed_merge_methods":["squash","rebase"],"dismiss_stale_reviews_on_push":true,"require_code_owner_review":true,"require_extra_approval_for_unattributed_changes":true,"require_last_push_approval":true,"required_approving_review_count":2,"required_review_thread_resolution":true}}`,
|
|
},
|
|
{
|
|
"pull_request_with_required_reviewers",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypePullRequest,
|
|
Parameters: &PullRequestRuleParameters{
|
|
AllowedMergeMethods: []PullRequestMergeMethod{
|
|
PullRequestMergeMethodMerge,
|
|
PullRequestMergeMethodSquash,
|
|
PullRequestMergeMethodRebase,
|
|
},
|
|
DismissStaleReviewsOnPush: false,
|
|
RequireCodeOwnerReview: false,
|
|
RequireExtraApprovalForUnattributedChanges: false,
|
|
RequireLastPushApproval: false,
|
|
RequiredApprovingReviewCount: 0,
|
|
RequiredReviewThreadResolution: false,
|
|
RequiredReviewers: []*RulesetRequiredReviewer{
|
|
{
|
|
MinimumApprovals: new(1),
|
|
FilePatterns: []string{"*"},
|
|
Reviewer: &RulesetReviewer{
|
|
ID: new(int64(123456)),
|
|
Type: new(RulesetReviewerTypeTeam),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
`{"type":"pull_request","parameters":{"allowed_merge_methods":["merge","squash","rebase"],"dismiss_stale_reviews_on_push":false,"require_code_owner_review":false,"require_extra_approval_for_unattributed_changes":false,"require_last_push_approval":false,"required_approving_review_count":0,"required_reviewers":[{"minimum_approvals":1,"file_patterns":["*"],"reviewer":{"id":123456,"type":"Team"}}],"required_review_thread_resolution":false}}`,
|
|
},
|
|
{
|
|
"pull_request_with_dismissal_restriction",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypePullRequest,
|
|
Parameters: &PullRequestRuleParameters{
|
|
DismissalRestriction: &DismissalRestriction{
|
|
AllowedActors: []*DismissalRestrictionActor{
|
|
{ID: 123456, Type: DismissalRestrictionActorTypeTeam},
|
|
{ID: 5, Type: DismissalRestrictionActorTypeRepositoryRole},
|
|
},
|
|
Enabled: true,
|
|
},
|
|
DismissStaleReviewsOnPush: false,
|
|
RequireCodeOwnerReview: false,
|
|
RequireLastPushApproval: false,
|
|
RequiredApprovingReviewCount: 1,
|
|
RequiredReviewThreadResolution: false,
|
|
},
|
|
},
|
|
`{"type":"pull_request","parameters":{"dismissal_restriction":{"allowed_actors":[{"id":123456,"type":"Team"},{"id":5,"type":"RepositoryRole"}],"enabled":true},"dismiss_stale_reviews_on_push":false,"require_code_owner_review":false,"require_last_push_approval":false,"required_approving_review_count":1,"required_review_thread_resolution":false}}`,
|
|
},
|
|
{
|
|
"pull_request_with_dismissal_restricted_to_nobody",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypePullRequest,
|
|
Parameters: &PullRequestRuleParameters{
|
|
DismissalRestriction: &DismissalRestriction{Enabled: true},
|
|
DismissStaleReviewsOnPush: false,
|
|
RequireCodeOwnerReview: false,
|
|
RequireLastPushApproval: false,
|
|
RequiredApprovingReviewCount: 1,
|
|
RequiredReviewThreadResolution: false,
|
|
},
|
|
},
|
|
`{"type":"pull_request","parameters":{"dismissal_restriction":{"enabled":true},"dismiss_stale_reviews_on_push":false,"require_code_owner_review":false,"require_last_push_approval":false,"required_approving_review_count":1,"required_review_thread_resolution":false}}`,
|
|
},
|
|
{
|
|
"pull_request_string_id",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypePullRequest,
|
|
Parameters: &PullRequestRuleParameters{
|
|
AllowedMergeMethods: []PullRequestMergeMethod{
|
|
PullRequestMergeMethodMerge,
|
|
PullRequestMergeMethodSquash,
|
|
PullRequestMergeMethodRebase,
|
|
},
|
|
DismissStaleReviewsOnPush: false,
|
|
RequireCodeOwnerReview: false,
|
|
RequireExtraApprovalForUnattributedChanges: false,
|
|
RequireLastPushApproval: false,
|
|
RequiredApprovingReviewCount: 0,
|
|
RequiredReviewThreadResolution: false,
|
|
RequiredReviewers: []*RulesetRequiredReviewer{
|
|
{
|
|
MinimumApprovals: new(1),
|
|
FilePatterns: []string{"*"},
|
|
Reviewer: &RulesetReviewer{
|
|
ID: new(int64(123456)),
|
|
Type: new(RulesetReviewerTypeTeam),
|
|
},
|
|
},
|
|
},
|
|
},
|
|
},
|
|
`{"type":"pull_request","parameters":{"allowed_merge_methods":["merge","squash","rebase"],"dismiss_stale_reviews_on_push":false,"require_code_owner_review":false,"require_extra_approval_for_unattributed_changes":false,"require_last_push_approval":false,"required_approving_review_count":0,"required_reviewers":[{"minimum_approvals":1,"file_patterns":["*"],"reviewer":{"id":"123456","type":"Team"}}],"required_review_thread_resolution":false}}`,
|
|
},
|
|
{
|
|
"required_status_checks",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypeRequiredStatusChecks,
|
|
Parameters: &RequiredStatusChecksRuleParameters{
|
|
RequiredStatusChecks: []*RuleStatusCheck{
|
|
{Context: "test1"},
|
|
{Context: "test2"},
|
|
},
|
|
StrictRequiredStatusChecksPolicy: true,
|
|
},
|
|
},
|
|
`{"type":"required_status_checks","parameters":{"required_status_checks":[{"context":"test1"},{"context":"test2"}],"strict_required_status_checks_policy":true}}`,
|
|
},
|
|
{
|
|
"non_fast_forward",
|
|
&RepositoryRule{Type: RulesetRuleTypeNonFastForward, Parameters: nil},
|
|
`{"type":"non_fast_forward"}`,
|
|
},
|
|
{
|
|
"commit_message_pattern",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypeCommitMessagePattern,
|
|
Parameters: &PatternRuleParameters{
|
|
Name: new("test"),
|
|
Negate: new(false),
|
|
Operator: PatternRuleOperatorStartsWith,
|
|
Pattern: "test",
|
|
},
|
|
},
|
|
`{"type":"commit_message_pattern","parameters":{"name":"test","negate":false,"operator":"starts_with","pattern":"test"}}`,
|
|
},
|
|
{
|
|
"commit_author_email_pattern",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypeCommitAuthorEmailPattern,
|
|
Parameters: &PatternRuleParameters{
|
|
Name: new("test"),
|
|
Negate: new(false),
|
|
Operator: PatternRuleOperatorStartsWith,
|
|
Pattern: "test",
|
|
},
|
|
},
|
|
`{"type":"commit_author_email_pattern","parameters":{"name":"test","negate":false,"operator":"starts_with","pattern":"test"}}`,
|
|
},
|
|
{
|
|
"committer_email_pattern",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypeCommitterEmailPattern,
|
|
Parameters: &PatternRuleParameters{
|
|
Name: new("test"),
|
|
Negate: new(false),
|
|
Operator: PatternRuleOperatorStartsWith,
|
|
Pattern: "test",
|
|
},
|
|
},
|
|
`{"type":"committer_email_pattern","parameters":{"name":"test","negate":false,"operator":"starts_with","pattern":"test"}}`,
|
|
},
|
|
{
|
|
"branch_name_pattern",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypeBranchNamePattern,
|
|
Parameters: &PatternRuleParameters{
|
|
Name: new("test"),
|
|
Negate: new(false),
|
|
Operator: PatternRuleOperatorStartsWith,
|
|
Pattern: "test",
|
|
},
|
|
},
|
|
`{"type":"branch_name_pattern","parameters":{"name":"test","negate":false,"operator":"starts_with","pattern":"test"}}`,
|
|
},
|
|
{
|
|
"tag_name_pattern",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypeTagNamePattern,
|
|
Parameters: &PatternRuleParameters{
|
|
Name: new("test"),
|
|
Negate: new(false),
|
|
Operator: PatternRuleOperatorStartsWith,
|
|
Pattern: "test",
|
|
},
|
|
},
|
|
`{"type":"tag_name_pattern","parameters":{"name":"test","negate":false,"operator":"starts_with","pattern":"test"}}`,
|
|
},
|
|
{
|
|
"file_path_restriction",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypeFilePathRestriction,
|
|
Parameters: &FilePathRestrictionRuleParameters{
|
|
RestrictedFilePaths: []string{"test1", "test2"},
|
|
},
|
|
},
|
|
`{"type":"file_path_restriction","parameters":{"restricted_file_paths":["test1","test2"]}}`,
|
|
},
|
|
{
|
|
"max_file_path_length",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypeMaxFilePathLength,
|
|
Parameters: &MaxFilePathLengthRuleParameters{MaxFilePathLength: 512},
|
|
},
|
|
`{"type":"max_file_path_length","parameters":{"max_file_path_length":512}}`,
|
|
},
|
|
{
|
|
"file_extension_restriction",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypeFileExtensionRestriction,
|
|
Parameters: &FileExtensionRestrictionRuleParameters{
|
|
RestrictedFileExtensions: []string{".exe", ".pkg"},
|
|
},
|
|
},
|
|
`{"type":"file_extension_restriction","parameters":{"restricted_file_extensions":[".exe",".pkg"]}}`,
|
|
},
|
|
{
|
|
"max_file_size",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypeMaxFileSize,
|
|
Parameters: &MaxFileSizeRuleParameters{MaxFileSize: 1024},
|
|
},
|
|
`{"type":"max_file_size","parameters":{"max_file_size":1024}}`,
|
|
},
|
|
{
|
|
"workflows",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypeWorkflows,
|
|
Parameters: &WorkflowsRuleParameters{
|
|
Workflows: []*RuleWorkflow{
|
|
{Path: ".github/workflows/test1.yaml"},
|
|
{Path: ".github/workflows/test2.yaml"},
|
|
},
|
|
},
|
|
},
|
|
`{"type":"workflows","parameters":{"workflows":[{"path":".github/workflows/test1.yaml"},{"path":".github/workflows/test2.yaml"}]}}`,
|
|
},
|
|
{
|
|
"code_scanning",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypeCodeScanning,
|
|
Parameters: &CodeScanningRuleParameters{
|
|
CodeScanningTools: []*RuleCodeScanningTool{
|
|
{
|
|
AlertsThreshold: CodeScanningAlertsThresholdAll,
|
|
SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdAll,
|
|
Tool: "test",
|
|
},
|
|
{
|
|
AlertsThreshold: CodeScanningAlertsThresholdNone,
|
|
SecurityAlertsThreshold: CodeScanningSecurityAlertsThresholdNone,
|
|
Tool: "test",
|
|
},
|
|
},
|
|
},
|
|
},
|
|
`{"type":"code_scanning","parameters":{"code_scanning_tools":[{"alerts_threshold":"all","security_alerts_threshold":"all","tool":"test"},{"alerts_threshold":"none","security_alerts_threshold":"none","tool":"test"}]}}`,
|
|
},
|
|
{
|
|
"copilot_code_review",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypeCopilotCodeReview,
|
|
Parameters: &CopilotCodeReviewRuleParameters{
|
|
ReviewOnPush: true,
|
|
ReviewDraftPullRequests: false,
|
|
},
|
|
},
|
|
`{"type":"copilot_code_review","parameters":{"review_on_push":true,"review_draft_pull_requests":false}}`,
|
|
},
|
|
{
|
|
"copilot_code_review_empty_params",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypeCopilotCodeReview,
|
|
Parameters: &CopilotCodeReviewRuleParameters{},
|
|
},
|
|
`{"type":"copilot_code_review","parameters":{"review_on_push":false,"review_draft_pull_requests":false}}`,
|
|
},
|
|
{
|
|
"repository_create",
|
|
&RepositoryRule{Type: RulesetRuleTypeRepositoryCreate, Parameters: nil},
|
|
`{"type":"repository_create"}`,
|
|
},
|
|
{
|
|
"repository_delete",
|
|
&RepositoryRule{Type: RulesetRuleTypeRepositoryDelete, Parameters: nil},
|
|
`{"type":"repository_delete"}`,
|
|
},
|
|
{
|
|
"repository_name",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypeRepositoryName,
|
|
Parameters: &SimplePatternRuleParameters{
|
|
Negate: false,
|
|
Pattern: "^test-.+",
|
|
},
|
|
},
|
|
`{"type":"repository_name","parameters":{"negate":false,"pattern":"^test-.+"}}`,
|
|
},
|
|
{
|
|
"repository_transfer",
|
|
&RepositoryRule{Type: RulesetRuleTypeRepositoryTransfer, Parameters: nil},
|
|
`{"type":"repository_transfer"}`,
|
|
},
|
|
{
|
|
"repository_visibility",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypeRepositoryVisibility,
|
|
Parameters: &RepositoryVisibilityRuleParameters{
|
|
Internal: true,
|
|
Private: false,
|
|
Public: true,
|
|
},
|
|
},
|
|
`{"type":"repository_visibility","parameters":{"internal":true,"private":false,"public":true}}`,
|
|
},
|
|
}
|
|
|
|
marshalTests := []struct {
|
|
name string
|
|
rule *RepositoryRule
|
|
json string
|
|
}{
|
|
{
|
|
"creation",
|
|
&RepositoryRule{Type: RulesetRuleTypeCreation, Parameters: nil},
|
|
`{"type":"creation"}`,
|
|
},
|
|
{
|
|
"copilot_code_review",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypeCopilotCodeReview,
|
|
Parameters: &CopilotCodeReviewRuleParameters{
|
|
ReviewOnPush: true,
|
|
ReviewDraftPullRequests: false,
|
|
},
|
|
},
|
|
`{"type":"copilot_code_review","parameters":{"review_on_push":true,"review_draft_pull_requests":false}}`,
|
|
},
|
|
{
|
|
"copilot_code_review_empty_params",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypeCopilotCodeReview,
|
|
Parameters: &CopilotCodeReviewRuleParameters{},
|
|
},
|
|
`{"type":"copilot_code_review","parameters":{"review_on_push":false,"review_draft_pull_requests":false}}`,
|
|
},
|
|
{
|
|
"repository_visibility",
|
|
&RepositoryRule{
|
|
Type: RulesetRuleTypeRepositoryVisibility,
|
|
Parameters: &RepositoryVisibilityRuleParameters{
|
|
Internal: true,
|
|
Private: false,
|
|
Public: true,
|
|
},
|
|
},
|
|
`{"type":"repository_visibility","parameters":{"internal":true,"private":false,"public":true}}`,
|
|
},
|
|
}
|
|
|
|
t.Run("MarshalJSON", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
for _, test := range marshalTests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got, err := json.Marshal(test.rule)
|
|
if err != nil {
|
|
t.Errorf("Unable to marshal JSON for %#v", test.rule)
|
|
}
|
|
|
|
if diff := cmp.Diff(test.json, string(got)); diff != "" {
|
|
t.Errorf(
|
|
"json.Marshal returned:\n%v\nwant:\n%v\ndiff:\n%v",
|
|
string(got),
|
|
test.json,
|
|
diff,
|
|
)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
|
|
t.Run("UnmarshalJSON", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got := &RepositoryRule{}
|
|
err := json.Unmarshal([]byte(test.json), got)
|
|
if err != nil {
|
|
t.Errorf("Unable to unmarshal JSON %v: %v", test.json, err)
|
|
}
|
|
|
|
if diff := cmp.Diff(test.rule, got); diff != "" {
|
|
t.Errorf(
|
|
"json.Unmarshal returned:\n%#v\nwant:\n%#v\ndiff:\n%v",
|
|
got,
|
|
test.rule,
|
|
diff,
|
|
)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
|
|
t.Run("UnmarshalJSON_Error", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
json string
|
|
}{
|
|
{
|
|
"invalid_copilot_code_review_bool",
|
|
`{"type":"copilot_code_review","parameters":{"review_on_push":"invalid_bool"}}`,
|
|
},
|
|
{
|
|
"invalid_copilot_code_review_draft_pr",
|
|
`{"type":"copilot_code_review","parameters":{"review_on_push":true,"review_draft_pull_requests":"not_a_bool"}}`,
|
|
},
|
|
{
|
|
"invalid_copilot_code_review_parameters",
|
|
`{"type":"copilot_code_review","parameters":"not_an_object"}`,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
got := &RepositoryRule{}
|
|
err := json.Unmarshal([]byte(tt.json), got)
|
|
if err == nil {
|
|
t.Errorf("Expected error unmarshaling %q, got nil", tt.json)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestRulesetReviewer_UnmarshalJSON(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
tests := []struct {
|
|
name string
|
|
json string
|
|
wantID int64
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "integer_id",
|
|
json: `{"id": 123456, "type": "Team"}`,
|
|
wantID: 123456,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "string_id",
|
|
json: `{"id": "123456", "type": "Team"}`,
|
|
wantID: 123456,
|
|
wantErr: false,
|
|
},
|
|
{
|
|
name: "invalid_string_id",
|
|
json: `{"id": "not-a-number", "type": "Team"}`,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "invalid_type_id",
|
|
json: `{"id": {}, "type": "Team"}`,
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: "malformed_json",
|
|
json: `{"id":`,
|
|
wantErr: true,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var got RulesetReviewer
|
|
err := json.Unmarshal([]byte(tt.json), &got)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("UnmarshalJSON() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
|
|
if !tt.wantErr {
|
|
if got.GetID() != tt.wantID {
|
|
t.Errorf("UnmarshalJSON() got ID = %v, want %v", got.GetID(), tt.wantID)
|
|
}
|
|
if got.GetType() == nil || *got.GetType() != RulesetReviewerTypeTeam {
|
|
t.Errorf("UnmarshalJSON() got Type = %v, want %v", got.GetType(), RulesetReviewerTypeTeam)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|