github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/plugins/dog/dog_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 dog 18 19 import ( 20 "encoding/json" 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 fakePack string 37 38 var human = flag.Bool("human", false, "Enable to run additional manual tests") 39 40 func (c fakePack) readDog(dogURL string) (string, error) { 41 if dogURL != "" { 42 return dogURL, nil 43 } 44 return string(c), nil 45 } 46 47 func TestRealDog(t *testing.T) { 48 if !*human { 49 t.Skip("Real dogs disabled for automation. Manual users can add --human [--category=foo]") 50 } 51 if dog, err := dogURL.readDog(""); err != nil { 52 t.Errorf("Could not read dog from %s: %v", dogURL, err) 53 } else { 54 fmt.Println(dog) 55 } 56 } 57 58 func TestFormat(t *testing.T) { 59 re := regexp.MustCompile(`\[!\[.+\]\(.+\)\]\(.+\)`) 60 basicURL := "http://example.com" 61 testcases := []struct { 62 name string 63 url string 64 err bool 65 }{ 66 { 67 name: "basically works", 68 url: basicURL, 69 err: false, 70 }, 71 { 72 name: "empty url", 73 url: "", 74 err: true, 75 }, 76 { 77 name: "bad url", 78 url: "http://this is not a url", 79 err: true, 80 }, 81 } 82 for _, tc := range testcases { 83 ret, err := FormatURL(tc.url) 84 switch { 85 case tc.err: 86 if err == nil { 87 t.Errorf("%s: failed to raise an error", tc.name) 88 } 89 case err != nil: 90 t.Errorf("%s: unexpected error: %v", tc.name, err) 91 case !re.MatchString(ret): 92 t.Errorf("%s: bad return value: %s", tc.name, ret) 93 } 94 } 95 } 96 97 // Medium integration test (depends on ability to open a TCP port) 98 func TestHttpResponse(t *testing.T) { 99 100 // create test cases for handling content length of images 101 contentLength := make(map[string]string) 102 contentLength["/dog.jpg"] = "717987" 103 contentLength["/doggo.mp4"] = "37943259" 104 contentLength["/bigdog.jpg"] = "12647753" 105 106 // fake server for images 107 ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 108 if s, ok := contentLength[r.URL.Path]; ok { 109 body := "binary image" 110 w.Header().Set("Content-Length", s) 111 io.WriteString(w, body) 112 } else { 113 t.Errorf("Cannot find content length for %s", r.URL.Path) 114 } 115 })) 116 defer ts2.Close() 117 118 // setup a stock valid request 119 url := ts2.URL + "/dog.jpg" 120 b, err := json.Marshal(&dogResult{ 121 URL: url, 122 }) 123 if err != nil { 124 t.Errorf("Failed to encode test data: %v", err) 125 } 126 127 // create test cases for handling http responses 128 validResponse := string(b) 129 var testcases = []struct { 130 name string 131 comment string 132 path string 133 response string 134 expected string 135 isValid bool 136 }{ 137 { 138 name: "valid", 139 comment: "/woof", 140 path: "/valid", 141 response: validResponse, 142 expected: url, 143 isValid: true, 144 }, 145 { 146 name: "invalid JSON", 147 comment: "/woof", 148 path: "/bad-json", 149 response: `{"bad-blob": "not-a-url"`, 150 isValid: false, 151 }, 152 { 153 name: "invalid URL", 154 comment: "/woof", 155 path: "/bad-url", 156 response: `{"url": "not a url.."}`, 157 isValid: false, 158 }, 159 { 160 name: "mp4 doggo unsupported :(", 161 comment: "/woof", 162 path: "/mp4-doggo", 163 response: fmt.Sprintf(`{"url": "%s/doggo.mp4"}`, ts2.URL), 164 isValid: false, 165 }, 166 { 167 name: "image too big", 168 comment: "/woof", 169 path: "/too-big", 170 response: fmt.Sprintf(`{"url": "%s/bigdog.jpg"}`, ts2.URL), 171 isValid: false, 172 }, 173 { 174 name: "this is fine", 175 comment: "/this-is-fine", 176 expected: "this_is_fine.png", 177 isValid: true, 178 }, 179 { 180 name: "this is not fine", 181 comment: "/this-is-not-fine", 182 expected: "this_is_not_fine.png", 183 isValid: true, 184 }, 185 { 186 name: "this is unbearable", 187 comment: "/this-is-unbearable", 188 expected: "this_is_unbearable.jpg", 189 isValid: true, 190 }, 191 } 192 193 // fake server for image urls 194 pathToResponse := make(map[string]string) 195 for _, testcase := range testcases { 196 pathToResponse[testcase.path] = testcase.response 197 } 198 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 199 if r, ok := pathToResponse[r.URL.Path]; ok { 200 io.WriteString(w, r) 201 } else { 202 io.WriteString(w, validResponse) 203 } 204 })) 205 defer ts.Close() 206 207 // run test for each case 208 for _, testcase := range testcases { 209 dog, err := realPack(ts.URL + testcase.path).readDog("") 210 if testcase.isValid && err != nil { 211 t.Errorf("For case %s, didn't expect error: %v", testcase.name, err) 212 } else if !testcase.isValid && err == nil { 213 t.Errorf("For case %s, expected error, received dog: %s", testcase.name, dog) 214 } 215 216 if !testcase.isValid { 217 continue 218 } 219 220 // github fake client 221 fc := &fakegithub.FakeClient{ 222 IssueComments: make(map[int][]github.IssueComment), 223 } 224 225 // fully test handling a comment 226 e := &github.GenericCommentEvent{ 227 Action: github.GenericCommentActionCreated, 228 Body: testcase.comment, 229 Number: 5, 230 IssueState: "open", 231 } 232 err = handle(fc, logrus.WithField("plugin", pluginName), e, realPack(ts.URL)) 233 if err != nil { 234 t.Errorf("tc %s: For comment %s, didn't expect error: %v", testcase.name, testcase.comment, err) 235 } 236 237 if len(fc.IssueComments[5]) != 1 { 238 t.Errorf("tc %s: should have commented", testcase.name) 239 } 240 if c := fc.IssueComments[5][0]; !strings.Contains(c.Body, testcase.expected) { 241 t.Errorf("tc %s: missing image url: %s from comment: %v", testcase.name, testcase.expected, c.Body) 242 } 243 } 244 } 245 246 // Small, unit tests 247 func TestDogs(t *testing.T) { 248 var testcases = []struct { 249 name string 250 action github.GenericCommentEventAction 251 body string 252 state string 253 pr bool 254 shouldComment bool 255 }{ 256 { 257 name: "ignore edited comment", 258 state: "open", 259 action: github.GenericCommentActionEdited, 260 body: "/woof", 261 shouldComment: false, 262 }, 263 { 264 name: "leave dog on pr", 265 state: "open", 266 action: github.GenericCommentActionCreated, 267 body: "/woof", 268 pr: true, 269 shouldComment: true, 270 }, 271 { 272 name: "leave dog on issue", 273 state: "open", 274 action: github.GenericCommentActionCreated, 275 body: "/woof", 276 shouldComment: true, 277 }, 278 { 279 name: "leave dog on issue, trailing space", 280 state: "open", 281 action: github.GenericCommentActionCreated, 282 body: "/woof \r", 283 shouldComment: true, 284 }, 285 { 286 name: "leave dog on issue, trailing /bark", 287 state: "open", 288 action: github.GenericCommentActionCreated, 289 body: "/bark", 290 shouldComment: true, 291 }, 292 { 293 name: "leave dog on issue, trailing /bark, trailing space", 294 state: "open", 295 action: github.GenericCommentActionCreated, 296 body: "/bark \r", 297 shouldComment: true, 298 }, 299 { 300 name: "leave this-is-fine on pr", 301 state: "open", 302 action: github.GenericCommentActionCreated, 303 body: "/this-is-fine", 304 pr: true, 305 shouldComment: true, 306 }, 307 { 308 name: "leave this-is-not-fine on pr", 309 state: "open", 310 action: github.GenericCommentActionCreated, 311 body: "/this-is-not-fine", 312 pr: true, 313 shouldComment: true, 314 }, 315 { 316 name: "leave this-is-unbearable on pr", 317 state: "open", 318 action: github.GenericCommentActionCreated, 319 body: "/this-is-unbearable", 320 pr: true, 321 shouldComment: true, 322 }, 323 } 324 for _, tc := range testcases { 325 fc := &fakegithub.FakeClient{ 326 IssueComments: make(map[int][]github.IssueComment), 327 } 328 e := &github.GenericCommentEvent{ 329 Action: tc.action, 330 Body: tc.body, 331 Number: 5, 332 IssueState: tc.state, 333 IsPR: tc.pr, 334 } 335 err := handle(fc, logrus.WithField("plugin", pluginName), e, fakePack("doge")) 336 if err != nil { 337 t.Errorf("For case %s, didn't expect error: %v", tc.name, err) 338 } 339 if tc.shouldComment && len(fc.IssueComments[5]) != 1 { 340 t.Errorf("For case %s, should have commented.", tc.name) 341 } else if !tc.shouldComment && len(fc.IssueComments[5]) != 0 { 342 t.Errorf("For case %s, should not have commented.", tc.name) 343 } 344 } 345 }