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 }