github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/plugins/pony/pony_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 pony 18 19 import ( 20 "encoding/json" 21 "flag" 22 "fmt" 23 "io" 24 "net/http" 25 "net/http/httptest" 26 "strings" 27 "testing" 28 29 "github.com/sirupsen/logrus" 30 "k8s.io/test-infra/prow/github" 31 "k8s.io/test-infra/prow/github/fakegithub" 32 ) 33 34 type fakeHerd string 35 36 var human = flag.Bool("human", false, "Enable to run additional manual tests") 37 var ponyFlag = flag.String("pony", "", "Request a particular pony if set") 38 39 func (c fakeHerd) readPony(tags string) (string, error) { 40 if tags != "" { 41 return tags, nil 42 } 43 return string(c), nil 44 } 45 46 func TestRealPony(t *testing.T) { 47 if !*human { 48 t.Skip("Real ponies disabled for automation. Manual users can add --human [--category=foo]") 49 } 50 if pony, err := ponyURL.readPony(*ponyFlag); err != nil { 51 t.Errorf("Could not read pony from %s: %v", ponyURL, err) 52 } else { 53 fmt.Println(pony) 54 } 55 } 56 57 func TestFormat(t *testing.T) { 58 result := formatURLs("http://example.com/small", "http://example.com/full") 59 expected := "[](http://example.com/full)" 60 if result != expected { 61 t.Errorf("Expected %q, but got %q", expected, result) 62 } 63 } 64 65 // Medium integration test (depends on ability to open a TCP port) 66 func TestHttpResponse(t *testing.T) { 67 68 // create test cases for handling content length of images 69 contentLength := make(map[string]string) 70 contentLength["/pony.jpg"] = "717987" 71 contentLength["/horse.png"] = "12647753" 72 73 // fake server for images 74 ts2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 75 if r.URL.Path == "/full" { 76 t.Errorf("Requested full-size image instead of small image.") 77 http.NotFound(w, r) 78 return 79 } 80 if s, ok := contentLength[r.URL.Path]; ok { 81 body := "binary image" 82 w.Header().Set("Content-Length", s) 83 io.WriteString(w, body) 84 } else { 85 t.Errorf("Cannot find content length for %s", r.URL.Path) 86 } 87 })) 88 defer ts2.Close() 89 90 // setup a stock valid request 91 url := ts2.URL + "/pony.jpg" 92 b, err := json.Marshal(&ponyResult{ 93 Pony: ponyResultPony{ 94 Representations: ponyRepresentations{ 95 Small: ts2.URL + "/pony.jpg", 96 Full: ts2.URL + "/full", 97 }, 98 }, 99 }) 100 if err != nil { 101 t.Errorf("Failed to encode test data: %v", err) 102 } 103 104 // create test cases for handling http responses 105 validResponse := string(b) 106 107 type testcase struct { 108 name string 109 comment string 110 path string 111 response string 112 expected string 113 expectTag string 114 isValid bool 115 noPony bool 116 } 117 118 var testcases = []testcase{ 119 { 120 name: "valid", 121 comment: "/pony", 122 path: "/valid", 123 response: validResponse, 124 expected: url, 125 isValid: true, 126 }, 127 { 128 name: "no pony found", 129 comment: "/pony", 130 path: "/404", 131 noPony: true, 132 isValid: false, 133 }, 134 { 135 name: "invalid JSON", 136 comment: "/pony", 137 path: "/bad-json", 138 response: `{"bad-blob": "not-a-url"`, 139 isValid: false, 140 }, 141 { 142 name: "image too big", 143 comment: "/pony", 144 path: "/too-big", 145 response: fmt.Sprintf(`{"pony":{"representations": {"small": "%s/horse.png", "full": "%s/full"}}}`, ts2.URL, ts2.URL), 146 isValid: false, 147 }, 148 { 149 name: "has tag", 150 comment: "/pony peach hack", 151 path: "/peach", 152 isValid: true, 153 expectTag: "peach hack", 154 response: validResponse, 155 }, 156 } 157 158 // fake server for image urls 159 pathToTestCase := make(map[string]*testcase) 160 for _, testcase := range testcases { 161 tc := testcase 162 pathToTestCase[testcase.path] = &tc 163 } 164 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 165 if tc, ok := pathToTestCase[r.URL.Path]; ok { 166 if tc.noPony { 167 http.NotFound(w, r) 168 return 169 } 170 q := r.URL.Query().Get("q") 171 if strings.HasSuffix(q, ",") { 172 t.Errorf("Expected query without trailing comma: %q", q) 173 } 174 if tc.expectTag != "" && !strings.HasSuffix(q, ", "+tc.expectTag) { 175 t.Errorf("Expected tag %q, but didn't find it in %q", tc.expectTag, q) 176 } 177 io.WriteString(w, tc.response) 178 } else { 179 io.WriteString(w, validResponse) 180 } 181 })) 182 defer ts.Close() 183 184 // run test for each case 185 for _, testcase := range testcases { 186 pony, err := realHerd(ts.URL + testcase.path).readPony(testcase.expectTag) 187 if testcase.isValid && err != nil { 188 t.Errorf("For case %s, didn't expect error: %v", testcase.name, err) 189 } else if !testcase.isValid && err == nil { 190 t.Errorf("For case %s, expected error, received pony: %s", testcase.name, pony) 191 } 192 193 if !testcase.isValid { 194 continue 195 } 196 197 // github fake client 198 fc := &fakegithub.FakeClient{ 199 IssueComments: make(map[int][]github.IssueComment), 200 } 201 202 // fully test handling a comment 203 e := &github.GenericCommentEvent{ 204 Action: github.GenericCommentActionCreated, 205 Body: testcase.comment, 206 Number: 5, 207 IssueState: "open", 208 } 209 err = handle(fc, logrus.WithField("plugin", pluginName), e, realHerd(ts.URL+testcase.path)) 210 if err != nil { 211 t.Errorf("tc %s: For comment %s, didn't expect error: %v", testcase.name, testcase.comment, err) 212 } 213 214 if len(fc.IssueComments[5]) != 1 { 215 t.Errorf("tc %s: should have commented", testcase.name) 216 } 217 if c := fc.IssueComments[5][0]; !strings.Contains(c.Body, testcase.expected) { 218 t.Errorf("tc %s: missing image url: %s from comment: %v", testcase.name, testcase.expected, c.Body) 219 } 220 } 221 } 222 223 // Small, unit tests 224 func TestPonies(t *testing.T) { 225 var testcases = []struct { 226 name string 227 action github.GenericCommentEventAction 228 body string 229 state string 230 pr bool 231 shouldComment bool 232 }{ 233 { 234 name: "ignore edited comment", 235 state: "open", 236 action: github.GenericCommentActionEdited, 237 body: "/pony", 238 shouldComment: false, 239 }, 240 { 241 name: "leave pony on pr", 242 state: "open", 243 action: github.GenericCommentActionCreated, 244 body: "/pony", 245 pr: true, 246 shouldComment: true, 247 }, 248 { 249 name: "leave pony on issue", 250 state: "open", 251 action: github.GenericCommentActionCreated, 252 body: "/pony", 253 shouldComment: true, 254 }, 255 { 256 name: "leave pony on issue, trailing space", 257 state: "open", 258 action: github.GenericCommentActionCreated, 259 body: "/pony \r", 260 shouldComment: true, 261 }, 262 { 263 name: "leave pony on issue, tag specified", 264 state: "open", 265 action: github.GenericCommentActionCreated, 266 body: "/pony Twilight Sparkle", 267 shouldComment: true, 268 }, 269 { 270 name: "leave pony on issue, tag specified, trailing space", 271 state: "open", 272 action: github.GenericCommentActionCreated, 273 body: "/pony Twilight Sparkle \r", 274 shouldComment: true, 275 }, 276 { 277 name: "don't leave cats or dogs", 278 state: "open", 279 action: github.GenericCommentActionCreated, 280 body: "/woof\n/meow", 281 shouldComment: false, 282 }, 283 { 284 name: "do nothing in the middle of a line", 285 state: "open", 286 action: github.GenericCommentActionCreated, 287 body: "did you know that /pony makes ponies happen?", 288 shouldComment: false, 289 }, 290 } 291 for _, tc := range testcases { 292 fc := &fakegithub.FakeClient{ 293 IssueComments: make(map[int][]github.IssueComment), 294 } 295 e := &github.GenericCommentEvent{ 296 Action: tc.action, 297 Body: tc.body, 298 Number: 5, 299 IssueState: tc.state, 300 IsPR: tc.pr, 301 } 302 err := handle(fc, logrus.WithField("plugin", pluginName), e, fakeHerd("pone")) 303 if err != nil { 304 t.Errorf("For case %s, didn't expect error: %v", tc.name, err) 305 } 306 if tc.shouldComment && len(fc.IssueComments[5]) != 1 { 307 t.Errorf("For case %s, should have commented.", tc.name) 308 } else if !tc.shouldComment && len(fc.IssueComments[5]) != 0 { 309 t.Errorf("For case %s, should not have commented.", tc.name) 310 } 311 } 312 }