Skip to content

Commit e32d34b

Browse files
deigaclaude
andcommitted
test(ruleset): add tests for required_reviewers feature
Add comprehensive unit tests for the expand/flatten helper functions: - TestExpandRequiredReviewers: validates expansion logic - TestExpandRequiredReviewersEmpty: handles edge cases - TestFlattenRequiredReviewers: validates flattening logic - TestFlattenRequiredReviewersEmpty: handles edge cases - TestRoundTripRequiredReviewers: verifies data integrity Add acceptance tests for both organization and repository rulesets: - Single reviewer configuration with file patterns - Multiple reviewers configuration - Update operations (minimum_approvals change) - Import state verification 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent fa8d3eb commit e32d34b

3 files changed

Lines changed: 543 additions & 0 deletions

File tree

github/resource_github_organization_ruleset_test.go

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -941,3 +941,204 @@ func TestOrganizationPushRulesetSupport(t *testing.T) {
941941
t.Errorf("Expected 4 restricted file extensions, got %d", len(restrictedExts))
942942
}
943943
}
944+
945+
func TestAccGithubOrganizationRuleset_requiredReviewers(t *testing.T) {
946+
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
947+
teamName := fmt.Sprintf("%steam-req-rev-%s", testResourcePrefix, randomID)
948+
rulesetName := fmt.Sprintf("%s-ruleset-req-rev-%s", testResourcePrefix, randomID)
949+
950+
config := fmt.Sprintf(`
951+
resource "github_team" "test" {
952+
name = "%s"
953+
}
954+
955+
resource "github_organization_ruleset" "test" {
956+
name = "%s"
957+
target = "branch"
958+
enforcement = "active"
959+
960+
conditions {
961+
repository_name {
962+
include = ["~ALL"]
963+
exclude = []
964+
}
965+
966+
ref_name {
967+
include = ["~ALL"]
968+
exclude = []
969+
}
970+
}
971+
972+
rules {
973+
pull_request {
974+
allowed_merge_methods = ["merge", "squash"]
975+
required_approving_review_count = 1
976+
977+
required_reviewers {
978+
reviewer {
979+
id = github_team.test.id
980+
type = "Team"
981+
}
982+
file_patterns = ["*.go", "src/**/*.ts"]
983+
minimum_approvals = 1
984+
}
985+
}
986+
}
987+
}
988+
`, teamName, rulesetName)
989+
990+
// Updated config: change minimum_approvals from 1 to 2
991+
configUpdated := fmt.Sprintf(`
992+
resource "github_team" "test" {
993+
name = "%s"
994+
}
995+
996+
resource "github_organization_ruleset" "test" {
997+
name = "%s"
998+
target = "branch"
999+
enforcement = "active"
1000+
1001+
conditions {
1002+
repository_name {
1003+
include = ["~ALL"]
1004+
exclude = []
1005+
}
1006+
1007+
ref_name {
1008+
include = ["~ALL"]
1009+
exclude = []
1010+
}
1011+
}
1012+
1013+
rules {
1014+
pull_request {
1015+
allowed_merge_methods = ["merge", "squash"]
1016+
required_approving_review_count = 1
1017+
1018+
required_reviewers {
1019+
reviewer {
1020+
id = github_team.test.id
1021+
type = "Team"
1022+
}
1023+
file_patterns = ["*.go", "src/**/*.ts"]
1024+
minimum_approvals = 2
1025+
}
1026+
}
1027+
}
1028+
}
1029+
`, teamName, rulesetName)
1030+
1031+
resource.Test(t, resource.TestCase{
1032+
PreCheck: func() { skipUnlessHasPaidOrgs(t) },
1033+
ProviderFactories: providerFactories,
1034+
Steps: []resource.TestStep{
1035+
{
1036+
Config: config,
1037+
Check: resource.ComposeTestCheckFunc(
1038+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "name", rulesetName),
1039+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "target", "branch"),
1040+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "enforcement", "active"),
1041+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "rules.0.pull_request.0.required_reviewers.#", "1"),
1042+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "rules.0.pull_request.0.required_reviewers.0.minimum_approvals", "1"),
1043+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "rules.0.pull_request.0.required_reviewers.0.file_patterns.#", "2"),
1044+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "rules.0.pull_request.0.required_reviewers.0.file_patterns.0", "*.go"),
1045+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "rules.0.pull_request.0.required_reviewers.0.file_patterns.1", "src/**/*.ts"),
1046+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "rules.0.pull_request.0.required_reviewers.0.reviewer.0.type", "Team"),
1047+
),
1048+
},
1049+
{
1050+
Config: configUpdated,
1051+
Check: resource.ComposeTestCheckFunc(
1052+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "rules.0.pull_request.0.required_reviewers.0.minimum_approvals", "2"),
1053+
),
1054+
},
1055+
{
1056+
ResourceName: "github_organization_ruleset.test",
1057+
ImportState: true,
1058+
ImportStateVerify: true,
1059+
ImportStateVerifyIgnore: []string{"etag"},
1060+
},
1061+
},
1062+
})
1063+
}
1064+
1065+
func TestAccGithubOrganizationRuleset_requiredReviewersMultiple(t *testing.T) {
1066+
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
1067+
teamName1 := fmt.Sprintf("%steam-req-rev-1-%s", testResourcePrefix, randomID)
1068+
teamName2 := fmt.Sprintf("%steam-req-rev-2-%s", testResourcePrefix, randomID)
1069+
rulesetName := fmt.Sprintf("%s-ruleset-multi-rev-%s", testResourcePrefix, randomID)
1070+
1071+
config := fmt.Sprintf(`
1072+
resource "github_team" "test1" {
1073+
name = "%s"
1074+
}
1075+
1076+
resource "github_team" "test2" {
1077+
name = "%s"
1078+
}
1079+
1080+
resource "github_organization_ruleset" "test" {
1081+
name = "%s"
1082+
target = "branch"
1083+
enforcement = "active"
1084+
1085+
conditions {
1086+
repository_name {
1087+
include = ["~ALL"]
1088+
exclude = []
1089+
}
1090+
1091+
ref_name {
1092+
include = ["~ALL"]
1093+
exclude = []
1094+
}
1095+
}
1096+
1097+
rules {
1098+
pull_request {
1099+
allowed_merge_methods = ["merge", "squash"]
1100+
required_approving_review_count = 1
1101+
1102+
required_reviewers {
1103+
reviewer {
1104+
id = github_team.test1.id
1105+
type = "Team"
1106+
}
1107+
file_patterns = ["*.go"]
1108+
minimum_approvals = 1
1109+
}
1110+
1111+
required_reviewers {
1112+
reviewer {
1113+
id = github_team.test2.id
1114+
type = "Team"
1115+
}
1116+
file_patterns = ["*.md", "docs/**/*"]
1117+
minimum_approvals = 1
1118+
}
1119+
}
1120+
}
1121+
}
1122+
`, teamName1, teamName2, rulesetName)
1123+
1124+
resource.Test(t, resource.TestCase{
1125+
PreCheck: func() { skipUnlessHasPaidOrgs(t) },
1126+
ProviderFactories: providerFactories,
1127+
Steps: []resource.TestStep{
1128+
{
1129+
Config: config,
1130+
Check: resource.ComposeTestCheckFunc(
1131+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "name", rulesetName),
1132+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "target", "branch"),
1133+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "enforcement", "active"),
1134+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "rules.0.pull_request.0.required_reviewers.#", "2"),
1135+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "rules.0.pull_request.0.required_reviewers.0.minimum_approvals", "1"),
1136+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "rules.0.pull_request.0.required_reviewers.0.file_patterns.#", "1"),
1137+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "rules.0.pull_request.0.required_reviewers.0.file_patterns.0", "*.go"),
1138+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "rules.0.pull_request.0.required_reviewers.1.minimum_approvals", "1"),
1139+
resource.TestCheckResourceAttr("github_organization_ruleset.test", "rules.0.pull_request.0.required_reviewers.1.file_patterns.#", "2"),
1140+
),
1141+
},
1142+
},
1143+
})
1144+
}

