sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/pkg/plugins/cherrypickunapproved/cherrypick-unapproved_test.go (about)

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package cherrypickunapproved
    18  
    19  import (
    20  	"encoding/json"
    21  	"reflect"
    22  	"regexp"
    23  	"testing"
    24  
    25  	"github.com/sirupsen/logrus"
    26  
    27  	"sigs.k8s.io/prow/pkg/github"
    28  	"sigs.k8s.io/prow/pkg/labels"
    29  )
    30  
    31  type fakeClient struct {
    32  	// current labels
    33  	labels []string
    34  	// labels that are added
    35  	added []string
    36  	// labels that are removed
    37  	removed []string
    38  	// commentsAdded tracks the comments in the client
    39  	commentsAdded map[int][]string
    40  }
    41  
    42  // AddLabel adds a label to the specified PR or issue
    43  func (fc *fakeClient) AddLabel(owner, repo string, number int, label string) error {
    44  	fc.added = append(fc.added, label)
    45  	fc.labels = append(fc.labels, label)
    46  	return nil
    47  }
    48  
    49  // RemoveLabel removes the label from the specified PR or issue
    50  func (fc *fakeClient) RemoveLabel(owner, repo string, number int, label string) error {
    51  	fc.removed = append(fc.removed, label)
    52  
    53  	// remove from existing labels
    54  	for k, v := range fc.labels {
    55  		if label == v {
    56  			fc.labels = append(fc.labels[:k], fc.labels[k+1:]...)
    57  			break
    58  		}
    59  	}
    60  
    61  	return nil
    62  }
    63  
    64  // GetIssueLabels gets the current labels on the specified PR or issue
    65  func (fc *fakeClient) GetIssueLabels(owner, repo string, number int) ([]github.Label, error) {
    66  	var la []github.Label
    67  	for _, l := range fc.labels {
    68  		la = append(la, github.Label{Name: l})
    69  	}
    70  	return la, nil
    71  }
    72  
    73  // CreateComment adds and tracks a comment in the client
    74  func (fc *fakeClient) CreateComment(owner, repo string, number int, comment string) error {
    75  	fc.commentsAdded[number] = append(fc.commentsAdded[number], comment)
    76  	return nil
    77  }
    78  
    79  // NumComments counts the number of tracked comments
    80  func (fc *fakeClient) NumComments() int {
    81  	n := 0
    82  	for _, comments := range fc.commentsAdded {
    83  		n += len(comments)
    84  	}
    85  	return n
    86  }
    87  
    88  type fakePruner struct{}
    89  
    90  func (fp *fakePruner) PruneComments(shouldPrune func(github.IssueComment) bool) {}
    91  
    92  func makeFakePullRequestEvent(action github.PullRequestEventAction, branch string, changes json.RawMessage) github.PullRequestEvent {
    93  	event := github.PullRequestEvent{
    94  		Action: action,
    95  		PullRequest: github.PullRequest{
    96  			Base: github.PullRequestBranch{
    97  				Ref: branch,
    98  			},
    99  		},
   100  	}
   101  
   102  	if changes != nil {
   103  		event.Changes = changes
   104  	}
   105  
   106  	return event
   107  }
   108  
   109  func TestCherryPickUnapprovedLabel(t *testing.T) {
   110  	var testcases = []struct {
   111  		name          string
   112  		branch        string
   113  		changes       json.RawMessage
   114  		action        github.PullRequestEventAction
   115  		labels        []string
   116  		added         []string
   117  		removed       []string
   118  		expectComment bool
   119  	}{
   120  		{
   121  			name:          "unsupported PR action -> no-op",
   122  			branch:        "release-1.10",
   123  			action:        github.PullRequestActionClosed,
   124  			labels:        []string{},
   125  			added:         []string{},
   126  			removed:       []string{},
   127  			expectComment: false,
   128  		},
   129  		{
   130  			name:          "branch that does match regexp -> no-op",
   131  			branch:        "master",
   132  			action:        github.PullRequestActionOpened,
   133  			labels:        []string{},
   134  			added:         []string{},
   135  			removed:       []string{},
   136  			expectComment: false,
   137  		},
   138  		{
   139  			name:          "has cpUnapproved -> no-op",
   140  			branch:        "release-1.10",
   141  			action:        github.PullRequestActionOpened,
   142  			labels:        []string{labels.CpUnapproved},
   143  			added:         []string{},
   144  			removed:       []string{},
   145  			expectComment: false,
   146  		},
   147  		{
   148  			name:          "has both cpApproved and cpUnapproved -> remove cpUnapproved",
   149  			branch:        "release-1.10",
   150  			action:        github.PullRequestActionOpened,
   151  			labels:        []string{labels.CpApproved, labels.CpUnapproved},
   152  			added:         []string{},
   153  			removed:       []string{labels.CpUnapproved},
   154  			expectComment: false,
   155  		},
   156  		{
   157  			name:          "does not have any labels, PR opened against a release branch -> add cpUnapproved and comment",
   158  			branch:        "release-1.10",
   159  			action:        github.PullRequestActionOpened,
   160  			labels:        []string{},
   161  			added:         []string{labels.CpUnapproved},
   162  			removed:       []string{},
   163  			expectComment: true,
   164  		},
   165  		{
   166  			name:          "does not have any labels, PR reopened against a release branch -> add cpUnapproved and comment",
   167  			branch:        "release-1.10",
   168  			action:        github.PullRequestActionReopened,
   169  			labels:        []string{},
   170  			added:         []string{labels.CpUnapproved},
   171  			removed:       []string{},
   172  			expectComment: true,
   173  		},
   174  		{
   175  			name:          "PR base branch master edited to release -> add cpUnapproved and comment",
   176  			branch:        "release-1.10",
   177  			action:        github.PullRequestActionEdited,
   178  			changes:       json.RawMessage(`{"base": {"ref": {"from": "master"}, "sha": {"from": "sha"}}}`),
   179  			labels:        []string{},
   180  			added:         []string{labels.CpUnapproved},
   181  			removed:       []string{},
   182  			expectComment: true,
   183  		},
   184  		{
   185  			name:          "PR base branch edited from release to master -> remove cpApproved and cpUnapproved",
   186  			branch:        "master",
   187  			action:        github.PullRequestActionEdited,
   188  			changes:       json.RawMessage(`{"base": {"ref": {"from": "release-1.10"}, "sha": {"from": "sha"}}}`),
   189  			labels:        []string{labels.CpApproved, labels.CpUnapproved},
   190  			added:         []string{},
   191  			removed:       []string{labels.CpApproved, labels.CpUnapproved},
   192  			expectComment: false,
   193  		},
   194  		{
   195  			name:          "PR title changed -> no-op",
   196  			branch:        "release-1.10",
   197  			action:        github.PullRequestActionEdited,
   198  			changes:       json.RawMessage(`{"title": {"from": "Update README.md"}}`),
   199  			labels:        []string{labels.CpApproved, labels.CpUnapproved},
   200  			added:         []string{},
   201  			removed:       []string{},
   202  			expectComment: false,
   203  		},
   204  	}
   205  
   206  	for _, tc := range testcases {
   207  		fc := &fakeClient{
   208  			labels:        tc.labels,
   209  			added:         []string{},
   210  			removed:       []string{},
   211  			commentsAdded: make(map[int][]string),
   212  		}
   213  
   214  		event := makeFakePullRequestEvent(tc.action, tc.branch, tc.changes)
   215  		branchRe := regexp.MustCompile(`^release-.*$`)
   216  		comment := "dummy cumment"
   217  		err := handlePR(fc, logrus.WithField("plugin", "fake-cherrypick-unapproved"), &event, &fakePruner{}, branchRe, comment)
   218  		switch {
   219  		case err != nil:
   220  			t.Errorf("%s: unexpected error: %v", tc.name, err)
   221  		case !reflect.DeepEqual(tc.added, fc.added):
   222  			t.Errorf("%s: added %v != actual %v", tc.name, tc.added, fc.added)
   223  		case !reflect.DeepEqual(tc.removed, fc.removed):
   224  			t.Errorf("%s: removed %v != actual %v", tc.name, tc.removed, fc.removed)
   225  		}
   226  
   227  		// if we expected a comment, verify that a comment was made
   228  		numComments := fc.NumComments()
   229  		if tc.expectComment && numComments != 1 {
   230  			t.Errorf("%s: expected 1 comment but received %d comments", tc.name, numComments)
   231  		}
   232  		if !tc.expectComment && numComments != 0 {
   233  			t.Errorf("%s: expected no comments but received %d comments", tc.name, numComments)
   234  		}
   235  	}
   236  }