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 }