github/resource_github_repository_ruleset_test.go

Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -748,6 +748,159 @@ func TestAccGithubRepositoryRulesetValidation(t *testing.T) {
748748
})
749749
}
750750

751+
func TestAccGithubRepositoryRuleset_requiredReviewers(t *testing.T) {
752+
randomID := acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum)
753+
repoName := fmt.Sprintf("%srepo-ruleset-req-rev-%s", testResourcePrefix, randomID)
754+
teamName := fmt.Sprintf("%steam-req-rev-%s", testResourcePrefix, randomID)
755+
rulesetName := fmt.Sprintf("%s-ruleset-req-rev-%s", testResourcePrefix, randomID)
756+
baseRepoVisibility := "public"
757+
758+
if testAccConf.authMode == enterprise {
759+
// This enables repos to be created even in GHEC EMU
760+
baseRepoVisibility = "private"
761+
}
762+
763+
config := fmt.Sprintf(`
764+
resource "github_repository" "test" {
765+
name = "%s"
766+
auto_init = true
767+
visibility = "%s"
768+
769+
ignore_vulnerability_alerts_during_read = true
770+
}
771+
772+
resource "github_team" "test" {
773+
name = "%s"
774+
}
775+
776+
resource "github_team_repository" "test" {
777+
team_id = github_team.test.id
778+
repository = github_repository.test.name
779+
permission = "push"
780+
}
781+
782+
resource "github_repository_ruleset" "test" {
783+
name = "%s"
784+
repository = github_repository.test.name
785+
target = "branch"
786+
enforcement = "active"
787+
788+
789+
conditions {
790+
ref_name {
791+
include = ["~ALL"]
792+
exclude = []
793+
}
794+
}
795+
796+
rules {
797+
pull_request {
798+
allowed_merge_methods = ["merge", "squash"]
799+
required_approving_review_count = 1
800+
801+
required_reviewers {
802+
reviewer {
803+
id = github_team.test.id
804+
type = "Team"
805+
}
806+
file_patterns = ["*.go"]
807+
minimum_approvals = 1
808+
}
809+
}
810+
}
811+
812+
depends_on = [github_team_repository.test]
813+
}
814+
`, repoName, baseRepoVisibility, teamName, rulesetName)
815+
816+
// Updated config: change minimum_approvals from 1 to 2
817+
configUpdated := fmt.Sprintf(`
818+
resource "github_repository" "test" {
819+
name = "%s"
820+
auto_init = true
821+
visibility = "%s"
822+
823+
ignore_vulnerability_alerts_during_read = true
824+
}
825+
826+
resource "github_team" "test" {
827+
name = "%s"
828+
}
829+
830+
resource "github_team_repository" "test" {
831+
team_id = github_team.test.id
832+
repository = github_repository.test.name
833+
permission = "push"
834+
}
835+
836+
resource "github_repository_ruleset" "test" {
837+
name = "%s"
838+
repository = github_repository.test.name
839+
target = "branch"
840+
enforcement = "active"
841+
842+
843+
conditions {
844+
ref_name {
845+
include = ["~ALL"]
846+
exclude = []
847+
}
848+
}
849+
850+
rules {
851+
pull_request {
852+
allowed_merge_methods = ["merge", "squash"]
853+
required_approving_review_count = 1
854+
855+
required_reviewers {
856+
reviewer {
857+
id = github_team.test.id
858+
type = "Team"
859+
}
860+
file_patterns = ["*.go"]
861+
minimum_approvals = 2
862+
}
863+
}
864+
}
865+
866+
depends_on = [github_team_repository.test]
867+
}
868+
`, repoName, baseRepoVisibility, teamName, rulesetName)
869+
870+
resource.Test(t, resource.TestCase{
871+
PreCheck: func() { skipUnlessHasOrgs(t) },
872+
ProviderFactories: providerFactories,
873+
Steps: []resource.TestStep{
874+
{
875+
Config: config,
876+
Check: resource.ComposeTestCheckFunc(
877+
resource.TestCheckResourceAttr("github_repository_ruleset.test", "name", rulesetName),
878+
resource.TestCheckResourceAttr("github_repository_ruleset.test", "target", "branch"),
879+
resource.TestCheckResourceAttr("github_repository_ruleset.test", "enforcement", "active"),
880+
resource.TestCheckResourceAttr("github_repository_ruleset.test", "rules.0.pull_request.0.required_reviewers.#", "1"),
881+
resource.TestCheckResourceAttr("github_repository_ruleset.test", "rules.0.pull_request.0.required_reviewers.0.minimum_approvals", "1"),
882+
resource.TestCheckResourceAttr("github_repository_ruleset.test", "rules.0.pull_request.0.required_reviewers.0.file_patterns.#", "1"),
883+
resource.TestCheckResourceAttr("github_repository_ruleset.test", "rules.0.pull_request.0.required_reviewers.0.file_patterns.0", "*.go"),
884+
resource.TestCheckResourceAttr("github_repository_ruleset.test", "rules.0.pull_request.0.required_reviewers.0.reviewer.0.type", "Team"),
885+
),
886+
},
887+
{
888+
Config: configUpdated,
889+
Check: resource.ComposeTestCheckFunc(
890+
resource.TestCheckResourceAttr("github_repository_ruleset.test", "rules.0.pull_request.0.required_reviewers.0.minimum_approvals", "2"),
891+
),
892+
},
893+
{
894+
ResourceName: "github_repository_ruleset.test",
895+
ImportState: true,
896+
ImportStateVerify: true,
897+
ImportStateIdFunc: importRepositoryRulesetByResourcePaths("github_repository.test", "github_repository_ruleset.test"),
898+
ImportStateVerifyIgnore: []string{"etag"},
899+
},
900+
},
901+
})
902+
}
903+
751904
func importRepositoryRulesetByResourcePaths(repoLogicalName, rulesetLogicalName string) resource.ImportStateIdFunc {
752905
// test importing using an ID of the form <repo-node-id>:<ruleset-id>
753906
return func(s *terraform.State) (string, error) {

0 commit comments

Comments
 (0)