sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/pkg/plugins/cherrypickapproved/cherrypickapproved_test.go (about)

     1  /*
     2  Copyright 2023 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 cherrypickapproved
    18  
    19  import (
    20  	"errors"
    21  	"regexp"
    22  	"testing"
    23  
    24  	"github.com/sirupsen/logrus"
    25  	"github.com/stretchr/testify/assert"
    26  	"sigs.k8s.io/prow/pkg/github"
    27  	"sigs.k8s.io/prow/pkg/labels"
    28  	"sigs.k8s.io/prow/pkg/plugins"
    29  	"sigs.k8s.io/prow/pkg/plugins/cherrypickapproved/cherrypickapprovedfakes"
    30  )
    31  
    32  const testOrgRepo = "kubernetes"
    33  
    34  var errTest = errors.New("test")
    35  
    36  func TestHandle(t *testing.T) {
    37  	t.Parallel()
    38  
    39  	successEvent := &github.ReviewEvent{
    40  		Action: github.ReviewActionSubmitted,
    41  		Review: github.Review{
    42  			State: github.ReviewStateApproved,
    43  			User:  github.User{Login: "user"},
    44  		},
    45  		Repo: github.Repo{
    46  			Name:  testOrgRepo,
    47  			Owner: github.User{Login: testOrgRepo},
    48  		},
    49  		PullRequest: github.PullRequest{
    50  			Base: github.PullRequestBranch{Ref: "release-1.29"},
    51  		},
    52  	}
    53  
    54  	testConfig := []plugins.CherryPickApproved{
    55  		{
    56  			Org:       testOrgRepo,
    57  			Repo:      testOrgRepo,
    58  			BranchRe:  regexp.MustCompile("^release-*"),
    59  			Approvers: []string{"user"},
    60  		},
    61  	}
    62  
    63  	for _, tc := range []struct {
    64  		name        string
    65  		config      []plugins.CherryPickApproved
    66  		modifyEvent func(*github.ReviewEvent) *github.ReviewEvent
    67  		prepare     func(*cherrypickapprovedfakes.FakeImpl)
    68  		assert      func(*cherrypickapprovedfakes.FakeImpl, error)
    69  	}{
    70  		{
    71  			name:   "success apply cherry-pick-approved label",
    72  			config: testConfig,
    73  			prepare: func(mock *cherrypickapprovedfakes.FakeImpl) {
    74  				mock.GetCombinedStatusReturns(&github.CombinedStatus{}, nil)
    75  				mock.GetIssueLabelsReturns(
    76  					[]github.Label{
    77  						{Name: labels.LGTM},
    78  						{Name: labels.Approved},
    79  						{Name: labels.CpUnapproved},
    80  					},
    81  					nil,
    82  				)
    83  			},
    84  			assert: func(mock *cherrypickapprovedfakes.FakeImpl, err error) {
    85  				assert.NoError(t, err)
    86  				assert.EqualValues(t, 1, mock.AddLabelCallCount())
    87  				assert.EqualValues(t, 1, mock.RemoveLabelCallCount())
    88  			},
    89  		},
    90  		{
    91  			name:   "success but failed to apply/remove labels",
    92  			config: testConfig,
    93  			prepare: func(mock *cherrypickapprovedfakes.FakeImpl) {
    94  				mock.GetCombinedStatusReturns(&github.CombinedStatus{}, nil)
    95  				mock.GetIssueLabelsReturns(
    96  					[]github.Label{
    97  						{Name: labels.LGTM},
    98  						{Name: labels.Approved},
    99  						{Name: labels.CpUnapproved},
   100  					},
   101  					nil,
   102  				)
   103  				mock.AddLabelReturns(errTest)
   104  				mock.RemoveLabelReturns(errTest)
   105  			},
   106  			assert: func(mock *cherrypickapprovedfakes.FakeImpl, err error) {
   107  				assert.NoError(t, err)
   108  				assert.EqualValues(t, 1, mock.AddLabelCallCount())
   109  				assert.EqualValues(t, 1, mock.RemoveLabelCallCount())
   110  			},
   111  		},
   112  		{
   113  			name: "skip non approver",
   114  			config: []plugins.CherryPickApproved{{
   115  				Org:       testOrgRepo,
   116  				Repo:      testOrgRepo,
   117  				BranchRe:  regexp.MustCompile("^release-*"),
   118  				Approvers: []string{"wrong"},
   119  			}},
   120  			prepare: func(mock *cherrypickapprovedfakes.FakeImpl) {
   121  				mock.GetCombinedStatusReturns(&github.CombinedStatus{}, nil)
   122  				mock.GetIssueLabelsReturns(
   123  					[]github.Label{
   124  						{Name: labels.LGTM},
   125  						{Name: labels.Approved},
   126  						{Name: labels.CpUnapproved},
   127  					},
   128  					nil,
   129  				)
   130  			},
   131  			assert: func(mock *cherrypickapprovedfakes.FakeImpl, err error) {
   132  				assert.NoError(t, err)
   133  				assert.EqualValues(t, 0, mock.AddLabelCallCount())
   134  				assert.EqualValues(t, 0, mock.RemoveLabelCallCount())
   135  			},
   136  		},
   137  		{
   138  			name:   "error on GetIssueLabels",
   139  			config: testConfig,
   140  			prepare: func(mock *cherrypickapprovedfakes.FakeImpl) {
   141  				mock.GetCombinedStatusReturns(&github.CombinedStatus{}, nil)
   142  				mock.GetIssueLabelsReturns(nil, errTest)
   143  			},
   144  			assert: func(mock *cherrypickapprovedfakes.FakeImpl, err error) {
   145  				assert.Error(t, err)
   146  			},
   147  		},
   148  		{
   149  			name:   "error on GetCombinedStatus",
   150  			config: testConfig,
   151  			prepare: func(mock *cherrypickapprovedfakes.FakeImpl) {
   152  				mock.GetCombinedStatusReturns(nil, errTest)
   153  			},
   154  			assert: func(mock *cherrypickapprovedfakes.FakeImpl, err error) {
   155  				assert.Error(t, err)
   156  			},
   157  		},
   158  		{
   159  			name:   "skip with failed tests",
   160  			config: testConfig,
   161  			prepare: func(mock *cherrypickapprovedfakes.FakeImpl) {
   162  				mock.GetCombinedStatusReturns(&github.CombinedStatus{
   163  					Statuses: []github.Status{{State: github.StatusError}},
   164  				}, nil)
   165  			},
   166  			assert: func(mock *cherrypickapprovedfakes.FakeImpl, err error) {
   167  				assert.NoError(t, err)
   168  				assert.EqualValues(t, 0, mock.GetIssueLabelsCallCount())
   169  			},
   170  		},
   171  		{
   172  			name:   "skip with wrong review state",
   173  			config: testConfig,
   174  			modifyEvent: func(e *github.ReviewEvent) *github.ReviewEvent {
   175  				newEvent := *e
   176  				newEvent.Review.State = github.ReviewStateCommented
   177  				return &newEvent
   178  			},
   179  			prepare: func(mock *cherrypickapprovedfakes.FakeImpl) {},
   180  			assert: func(mock *cherrypickapprovedfakes.FakeImpl, err error) {
   181  				assert.NoError(t, err)
   182  				assert.EqualValues(t, 0, mock.GetCombinedStatusCallCount())
   183  			},
   184  		},
   185  		{
   186  			name:   "skip with wrong review action",
   187  			config: testConfig,
   188  			modifyEvent: func(e *github.ReviewEvent) *github.ReviewEvent {
   189  				newEvent := *e
   190  				newEvent.Action = github.ReviewActionEdited
   191  				return &newEvent
   192  			},
   193  			prepare: func(mock *cherrypickapprovedfakes.FakeImpl) {},
   194  			assert: func(mock *cherrypickapprovedfakes.FakeImpl, err error) {
   195  				assert.NoError(t, err)
   196  				assert.EqualValues(t, 0, mock.GetCombinedStatusCallCount())
   197  			},
   198  		},
   199  		{
   200  			name:   "skip with non matching branch",
   201  			config: testConfig,
   202  			modifyEvent: func(e *github.ReviewEvent) *github.ReviewEvent {
   203  				newEvent := *e
   204  				newEvent.PullRequest.Base.Ref = "invalid"
   205  				return &newEvent
   206  			},
   207  			prepare: func(mock *cherrypickapprovedfakes.FakeImpl) {},
   208  			assert: func(mock *cherrypickapprovedfakes.FakeImpl, err error) {
   209  				assert.NoError(t, err)
   210  				assert.EqualValues(t, 0, mock.GetCombinedStatusCallCount())
   211  			},
   212  		},
   213  		{
   214  			name: "skip with no approvers",
   215  			config: []plugins.CherryPickApproved{{
   216  				Org:      testOrgRepo,
   217  				Repo:     testOrgRepo,
   218  				BranchRe: regexp.MustCompile("^release-*"),
   219  			}},
   220  			prepare: func(mock *cherrypickapprovedfakes.FakeImpl) {},
   221  			assert: func(mock *cherrypickapprovedfakes.FakeImpl, err error) {
   222  				assert.NoError(t, err)
   223  				assert.EqualValues(t, 0, mock.GetCombinedStatusCallCount())
   224  			},
   225  		},
   226  		{
   227  			name:   "skip with non matching repo",
   228  			config: testConfig,
   229  			modifyEvent: func(e *github.ReviewEvent) *github.ReviewEvent {
   230  				newEvent := *e
   231  				newEvent.Repo.Name = "invalid"
   232  				return &newEvent
   233  			},
   234  			prepare: func(mock *cherrypickapprovedfakes.FakeImpl) {},
   235  			assert: func(mock *cherrypickapprovedfakes.FakeImpl, err error) {
   236  				assert.NoError(t, err)
   237  				assert.EqualValues(t, 0, mock.GetCombinedStatusCallCount())
   238  			},
   239  		},
   240  		{
   241  			name:   "skip with non matching org",
   242  			config: testConfig,
   243  			modifyEvent: func(e *github.ReviewEvent) *github.ReviewEvent {
   244  				newEvent := *e
   245  				newEvent.Repo.Owner.Login = "invalid"
   246  				return &newEvent
   247  			},
   248  			prepare: func(mock *cherrypickapprovedfakes.FakeImpl) {},
   249  			assert: func(mock *cherrypickapprovedfakes.FakeImpl, err error) {
   250  				assert.NoError(t, err)
   251  				assert.EqualValues(t, 0, mock.GetCombinedStatusCallCount())
   252  			},
   253  		},
   254  	} {
   255  		t.Run(tc.name, func(t *testing.T) {
   256  			mock := &cherrypickapprovedfakes.FakeImpl{}
   257  			tc.prepare(mock)
   258  
   259  			event := successEvent
   260  			if tc.modifyEvent != nil {
   261  				event = tc.modifyEvent(event)
   262  			}
   263  
   264  			sut := newHandler()
   265  			sut.impl = mock
   266  
   267  			log := logrus.NewEntry(logrus.StandardLogger())
   268  			err := sut.handle(log, nil, *event, tc.config)
   269  
   270  			tc.assert(mock, err)
   271  		})
   272  	}
   273  }