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