github.com/abayer/test-infra@v0.0.5/prow/plugins/cat/cat_test.go (about) 1 /* 2 Copyright 2018 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 cat 18 19 import ( 20 "errors" 21 "flag" 22 "fmt" 23 "io" 24 "net/http" 25 "net/http/httptest" 26 "regexp" 27 "strings" 28 "testing" 29 30 "github.com/sirupsen/logrus" 31 32 "k8s.io/test-infra/prow/github" 33 "k8s.io/test-infra/prow/github/fakegithub" 34 ) 35 36 type fakeClowder string 37 38 var human = flag.Bool("human", false, "Enable to run additional manual tests") 39 var category = flag.String("category", "", "Request a particular category if set") 40 var path = flag.String("key-path", "", "Path to api key if set") 41 42 func (c fakeClowder) readCat(category string) (string, error) { 43 if category == "error" { 44 return "", errors.New(string(c)) 45 } 46 return fmt.Sprintf("![fake cat image](%s)", c), nil 47 } 48 49 func TestRealCat(t *testing.T) { 50 if !*human { 51 t.Skip("Real cats disabled for automation. Manual users can add --human [--category=foo]") 52 } 53 if *path != "" { 54 meow.setKey(*path, logrus.WithField("plugin", pluginName)) 55 } 56 57 if cat, err := meow.readCat(*category); err != nil { 58 t.Errorf("Could not read cats from %#v: %v", meow, err) 59 } else { 60 fmt.Println(cat) 61 } 62 } 63 64 func TestUrl(t *testing.T) { 65 cases := []struct { 66 name string 67 url string 68 category string 69 key string 70 require []string 71 deny []string 72 }{ 73 { 74 name: "only url", 75 url: "http://foo", 76 }, 77 { 78 name: "key", 79 url: "http://foo", 80 key: "blah", 81 require: []string{"api_key=blah"}, 82 deny: []string{"category="}, 83 }, 84 { 85 name: "category", 86 url: "http://foo", 87 category: "bar", 88 require: []string{"category=bar"}, 89 deny: []string{"api_key="}, 90 }, 91 { 92 name: "category and key", 93 url: "http://foo", 94 category: "this", 95 key: "that", 96 require: []string{"category=this", "api_key=that", "&"}, 97 }, 98 } 99 100 for _, tc := range cases { 101 rc := realClowder{ 102 url: tc.url, 103 key: tc.key, 104 } 105 url := rc.Url(tc.category) 106 for _, r := range tc.require { 107 if !strings.Contains(url, r) { 108 t.Errorf("%s: %s does not contain %s", tc.name, url, r) 109 } 110 } 111 for _, d := range tc.deny { 112 if strings.Contains(url, d) { 113 t.Errorf("%s: %s contained unexpected %s", tc.name, url, d) 114 } 115 } 116 } 117 } 118 119 func TestFormat(t *testing.T) { 120 re := regexp.MustCompile(`\[!\[.+\]\(.+\)\]\(.+\)`) 121 basicURL := "http://example.com" 122 testcases := []struct { 123 name string 124 src string 125 img string 126 err bool 127 }{ 128 { 129 name: "basically works", 130 src: basicURL, 131 img: basicURL, 132 err: false, 133 }, 134 { 135 name: "empty source", 136 src: "", 137 img: basicURL, 138 err: true, 139 }, 140 { 141 name: "empty image", 142 src: basicURL, 143 img: "", 144 err: true, 145 }, 146 { 147 name: "bad source", 148 src: "http://this is not a url", 149 img: basicURL, 150 err: true, 151 }, 152 { 153 name: "bad image", 154 src: basicURL, 155 img: "http://still a bad url", 156 err: true, 157 }, 158 } 159 for _, tc := range testcases { 160 ret, err := catResult{ 161 Source: tc.src, 162 Image: tc.img, 163 }.Format() 164 switch { 165 case tc.err: 166 if err == nil { 167 t.Errorf("%s: failed to raise an error", tc.name) 168 } 169 case err != nil: 170 t.Errorf("%s: unexpected error: %v", tc.name, err) 171 case !re.MatchString(ret): 172 t.Errorf("%s: bad return value: %s", tc.name, ret) 173 } 174 } 175 } 176 177 // Medium integration test (depends on ability to open a TCP port) 178 func TestHttpResponse(t *testing.T) { 179 // create test cases for handling content length of images 180 contentLength := make(map[string]string) 181 contentLength["/cat.jpg"] = "717987" 182 contentLength["/bigcat.jpg"] = "12647753" 183 184 // fake server for images 185 ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 186 if s, ok := contentLength[r.URL.Path]; ok { 187 body := "binary image" 188 w.Header().Set("Content-Length", s) 189 io.WriteString(w, body) 190 } else { 191 t.Errorf("Cannot find content length for %s", r.URL.Path) 192 } 193 })) 194 defer ts2.Close() 195 196 // create test cases for handling http responses 197 img := ts2.URL + "/cat.jpg" 198 bigimg := ts2.URL + "/bigcat.jpg" 199 src := "http://localhost?kind=source_url" 200 validResponse := fmt.Sprintf(`<response><data><images><image><url>%s</url><source_url>%s</source_url></image></images></data></response>`, img, src) 201 var testcases = []struct { 202 name string 203 path string 204 response string 205 isValid bool 206 }{ 207 { 208 name: "valid", 209 path: "/valid", 210 response: fmt.Sprintf(`<response><data><images><image><url>%s</url><source_url>%s</source_url></image></images></data></response>`, img, src), 211 isValid: true, 212 }, 213 { 214 name: "image too big", 215 path: "/too-big", 216 response: fmt.Sprintf(`<response><data><images><image><url>%s</url><source_url>%s</source_url></image></images></data></response>`, bigimg, src), 217 isValid: false, 218 }, 219 } 220 221 // fake server for image urls 222 pathToResponse := make(map[string]string) 223 for _, testcase := range testcases { 224 pathToResponse[testcase.path] = testcase.response 225 } 226 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 227 if r, ok := pathToResponse[r.URL.Path]; ok { 228 io.WriteString(w, r) 229 } else { 230 io.WriteString(w, validResponse) 231 } 232 })) 233 defer ts.Close() 234 235 // github fake client 236 fc := &fakegithub.FakeClient{ 237 IssueComments: make(map[int][]github.IssueComment), 238 } 239 240 // run test for each case 241 for _, testcase := range testcases { 242 fakemeow := &realClowder{url: ts.URL + testcase.path} 243 cat, err := fakemeow.readCat(*category) 244 if testcase.isValid && err != nil { 245 t.Errorf("For case %s, didn't expect error: %v", testcase.name, err) 246 } else if !testcase.isValid && err == nil { 247 t.Errorf("For case %s, expected error, received cat: %s", testcase.name, cat) 248 } 249 } 250 251 // fully test handling a comment 252 comment := "/meow" 253 254 e := &github.GenericCommentEvent{ 255 Action: github.GenericCommentActionCreated, 256 Body: comment, 257 Number: 5, 258 IssueState: "open", 259 } 260 if err := handle(fc, logrus.WithField("plugin", pluginName), e, &realClowder{url: ts.URL}, func() {}); err != nil { 261 t.Errorf("didn't expect error: %v", err) 262 return 263 } 264 if len(fc.IssueComments[5]) != 1 { 265 t.Error("should have commented.") 266 return 267 } 268 if c := fc.IssueComments[5][0]; !strings.Contains(c.Body, img) { 269 t.Errorf("missing image url: %s from comment: %v", img, c) 270 } else if !strings.Contains(c.Body, src) { 271 t.Errorf("missing source url: %s from comment: %v", src, c) 272 } 273 274 } 275 276 // Small, unit tests 277 func TestCats(t *testing.T) { 278 var testcases = []struct { 279 name string 280 action github.GenericCommentEventAction 281 body string 282 state string 283 pr bool 284 shouldComment bool 285 shouldError bool 286 }{ 287 { 288 name: "ignore edited comment", 289 state: "open", 290 action: github.GenericCommentActionEdited, 291 body: "/meow", 292 shouldComment: false, 293 shouldError: false, 294 }, 295 { 296 name: "leave cat on pr", 297 state: "open", 298 action: github.GenericCommentActionCreated, 299 body: "/meow", 300 pr: true, 301 shouldComment: true, 302 shouldError: false, 303 }, 304 { 305 name: "leave cat on issue", 306 state: "open", 307 action: github.GenericCommentActionCreated, 308 body: "/meow", 309 shouldComment: true, 310 shouldError: false, 311 }, 312 { 313 name: "leave cat on issue, trailing space", 314 state: "open", 315 action: github.GenericCommentActionCreated, 316 body: "/meow \r", 317 shouldComment: true, 318 shouldError: false, 319 }, 320 { 321 name: "categorical cat", 322 state: "open", 323 action: github.GenericCommentActionCreated, 324 body: "/meow clothes", 325 shouldComment: true, 326 shouldError: false, 327 }, 328 { 329 name: "bad cat", 330 state: "open", 331 action: github.GenericCommentActionCreated, 332 body: "/meow error", 333 shouldComment: true, 334 shouldError: true, 335 }, 336 } 337 for _, tc := range testcases { 338 fc := &fakegithub.FakeClient{ 339 IssueComments: make(map[int][]github.IssueComment), 340 } 341 e := &github.GenericCommentEvent{ 342 Action: tc.action, 343 Body: tc.body, 344 Number: 5, 345 IssueState: tc.state, 346 IsPR: tc.pr, 347 } 348 err := handle(fc, logrus.WithField("plugin", pluginName), e, fakeClowder("tubbs"), func() {}) 349 if !tc.shouldError && err != nil { 350 t.Errorf("%s: didn't expect error: %v", tc.name, err) 351 continue 352 } else if tc.shouldError && err == nil { 353 t.Errorf("%s: expected an error to occur", tc.name) 354 continue 355 } 356 if tc.shouldComment && len(fc.IssueComments[5]) != 1 { 357 t.Errorf("%s: should have commented.", tc.name) 358 } else if tc.shouldComment { 359 shouldImage := !tc.shouldError 360 body := fc.IssueComments[5][0].Body 361 hasImage := strings.Contains(body, "![") 362 if hasImage && !shouldImage { 363 t.Errorf("%s: unexpected image in %s", tc.name, body) 364 } else if !hasImage && shouldImage { 365 t.Errorf("%s: no image in %s", tc.name, body) 366 } 367 } else if !tc.shouldComment && len(fc.IssueComments[5]) != 0 { 368 t.Errorf("%s: should not have commented.", tc.name) 369 } 370 } 371 }