Repository: xanzy/go-gitlab
Pull request #1396 (2022-02-28 13:02:02)

feat: Add any_approver project approval rule
This rule is used in GitLab to set up Eligible Approvers,
which is currently not possible.


Patch:
diff --git a/event_parsing.go b/event_parsing.go
index e17a0f1..69259e0 100644
--- a/event_parsing.go
+++ b/event_parsing.go
@@ -39,7 +39,6 @@ const (
 	EventTypePush          EventType = "Push Hook"
 	EventTypeRelease       EventType = "Release Hook"
 	EventTypeServiceHook   EventType = "Service Hook"
-	EventTypeSubGroup      EventType = "Subgroup Hook"
 	EventTypeSystemHook    EventType = "System Hook"
 	EventTypeTagPush       EventType = "Tag Push Hook"
 	EventTypeWikiPage      EventType = "Wiki Page Hook"
@@ -266,8 +265,6 @@ func ParseWebhook(eventType EventType, payload []byte) (event interface{}, err e
 		default:
 			return nil, fmt.Errorf("unexpected service type %s", service.ObjectKind)
 		}
-	case EventTypeSubGroup:
-		event = &SubGroupEvent{}
 	case EventTypeTagPush:
 		event = &TagEvent{}
 	case EventTypeWikiPage:
diff --git a/event_webhook_types.go b/event_webhook_types.go
index cbdcd23..1a14052 100644
--- a/event_webhook_types.go
+++ b/event_webhook_types.go
@@ -61,7 +61,7 @@ type BuildEvent struct {
 // CommitCommentEvent represents a comment on a commit event.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ce/user/project/integrations/webhook_events.html#comment-on-a-commit
+// https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#comment-on-commit
 type CommitCommentEvent struct {
 	ObjectKind string `json:"object_kind"`
 	User       *User  `json:"user"`
@@ -125,7 +125,7 @@ type CommitCommentEvent struct {
 // DeploymentEvent represents a deployment event
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ce/user/project/integrations/webhook_events.html#deployment-events
+// https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#deployment-events
 type DeploymentEvent struct {
 	ObjectKind    string `json:"object_kind"`
 	Status        string `json:"status"`
@@ -160,7 +160,7 @@ type DeploymentEvent struct {
 // IssueCommentEvent represents a comment on an issue event.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ce/user/project/integrations/webhook_events.html#comment-on-an-issue
+// https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#comment-on-issue
 type IssueCommentEvent struct {
 	ObjectKind string `json:"object_kind"`
 	User       *User  `json:"user"`
@@ -230,7 +230,7 @@ type IssueCommentEvent struct {
 // IssueEvent represents a issue event.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ce/user/project/integrations/webhook_events.html#issue-events
+// https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#issues-events
 type IssueEvent struct {
 	ObjectKind string     `json:"object_kind"`
 	User       *EventUser `json:"user"`
@@ -344,7 +344,7 @@ type JobEvent struct {
 // MergeCommentEvent represents a comment on a merge event.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#comment-on-a-merge-request
+// https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#comment-on-merge-request
 type MergeCommentEvent struct {
 	ObjectKind string     `json:"object_kind"`
 	User       *EventUser `json:"user"`
@@ -447,7 +447,7 @@ type MergeCommentEvent struct {
 // MergeEvent represents a merge event.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ce/user/project/integrations/webhook_events.html#merge-request-events
+// https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#merge-request-events
 type MergeEvent struct {
 	ObjectKind string     `json:"object_kind"`
 	User       *EventUser `json:"user"`
@@ -623,7 +623,7 @@ func (p *MergeParams) UnmarshalJSON(b []byte) error {
 // PipelineEvent represents a pipeline event.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ce/user/project/integrations/webhook_events.html#pipeline-events
+// https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#pipeline-events
 type PipelineEvent struct {
 	ObjectKind       string `json:"object_kind"`
 	ObjectAttributes struct {
@@ -720,7 +720,7 @@ type PipelineEvent struct {
 // PushEvent represents a push event.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ce/user/project/integrations/webhook_events.html#push-events
+// https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#push-events
 type PushEvent struct {
 	ObjectKind   string `json:"object_kind"`
 	Before       string `json:"before"`
@@ -770,7 +770,7 @@ type PushEvent struct {
 // ReleaseEvent represents a release event
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ce/user/project/integrations/webhook_events.html#release-events
+// https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#release-events
 type ReleaseEvent struct {
 	ID          int    `json:"id"`
 	CreatedAt   string `json:"created_at"` // Should be *time.Time (see Gitlab issue #21468)
@@ -829,7 +829,7 @@ type ReleaseEvent struct {
 // SnippetCommentEvent represents a comment on a snippet event.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#comment-on-a-code-snippet
+// https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#comment-on-code-snippet
 type SnippetCommentEvent struct {
 	ObjectKind string     `json:"object_kind"`
 	User       *EventUser `json:"user"`
@@ -871,28 +871,10 @@ type SnippetCommentEvent struct {
 	Snippet *Snippet `json:"snippet"`
 }
 
-// SubGroupEvent represents a subgroup event.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/user/project/integrations/webhook_events.html#subgroup-events
-type SubGroupEvent struct {
-	CreatedAt      *time.Time `json:"created_at"`
-	UpdatedAt      *time.Time `json:"updated_at"`
-	EventName      string     `json:"event_name"`
-	Name           string     `json:"name"`
-	Path           string     `json:"path"`
-	FullPath       string     `json:"full_path"`
-	GroupID        int        `json:"group_id"`
-	ParentGroupID  int        `json:"parent_group_id"`
-	ParentName     string     `json:"parent_name"`
-	ParentPath     string     `json:"parent_path"`
-	ParentFullPath string     `json:"parent_full_path"`
-}
-
 // TagEvent represents a tag event.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ce/user/project/integrations/webhook_events.html#tag-events
+// https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#tag-events
 type TagEvent struct {
 	ObjectKind   string `json:"object_kind"`
 	Before       string `json:"before"`
@@ -943,7 +925,7 @@ type TagEvent struct {
 // WikiPageEvent represents a wiki page event.
 //
 // GitLab API docs:
-// https://docs.gitlab.com/ce/user/project/integrations/webhook_events.html#wiki-page-events
+// https://docs.gitlab.com/ce/user/project/integrations/webhooks.html#wiki-page-events
 type WikiPageEvent struct {
 	ObjectKind string     `json:"object_kind"`
 	User       *EventUser `json:"user"`
diff --git a/groups.go b/groups.go
index b3535ef..2383ca8 100644
--- a/groups.go
+++ b/groups.go
@@ -73,7 +73,6 @@ type Group struct {
 	LDAPGroupLinks                 []*LDAPGroupLink `json:"ldap_group_links"`
 	SharedRunnersMinutesLimit      int              `json:"shared_runners_minutes_limit"`
 	ExtraSharedRunnersMinutesLimit int              `json:"extra_shared_runners_minutes_limit"`
-	PreventForkingOutsideGroup     bool             `json:"prevent_forking_outside_group"`
 	MarkedForDeletionOn            *ISOTime         `json:"marked_for_deletion_on"`
 	CreatedAt                      *time.Time       `json:"created_at"`
 }
diff --git a/projects.go b/projects.go
index baf3b31..278f16c 100644
--- a/projects.go
+++ b/projects.go
@@ -909,16 +909,9 @@ func (s *ProjectsService) EditProject(pid interface{}, opt *EditProjectOptions,
 //
 // GitLab API docs: https://docs.gitlab.com/ce/api/projects.html#fork-project
 type ForkProjectOptions struct {
-	Description                   *string          `url:"description,omitempty" json:"description,omitempty"`
-	MergeRequestDefaultTargetSelf *bool            `url:"mr_default_target_self,omitempty" json:"mr_default_target_self,omitempty"`
-	Name                          *string          `url:"name,omitempty" json:"name,omitempty"`
-	NamespaceID                   *int             `url:"namespace_id,omitempty" json:"namespace_id,omitempty"`
-	NamespacePath                 *string          `url:"namespace_path,omitempty" json:"namespace_path,omitempty"`
-	Path                          *string          `url:"path,omitempty" json:"path,omitempty"`
-	Visibility                    *VisibilityValue `url:"visibility,omitempty" json:"visibility,omitempty"`
-
-	// Deprecated members
+	Name      *string `url:"name,omitempty" json:"name,omitempty" `
 	Namespace *string `url:"namespace,omitempty" json:"namespace,omitempty"`
+	Path      *string `url:"path,omitempty" json:"path,omitempty"`
 }
 
 // ForkProject forks a project into the user namespace of the authenticated
@@ -1746,11 +1739,12 @@ func (s *ProjectsService) GetProjectApprovalRules(pid interface{}, options ...Re
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/merge_request_approvals.html#create-project-level-rules
 type CreateProjectLevelRuleOptions struct {
+	Name               *string `url:"name,omitempty" json:"name,omitempty"`
 	ApprovalsRequired  *int    `url:"approvals_required,omitempty" json:"approvals_required,omitempty"`
+	RuleType           *string `url:"rule_type,omitempty" json:"rule_type,omitempty"`
+	UserIDs            *[]int  `url:"user_ids,omitempty" json:"user_ids,omitempty"`
 	GroupIDs           *[]int  `url:"group_ids,omitempty" json:"group_ids,omitempty"`
-	Name               *string `url:"name,omitempty" json:"name,omitempty"`
 	ProtectedBranchIDs *[]int  `url:"protected_branch_ids,omitempty" json:"protected_branch_ids,omitempty"`
-	UserIDs            *[]int  `url:"user_ids,omitempty" json:"user_ids,omitempty"`
 }
 
 // CreateProjectApprovalRule creates a new project-level approval rule.
@@ -1784,11 +1778,11 @@ func (s *ProjectsService) CreateProjectApprovalRule(pid interface{}, opt *Create
 // GitLab API docs:
 // https://docs.gitlab.com/ee/api/merge_request_approvals.html#update-project-level-rules
 type UpdateProjectLevelRuleOptions struct {
+	Name               *string `url:"name,omitempty" json:"name,omitempty"`
 	ApprovalsRequired  *int    `url:"approvals_required,omitempty" json:"approvals_required,omitempty"`
+	UserIDs            *[]int  `url:"user_ids,omitempty" json:"user_ids,omitempty"`
 	GroupIDs           *[]int  `url:"group_ids,omitempty" json:"group_ids,omitempty"`
-	Name               *string `url:"name,omitempty" json:"name,omitempty"`
 	ProtectedBranchIDs *[]int  `url:"protected_branch_ids,omitempty" json:"protected_branch_ids,omitempty"`
-	UserIDs            *[]int  `url:"user_ids,omitempty" json:"user_ids,omitempty"`
 }
 
 // UpdateProjectApprovalRule updates an existing approval rule with new options.
diff --git a/topics.go b/topics.go
index b227e8b..04f4473 100644
--- a/topics.go
+++ b/topics.go
@@ -150,18 +150,3 @@ func (s *TopicsService) UpdateTopic(topic int, opt *UpdateTopicOptions, options
 
 	return t, resp, err
 }
-
-// DeleteTopic deletes a project topic. Only available to administrators.
-//
-// GitLab API docs:
-// https://docs.gitlab.com/ee/api/topics.html#delete-a-project-topic
-func (s *TopicsService) DeleteTopic(topic int, options ...RequestOptionFunc) (*Response, error) {
-	u := fmt.Sprintf("topics/%d", topic)
-
-	req, err := s.client.NewRequest(http.MethodDelete, u, nil, options)
-	if err != nil {
-		return nil, err
-	}
-
-	return s.client.Do(req, nil)
-}


Test patch:
diff --git a/event_parsing_webhook_test.go b/event_parsing_webhook_test.go
index b991deb..cf582d5 100644
--- a/event_parsing_webhook_test.go
+++ b/event_parsing_webhook_test.go
@@ -387,24 +387,6 @@ func TestParseSnippetCommentHook(t *testing.T) {
 	}
 }
 
-func TestParseSubGroupHook(t *testing.T) {
-	raw := loadFixture("testdata/webhooks/subgroup.json")
-
-	parsedEvent, err := ParseWebhook("Subgroup Hook", raw)
-	if err != nil {
-		t.Errorf("Error parsing subgroup hook: %s", err)
-	}
-
-	event, ok := parsedEvent.(*SubGroupEvent)
-	if !ok {
-		t.Errorf("Expected SubGroupEvent, but parsing produced %T", parsedEvent)
-	}
-
-	if event.EventName != "subgroup_create" {
-		t.Errorf("EventName is %v, want %v", event.EventName, "subgroup_create")
-	}
-}
-
 func TestParseTagHook(t *testing.T) {
 	raw := loadFixture("testdata/webhooks/tag_push.json")
 
diff --git a/event_webhook_types_test.go b/event_webhook_types_test.go
index b0119da..9ba1bef 100644
--- a/event_webhook_types_test.go
+++ b/event_webhook_types_test.go
@@ -19,7 +19,6 @@ package gitlab
 import (
 	"encoding/json"
 	"testing"
-	"time"
 )
 
 const (
@@ -340,7 +339,7 @@ func TestPipelineEventUnmarshal(t *testing.T) {
 	if event.Builds[0].AllowFailure != true {
 		t.Errorf("Builds.0.AllowFailure is %v, want %v", event.Builds[0].AllowFailure, true)
 	}
-
+	
 	if event.Builds[0].Environment.Name != "production" {
 		t.Errorf("Builds.0.Environment.Name is %v, want %v", event.Builds[0].Environment.Name, "production")
 	}
@@ -384,114 +383,83 @@ func TestPushEventUnmarshal(t *testing.T) {
 	}
 }
 
-func TestReleaseEventUnmarshal(t *testing.T) {
-	jsonObject := loadFixture("testdata/webhooks/release.json")
-
-	var event *ReleaseEvent
+func TestTagEventUnmarshal(t *testing.T) {
+	jsonObject := loadFixture("testdata/webhooks/tag_push.json")
+	var event *TagEvent
 	err := json.Unmarshal(jsonObject, &event)
 
 	if err != nil {
-		t.Errorf("Release Event can not unmarshaled: %v\n ", err.Error())
+		t.Errorf("Tag Event can not unmarshaled: %v\n ", err.Error())
 	}
 
 	if event == nil {
-		t.Errorf("Release Event is null")
-	}
-
-	if event.Project.ID != 327622 {
-		t.Errorf("Project.ID is %v, want %v", event.Project.ID, 327622)
+		t.Errorf("Tag Event is null")
 	}
 
-	if event.Commit.Title != "Merge branch 'example-branch' into 'master'" {
-		t.Errorf("Commit title is %s, want %s", event.Commit.Title, "Merge branch 'example-branch' into 'master'")
+	if event.ProjectID != 1 {
+		t.Errorf("ProjectID is %v, want %v", event.ProjectID, 1)
 	}
 
-	if len(event.Assets.Sources) != 4 {
-		t.Errorf("Asset sources length is %d, want %d", len(event.Assets.Sources), 4)
+	if event.UserName != exampleEventUserName {
+		t.Errorf("Username is %s, want %s", event.UserName, exampleEventUserName)
 	}
 
-	if event.Assets.Sources[0].Format != "zip" {
-		t.Errorf("First asset source format is %s, want %s", event.Assets.Sources[0].Format, "zip")
+	if event.Commits[0] == nil || event.Commits[0].Timestamp == nil {
+		t.Errorf("Commit Timestamp isn't nil")
 	}
 
-	if len(event.Assets.Links) != 1 {
-		t.Errorf("Asset links length is %d, want %d", len(event.Assets.Links), 1)
+	if event.Commits[0] == nil || event.Commits[0].Message != exampleCommitMessage {
+		t.Errorf("Commit Message is %s, want %s", event.Commits[0].Message, exampleCommitMessage)
 	}
 
-	if event.Assets.Links[0].Name != "Changelog" {
-		t.Errorf("First asset link name is %s, want %s", event.Assets.Links[0].Name, "Changelog")
+	if event.Commits[0] == nil || event.Commits[0].Title != exampleCommitTitle {
+		t.Errorf("Commit Title is %s, want %s", event.Commits[0].Title, exampleCommitTitle)
 	}
 
-	if event.Commit.Author.Name != "User" {
-		t.Errorf("Commit author name is %s, want %s", event.Commit.Author.Name, "User")
+	if event.Commits[0] == nil || event.Commits[0].Author.Name != exampleEventUserName {
+		t.Errorf("Commit Username is %s, want %s", event.UserName, exampleEventUserName)
 	}
 }
 
-func TestSubGroupEventUnmarshal(t *testing.T) {
-	jsonObject := loadFixture("testdata/webhooks/subgroup.json")
+func TestReleaseEventUnmarshal(t *testing.T) {
+	jsonObject := loadFixture("testdata/webhooks/release.json")
 
-	var event *SubGroupEvent
+	var event *ReleaseEvent
 	err := json.Unmarshal(jsonObject, &event)
 
 	if err != nil {
-		t.Errorf("SubGroup Event can not unmarshaled: %v\n ", err.Error())
+		t.Errorf("Release Event can not unmarshaled: %v\n ", err.Error())
 	}
 
 	if event == nil {
-		t.Errorf("SubGroup Event is null")
-	}
-
-	if event.Name != "Subgroup 1" {
-		t.Errorf("Name is %v, want %v", event.Name, "Subgroup 1")
-	}
-
-	if event.GroupID != 2 {
-		t.Errorf("GroupID is %v, want %v", event.GroupID, 2)
-	}
-
-	if event.ParentGroupID != 1 {
-		t.Errorf("ParentGroupID is %v, want %v", event.ParentGroupID, 1)
-	}
-
-	if event.CreatedAt.Format(time.RFC3339) != "2022-01-24T14:23:59Z" {
-		t.Errorf("CreatedAt is %v, want %v", event.CreatedAt.Format(time.RFC3339), "2022-01-24T14:23:59Z")
-	}
-}
-
-func TestTagEventUnmarshal(t *testing.T) {
-	jsonObject := loadFixture("testdata/webhooks/tag_push.json")
-	var event *TagEvent
-	err := json.Unmarshal(jsonObject, &event)
-
-	if err != nil {
-		t.Errorf("Tag Event can not unmarshaled: %v\n ", err.Error())
+		t.Errorf("Release Event is null")
 	}
 
-	if event == nil {
-		t.Errorf("Tag Event is null")
+	if event.Project.ID != 327622 {
+		t.Errorf("Project.ID is %v, want %v", event.Project.ID, 327622)
 	}
 
-	if event.ProjectID != 1 {
-		t.Errorf("ProjectID is %v, want %v", event.ProjectID, 1)
+	if event.Commit.Title != "Merge branch 'example-branch' into 'master'" {
+		t.Errorf("Commit title is %s, want %s", event.Commit.Title, "Merge branch 'example-branch' into 'master'")
 	}
 
-	if event.UserName != exampleEventUserName {
-		t.Errorf("Username is %s, want %s", event.UserName, exampleEventUserName)
+	if len(event.Assets.Sources) != 4 {
+		t.Errorf("Asset sources length is %d, want %d", len(event.Assets.Sources), 4)
 	}
 
-	if event.Commits[0] == nil || event.Commits[0].Timestamp == nil {
-		t.Errorf("Commit Timestamp isn't nil")
+	if event.Assets.Sources[0].Format != "zip" {
+		t.Errorf("First asset source format is %s, want %s", event.Assets.Sources[0].Format, "zip")
 	}
 
-	if event.Commits[0] == nil || event.Commits[0].Message != exampleCommitMessage {
-		t.Errorf("Commit Message is %s, want %s", event.Commits[0].Message, exampleCommitMessage)
+	if len(event.Assets.Links) != 1 {
+		t.Errorf("Asset links length is %d, want %d", len(event.Assets.Links), 1)
 	}
 
-	if event.Commits[0] == nil || event.Commits[0].Title != exampleCommitTitle {
-		t.Errorf("Commit Title is %s, want %s", event.Commits[0].Title, exampleCommitTitle)
+	if event.Assets.Links[0].Name != "Changelog" {
+		t.Errorf("First asset link name is %s, want %s", event.Assets.Links[0].Name, "Changelog")
 	}
 
-	if event.Commits[0] == nil || event.Commits[0].Author.Name != exampleEventUserName {
-		t.Errorf("Commit Username is %s, want %s", event.UserName, exampleEventUserName)
+	if event.Commit.Author.Name != "User" {
+		t.Errorf("Commit author name is %s, want %s", event.Commit.Author.Name, "User")
 	}
 }
diff --git a/projects_test.go b/projects_test.go
index 90815e7..f81694d 100644
--- a/projects_test.go
+++ b/projects_test.go
@@ -751,20 +751,20 @@ func TestForkProject(t *testing.T) {
 	mux, server, client := setup(t)
 	defer teardown(server)
 
-	namespaceID := 42
+	namespace := "mynamespace"
 	name := "myreponame"
 	path := "myrepopath"
 
 	mux.HandleFunc("/api/v4/projects/1/fork", func(w http.ResponseWriter, r *http.Request) {
 		testMethod(t, r, http.MethodPost)
-		testBody(t, r, fmt.Sprintf(`{"name":"%s","namespace_id":%d,"path":"%s"}`, name, namespaceID, path))
+		testBody(t, r, fmt.Sprintf(`{"name":"%s","namespace":"%s","path":"%s"}`, name, namespace, path))
 		fmt.Fprint(w, `{"id":2}`)
 	})
 
 	project, _, err := client.Projects.ForkProject(1, &ForkProjectOptions{
-		NamespaceID: Int(namespaceID),
-		Name:        String(name),
-		Path:        String(path),
+		Namespace: String(namespace),
+		Name:      String(name),
+		Path:      String(path),
 	})
 	if err != nil {
 		t.Errorf("Projects.ForkProject returned error: %v", err)
@@ -1130,3 +1130,48 @@ func TestCreateProjectApprovalRule(t *testing.T) {
 		t.Errorf("Projects.CreateProjectApprovalRule returned %+v, want %+v", rule, want)
 	}
 }
+
+func TestCreateProjectApprovalRuleEligibleApprovers(t *testing.T) {
+	mux, server, client := setup(t)
+	defer teardown(server)
+
+	mux.HandleFunc("/api/v4/projects/1/approval_rules", func(w http.ResponseWriter, r *http.Request) {
+		testMethod(t, r, http.MethodPost)
+		fmt.Fprint(w, `{
+			"id": 1,
+			"name": "Any name",
+			"rule_type": "any_approver",
+			"eligible_approvers": [],
+			"approvals_required": 1,
+			"users": [],
+			"groups": [],
+			"contains_hidden_groups": false,
+			"protected_branches": []
+		}`)
+	})
+
+	opt := &CreateProjectLevelRuleOptions{
+		Name:              String("Any name"),
+		ApprovalsRequired: Int(1),
+	}
+
+	rule, _, err := client.Projects.CreateProjectApprovalRule(1, opt)
+	if err != nil {
+		t.Errorf("Projects.CreateProjectApprovalRule returned error: %v", err)
+	}
+
+	want := &ProjectApprovalRule{
+		ID:       1,
+		Name:     "Any name",
+		RuleType: "any_approver",
+		EligibleApprovers: []*BasicUser{},
+		ApprovalsRequired: 1,
+		Users: []*BasicUser{},
+		Groups: []*Group{},
+		ProtectedBranches: []*ProtectedBranch{},
+	}
+
+	if !reflect.DeepEqual(want, rule) {
+		t.Errorf("Projects.CreateProjectApprovalRule returned %+v, want %+v", rule, want)
+	}
+}
diff --git a/testdata/webhooks/subgroup.json b/testdata/webhooks/subgroup.json
deleted file mode 100644
index 0c336cd..0000000
--- a/testdata/webhooks/subgroup.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
-  "created_at": "2022-01-24T14:23:59Z",
-  "updated_at": "2022-01-24T14:23:59Z",
-  "event_name": "subgroup_create",
-  "name": "Subgroup 1",
-  "path": "subgroup-1",
-  "full_path": "group-1/subgroup-1",
-  "group_id": 2,
-  "parent_group_id": 1,
-  "parent_name": "Group 1",
-  "parent_path": "group-1",
-  "parent_full_path": "group-1"
-}