github.com/abayer/test-infra@v0.0.5/prow/external-plugins/needs-rebase/plugin/plugin_test.go (about)

     1  /*
     2  Copyright 2017 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 plugin
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"reflect"
    24  	"sort"
    25  	"testing"
    26  	"time"
    27  
    28  	"github.com/shurcooL/githubv4"
    29  	"github.com/sirupsen/logrus"
    30  
    31  	"k8s.io/test-infra/prow/github"
    32  	"k8s.io/test-infra/prow/plugins"
    33  )
    34  
    35  func testKey(org, repo string, num int) string {
    36  	return fmt.Sprintf("%s/%s#%d", org, repo, num)
    37  }
    38  
    39  type fghc struct {
    40  	allPRs []struct {
    41  		PullRequest pullRequest `graphql:"... on PullRequest"`
    42  	}
    43  
    44  	initialLabels []github.Label
    45  	mergeable     bool
    46  
    47  	// The following are maps are keyed using 'testKey'
    48  	commentCreated, commentDeleted map[string]bool
    49  	labelsAdded, labelsRemoved     map[string][]string
    50  }
    51  
    52  func newFakeClient(prs []pullRequest, initialLabels []string, mergeable bool) *fghc {
    53  	f := &fghc{
    54  		mergeable:      mergeable,
    55  		commentCreated: make(map[string]bool),
    56  		commentDeleted: make(map[string]bool),
    57  		labelsAdded:    make(map[string][]string),
    58  		labelsRemoved:  make(map[string][]string),
    59  	}
    60  	for _, pr := range prs {
    61  		s := struct {
    62  			PullRequest pullRequest `graphql:"... on PullRequest"`
    63  		}{pr}
    64  		f.allPRs = append(f.allPRs, s)
    65  	}
    66  	for _, label := range initialLabels {
    67  		f.initialLabels = append(f.initialLabels, github.Label{Name: label})
    68  	}
    69  	return f
    70  }
    71  
    72  func (f *fghc) GetIssueLabels(org, repo string, number int) ([]github.Label, error) {
    73  	return f.initialLabels, nil
    74  }
    75  
    76  func (f *fghc) CreateComment(org, repo string, number int, comment string) error {
    77  	f.commentCreated[testKey(org, repo, number)] = true
    78  	return nil
    79  }
    80  
    81  func (f *fghc) BotName() (string, error) {
    82  	return "k8s-ci-robot", nil
    83  }
    84  
    85  func (f *fghc) AddLabel(org, repo string, number int, label string) error {
    86  	key := testKey(org, repo, number)
    87  	f.labelsAdded[key] = append(f.labelsAdded[key], label)
    88  	return nil
    89  }
    90  
    91  func (f *fghc) RemoveLabel(org, repo string, number int, label string) error {
    92  	key := testKey(org, repo, number)
    93  	f.labelsRemoved[key] = append(f.labelsRemoved[key], label)
    94  	return nil
    95  }
    96  
    97  func (f *fghc) IsMergeable(org, repo string, number int, sha string) (bool, error) {
    98  	return f.mergeable, nil
    99  }
   100  
   101  func (f *fghc) DeleteStaleComments(org, repo string, number int, comments []github.IssueComment, isStale func(github.IssueComment) bool) error {
   102  	f.commentDeleted[testKey(org, repo, number)] = true
   103  	return nil
   104  }
   105  
   106  func (f *fghc) Query(_ context.Context, q interface{}, _ map[string]interface{}) error {
   107  	query, ok := q.(*searchQuery)
   108  	if !ok {
   109  		return errors.New("invalid query format")
   110  	}
   111  	query.Search.Nodes = f.allPRs
   112  	return nil
   113  }
   114  
   115  func (f *fghc) compareExpected(t *testing.T, org, repo string, num int, expectedAdded []string, expectedRemoved []string, expectComment bool, expectDeletion bool) {
   116  	key := testKey(org, repo, num)
   117  	sort.Strings(expectedAdded)
   118  	sort.Strings(expectedRemoved)
   119  	sort.Strings(f.labelsAdded[key])
   120  	sort.Strings(f.labelsRemoved[key])
   121  	if !reflect.DeepEqual(expectedAdded, f.labelsAdded[key]) {
   122  		t.Errorf("Expected the following labels to be added to %s: %q, but got %q.", key, expectedAdded, f.labelsAdded[key])
   123  	}
   124  	if !reflect.DeepEqual(expectedRemoved, f.labelsRemoved[key]) {
   125  		t.Errorf("Expected the following labels to be removed from %s: %q, but got %q.", key, expectedRemoved, f.labelsRemoved[key])
   126  	}
   127  	if expectComment && !f.commentCreated[key] {
   128  		t.Errorf("Expected a comment to be created on %s, but none was.", key)
   129  	} else if !expectComment && f.commentCreated[key] {
   130  		t.Errorf("Unexpected comment on %s.", key)
   131  	}
   132  	if expectDeletion && !f.commentDeleted[key] {
   133  		t.Errorf("Expected a comment to be deleted from %s, but none was.", key)
   134  	} else if !expectDeletion && f.commentDeleted[key] {
   135  		t.Errorf("Unexpected comment deletion on %s.", key)
   136  	}
   137  }
   138  
   139  func TestHandleEvent(t *testing.T) {
   140  	oldSleep := sleep
   141  	sleep = func(time.Duration) { return }
   142  	defer func() { sleep = oldSleep }()
   143  
   144  	testCases := []struct {
   145  		name string
   146  
   147  		mergeable bool
   148  		labels    []string
   149  
   150  		expectedAdded   []string
   151  		expectedRemoved []string
   152  		expectComment   bool
   153  		expectDeletion  bool
   154  	}{
   155  		{
   156  			name:      "mergeable no-op",
   157  			mergeable: true,
   158  			labels:    []string{"lgtm", "needs-sig"},
   159  		},
   160  		{
   161  			name:      "unmergeable no-op",
   162  			mergeable: false,
   163  			labels:    []string{"lgtm", "needs-sig", "needs-rebase"},
   164  		},
   165  		{
   166  			name:      "mergeable -> unmergeable",
   167  			mergeable: false,
   168  			labels:    []string{"lgtm", "needs-sig"},
   169  
   170  			expectedAdded: []string{"needs-rebase"},
   171  			expectComment: true,
   172  		},
   173  		{
   174  			name:      "unmergeable -> mergeable",
   175  			mergeable: true,
   176  			labels:    []string{"lgtm", "needs-sig", "needs-rebase"},
   177  
   178  			expectedRemoved: []string{"needs-rebase"},
   179  			expectDeletion:  true,
   180  		},
   181  	}
   182  
   183  	for _, tc := range testCases {
   184  		fake := newFakeClient(nil, tc.labels, tc.mergeable)
   185  		pre := &github.PullRequestEvent{
   186  			Action: github.PullRequestActionSynchronize,
   187  			Repo: github.Repo{
   188  				Name:  "repo",
   189  				Owner: github.User{Login: "org"},
   190  			},
   191  			Number: 5,
   192  		}
   193  		t.Logf("Running test scenario: %q", tc.name)
   194  		if err := HandleEvent(logrus.WithField("plugin", pluginName), fake, pre); err != nil {
   195  			t.Fatalf("Unexpected error handling event: %v.", err)
   196  		}
   197  		fake.compareExpected(t, "org", "repo", 5, tc.expectedAdded, tc.expectedRemoved, tc.expectComment, tc.expectDeletion)
   198  	}
   199  }
   200  
   201  func TestHandleAll(t *testing.T) {
   202  	testPRs := []struct {
   203  		labels    []string
   204  		mergeable bool
   205  
   206  		expectedAdded, expectedRemoved []string
   207  		expectComment, expectDeletion  bool
   208  	}{
   209  		{
   210  			mergeable: true,
   211  			labels:    []string{"lgtm", "needs-sig"},
   212  		},
   213  		{
   214  			mergeable: false,
   215  			labels:    []string{"lgtm", "needs-sig", "needs-rebase"},
   216  		},
   217  		{
   218  			mergeable: false,
   219  			labels:    []string{"lgtm", "needs-sig"},
   220  
   221  			expectedAdded: []string{"needs-rebase"},
   222  			expectComment: true,
   223  		},
   224  		{
   225  			mergeable: true,
   226  			labels:    []string{"lgtm", "needs-sig", "needs-rebase"},
   227  
   228  			expectedRemoved: []string{"needs-rebase"},
   229  			expectDeletion:  true,
   230  		},
   231  	}
   232  
   233  	prs := []pullRequest{}
   234  	for i, testPR := range testPRs {
   235  		pr := pullRequest{
   236  			Number: githubv4.Int(i),
   237  		}
   238  		if testPR.mergeable {
   239  			pr.Mergeable = githubv4.MergeableStateMergeable
   240  		} else {
   241  			pr.Mergeable = githubv4.MergeableStateConflicting
   242  		}
   243  		for _, label := range testPR.labels {
   244  			s := struct {
   245  				Name githubv4.String
   246  			}{
   247  				Name: githubv4.String(label),
   248  			}
   249  			pr.Labels.Nodes = append(pr.Labels.Nodes, s)
   250  		}
   251  		prs = append(prs, pr)
   252  	}
   253  	fake := newFakeClient(prs, nil, false)
   254  	config := &plugins.Configuration{
   255  		Plugins: map[string][]string{"/": {"lgtm", pluginName}},
   256  
   257  		ExternalPlugins: map[string][]plugins.ExternalPlugin{"/": {{Name: pluginName}}},
   258  	}
   259  
   260  	if err := HandleAll(logrus.WithField("plugin", pluginName), fake, config); err != nil {
   261  		t.Fatalf("Unexpected error handling all prs: %v.", err)
   262  	}
   263  	for i, pr := range testPRs {
   264  		fake.compareExpected(t, "", "", i, pr.expectedAdded, pr.expectedRemoved, pr.expectComment, pr.expectDeletion)
   265  	}
   266  }