sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/pkg/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 "sigs.k8s.io/prow/pkg/github" 33 "sigs.k8s.io/prow/pkg/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 contentLength["/this_is_fine.png"] = "317624" 106 contentLength["/this_is_not_fine.png"] = "645595" 107 contentLength["/this_is_unbearable.jpg"] = "34241" 108 109 // fake server for images 110 ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 111 if s, ok := contentLength[r.URL.Path]; ok { 112 body := "binary image" 113 w.Header().Set("Content-Length", s) 114 io.WriteString(w, body) 115 } else { 116 t.Errorf("Cannot find content length for %s", r.URL.Path) 117 } 118 })) 119 defer ts2.Close() 120 121 // setup a stock valid request 122 url := ts2.URL + "/dog.jpg" 123 b, err := json.Marshal(&dogResult{ 124 URL: url, 125 }) 126 if err != nil { 127 t.Errorf("Failed to encode test data: %v", err) 128 } 129 130 // create test cases for handling http responses 131 validResponse := string(b) 132 var testcases = []struct { 133 name string 134 comment string 135 path string 136 response string 137 expected string 138 isValid bool 139 }{ 140 { 141 name: "valid", 142 comment: "/woof", 143 path: "/valid", 144 response: validResponse, 145 expected: url, 146 isValid: true, 147 }, 148 { 149 name: "invalid JSON", 150 comment: "/woof", 151 path: "/bad-json", 152 response: `{"bad-blob": "not-a-url"`, 153 isValid: false, 154 }, 155 { 156 name: "invalid URL", 157 comment: "/woof", 158 path: "/bad-url", 159 response: `{"url": "not a url.."}`, 160 isValid: false, 161 }, 162 { 163 name: "mp4 doggo unsupported :(", 164 comment: "/woof", 165 path: "/mp4-doggo", 166 response: fmt.Sprintf(`{"url": "%s/doggo.mp4"}`, ts2.URL), 167 isValid: false, 168 }, 169 { 170 name: "image too big", 171 comment: "/woof", 172 path: "/too-big", 173 response: fmt.Sprintf(`{"url": "%s/bigdog.jpg"}`, ts2.URL), 174 isValid: false, 175 }, 176 { 177 name: "this is fine", 178 comment: "/this-is-fine", 179 expected: "this_is_fine.png", 180 isValid: true, 181 }, 182 { 183 name: "this is not fine", 184 comment: "/this-is-not-fine", 185 expected: "this_is_not_fine.png", 186 isValid: true, 187 }, 188 { 189 name: "this is unbearable", 190 comment: "/this-is-unbearable", 191 expected: "this_is_unbearable.jpg", 192 isValid: true, 193 }, 194 } 195 196 // fake server for image urls 197 pathToResponse := make(map[string]string) 198 for _, testcase := range testcases { 199 pathToResponse[testcase.path] = testcase.response 200 } 201 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 202 if r, ok := pathToResponse[r.URL.Path]; ok { 203 io.WriteString(w, r) 204 } else { 205 io.WriteString(w, validResponse) 206 } 207 })) 208 defer ts.Close() 209 210 // run test for each case 211 for _, testcase := range testcases { 212 dog, err := realPack(ts.URL + testcase.path).readDog("") 213 if testcase.isValid && err != nil { 214 t.Errorf("For case %s, didn't expect error: %v", testcase.name, err) 215 } else if !testcase.isValid && err == nil { 216 t.Errorf("For case %s, expected error, received dog: %s", testcase.name, dog) 217 } 218 219 if !testcase.isValid { 220 continue 221 } 222 223 // github fake client 224 fc := fakegithub.NewFakeClient() 225 fc.IssueComments = make(map[int][]github.IssueComment) 226 227 // fully test handling a comment 228 e := &github.GenericCommentEvent{ 229 Action: github.GenericCommentActionCreated, 230 Body: testcase.comment, 231 Number: 5, 232 IssueState: "open", 233 } 234 err = handle(fc, logrus.WithField("plugin", pluginName), e, realPack(ts.URL), ts2.URL+"/") 235 if err != nil { 236 t.Errorf("tc %s: For comment %s, didn't expect error: %v", testcase.name, testcase.comment, err) 237 } 238 239 if len(fc.IssueComments[5]) != 1 { 240 t.Errorf("tc %s: should have commented", testcase.name) 241 } 242 if c := fc.IssueComments[5][0]; !strings.Contains(c.Body, testcase.expected) { 243 t.Errorf("tc %s: missing image url: %s from comment: %v", testcase.name, testcase.expected, c.Body) 244 } 245 } 246 } 247 248 // Small, unit tests 249 func TestDogs(t *testing.T) { 250 var testcases = []struct { 251 name string 252 action github.GenericCommentEventAction 253 body string 254 state string 255 pr bool 256 shouldComment bool 257 }{ 258 { 259 name: "ignore edited comment", 260 state: "open", 261 action: github.GenericCommentActionEdited, 262 body: "/woof", 263 shouldComment: false, 264 }, 265 { 266 name: "leave dog on pr", 267 state: "open", 268 action: github.GenericCommentActionCreated, 269 body: "/woof", 270 pr: true, 271 shouldComment: true, 272 }, 273 { 274 name: "leave dog on issue", 275 state: "open", 276 action: github.GenericCommentActionCreated, 277 body: "/woof", 278 shouldComment: true, 279 }, 280 { 281 name: "leave dog on issue, trailing space", 282 state: "open", 283 action: github.GenericCommentActionCreated, 284 body: "/woof \r", 285 shouldComment: true, 286 }, 287 { 288 name: "leave dog on issue, trailing /bark", 289 state: "open", 290 action: github.GenericCommentActionCreated, 291 body: "/bark", 292 shouldComment: true, 293 }, 294 { 295 name: "leave dog on issue, trailing /bark, trailing space", 296 state: "open", 297 action: github.GenericCommentActionCreated, 298 body: "/bark \r", 299 shouldComment: true, 300 }, 301 { 302 name: "leave this-is-fine on pr", 303 state: "open", 304 action: github.GenericCommentActionCreated, 305 body: "/this-is-fine", 306 pr: true, 307 shouldComment: true, 308 }, 309 { 310 name: "leave this-is-not-fine on pr", 311 state: "open", 312 action: github.GenericCommentActionCreated, 313 body: "/this-is-not-fine", 314 pr: true, 315 shouldComment: true, 316 }, 317 { 318 name: "leave this-is-unbearable on pr", 319 state: "open", 320 action: github.GenericCommentActionCreated, 321 body: "/this-is-unbearable", 322 pr: true, 323 shouldComment: true, 324 }, 325 } 326 for _, tc := range testcases { 327 fc := fakegithub.NewFakeClient() 328 fc.IssueComments = make(map[int][]github.IssueComment) 329 e := &github.GenericCommentEvent{ 330 Action: tc.action, 331 Body: tc.body, 332 Number: 5, 333 IssueState: tc.state, 334 IsPR: tc.pr, 335 } 336 err := handle(fc, logrus.WithField("plugin", pluginName), e, fakePack("doge"), "") 337 if err != nil { 338 t.Errorf("For case %s, didn't expect error: %v", tc.name, err) 339 } 340 if tc.shouldComment && len(fc.IssueComments[5]) != 1 { 341 t.Errorf("For case %s, should have commented.", tc.name) 342 } else if !tc.shouldComment && len(fc.IssueComments[5]) != 0 { 343 t.Errorf("For case %s, should not have commented.", tc.name) 344 } 345 } 346 }