github.com/yrj2011/jx-test-infra@v0.0.0-20190529031832-7a2065ee98eb/prow/cmd/deck/main_test.go (about) 1 /* 2 Copyright 2016 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 main 18 19 import ( 20 "bufio" 21 "bytes" 22 "encoding/json" 23 "errors" 24 "fmt" 25 "io" 26 "io/ioutil" 27 "net/http" 28 "net/http/httptest" 29 "net/url" 30 "reflect" 31 "strconv" 32 "testing" 33 "time" 34 35 "github.com/ghodss/yaml" 36 37 "k8s.io/test-infra/prow/config" 38 "k8s.io/test-infra/prow/kube" 39 "k8s.io/test-infra/prow/pluginhelp" 40 "k8s.io/test-infra/prow/tide" 41 ) 42 43 func TestOptions_Validate(t *testing.T) { 44 var testCases = []struct { 45 name string 46 input options 47 expectedErr bool 48 }{ 49 { 50 name: "minimal set ok", 51 input: options{ 52 configPath: "test", 53 }, 54 expectedErr: false, 55 }, 56 { 57 name: "missing configpath", 58 input: options{}, 59 expectedErr: true, 60 }, 61 { 62 name: "ok with oauth", 63 input: options{ 64 configPath: "test", 65 oauthURL: "website", 66 githubOAuthConfigFile: "something", 67 cookieSecretFile: "yum", 68 }, 69 expectedErr: false, 70 }, 71 { 72 name: "missing github config with oauth", 73 input: options{ 74 configPath: "test", 75 oauthURL: "website", 76 cookieSecretFile: "yum", 77 }, 78 expectedErr: true, 79 }, 80 { 81 name: "missing cookie with oauth", 82 input: options{ 83 configPath: "test", 84 oauthURL: "website", 85 githubOAuthConfigFile: "something", 86 }, 87 expectedErr: true, 88 }, 89 } 90 91 for _, testCase := range testCases { 92 err := testCase.input.Validate() 93 if testCase.expectedErr && err == nil { 94 t.Errorf("%s: expected an error but got none", testCase.name) 95 } 96 if !testCase.expectedErr && err != nil { 97 t.Errorf("%s: expected no error but got one: %v", testCase.name, err) 98 } 99 } 100 } 101 102 type flc int 103 104 func (f flc) GetJobLog(job, id string) ([]byte, error) { 105 if job == "job" && id == "123" { 106 return []byte("hello"), nil 107 } 108 return nil, errors.New("muahaha") 109 } 110 111 func TestHandleLog(t *testing.T) { 112 var testcases = []struct { 113 name string 114 path string 115 code int 116 }{ 117 { 118 name: "no job name", 119 path: "", 120 code: http.StatusBadRequest, 121 }, 122 { 123 name: "job but no id", 124 path: "?job=job", 125 code: http.StatusBadRequest, 126 }, 127 { 128 name: "id but no job", 129 path: "?id=123", 130 code: http.StatusBadRequest, 131 }, 132 { 133 name: "id and job, found", 134 path: "?job=job&id=123", 135 code: http.StatusOK, 136 }, 137 { 138 name: "id and job, not found", 139 path: "?job=ohno&id=123", 140 code: http.StatusNotFound, 141 }, 142 } 143 handler := handleLog(flc(0)) 144 for _, tc := range testcases { 145 req, err := http.NewRequest(http.MethodGet, "", nil) 146 if err != nil { 147 t.Fatalf("Error making request: %v", err) 148 } 149 u, err := url.Parse(tc.path) 150 if err != nil { 151 t.Fatalf("Error parsing URL: %v", err) 152 } 153 var follow = false 154 if ok, _ := strconv.ParseBool(u.Query().Get("follow")); ok { 155 follow = true 156 } 157 req.URL = u 158 rr := httptest.NewRecorder() 159 handler.ServeHTTP(rr, req) 160 if rr.Code != tc.code { 161 t.Errorf("Wrong error code. Got %v, want %v", rr.Code, tc.code) 162 } else if rr.Code == http.StatusOK { 163 if follow { 164 //wait a little to get the chunks 165 time.Sleep(2 * time.Millisecond) 166 reader := bufio.NewReader(rr.Body) 167 var buf bytes.Buffer 168 for { 169 line, err := reader.ReadBytes('\n') 170 if err == io.EOF { 171 break 172 } 173 if err != nil { 174 t.Fatalf("Expecting reply with content but got error: %v", err) 175 } 176 buf.Write(line) 177 } 178 if !bytes.Contains(buf.Bytes(), []byte("hello")) { 179 t.Errorf("Unexpected body: got %s.", buf.String()) 180 } 181 } else { 182 resp := rr.Result() 183 defer resp.Body.Close() 184 if body, err := ioutil.ReadAll(resp.Body); err != nil { 185 t.Errorf("Error reading response body: %v", err) 186 } else if string(body) != "hello" { 187 t.Errorf("Unexpected body: got %s.", string(body)) 188 } 189 } 190 } 191 } 192 } 193 194 type fpjc kube.ProwJob 195 196 func (fc *fpjc) GetProwJob(name string) (kube.ProwJob, error) { 197 return kube.ProwJob(*fc), nil 198 } 199 200 // TestRerun just checks that the result can be unmarshaled properly, has an 201 // updated status, and has equal spec. 202 func TestRerun(t *testing.T) { 203 fc := fpjc(kube.ProwJob{ 204 Spec: kube.ProwJobSpec{ 205 Job: "whoa", 206 Type: kube.PresubmitJob, 207 Refs: &kube.Refs{ 208 Org: "org", 209 Repo: "repo", 210 Pulls: []kube.Pull{ 211 {Number: 1}, 212 }, 213 }, 214 }, 215 Status: kube.ProwJobStatus{ 216 State: kube.PendingState, 217 }, 218 }) 219 handler := handleRerun(&fc) 220 req, err := http.NewRequest(http.MethodGet, "/rerun?prowjob=wowsuch", nil) 221 if err != nil { 222 t.Fatalf("Error making request: %v", err) 223 } 224 rr := httptest.NewRecorder() 225 handler.ServeHTTP(rr, req) 226 if rr.Code != http.StatusOK { 227 t.Fatalf("Bad error code: %d", rr.Code) 228 } 229 resp := rr.Result() 230 defer resp.Body.Close() 231 body, err := ioutil.ReadAll(resp.Body) 232 if err != nil { 233 t.Fatalf("Error reading response body: %v", err) 234 } 235 var res kube.ProwJob 236 if err := yaml.Unmarshal(body, &res); err != nil { 237 t.Fatalf("Error unmarshaling: %v", err) 238 } 239 if res.Spec.Job != "whoa" { 240 t.Errorf("Wrong job, expected \"whoa\", got \"%s\"", res.Spec.Job) 241 } 242 if res.Status.State != kube.TriggeredState { 243 t.Errorf("Wrong state, expected \"%v\", got \"%v\"", kube.TriggeredState, res.Status.State) 244 } 245 } 246 247 func TestTide(t *testing.T) { 248 s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 249 pools := []tide.Pool{ 250 { 251 Org: "o", 252 }, 253 } 254 b, err := json.Marshal(pools) 255 if err != nil { 256 t.Fatalf("Marshaling: %v", err) 257 } 258 fmt.Fprintf(w, string(b)) 259 })) 260 ca := &config.Agent{} 261 ca.Set(&config.Config{ 262 ProwConfig: config.ProwConfig{ 263 Tide: config.Tide{ 264 Queries: []config.TideQuery{ 265 {Repos: []string{"kubernetes/test-infra"}}, 266 }, 267 }, 268 }, 269 }) 270 ta := tideAgent{ 271 path: s.URL, 272 updatePeriod: func() time.Duration { return time.Minute }, 273 } 274 if err := ta.update(); err != nil { 275 t.Fatalf("Updating: %v", err) 276 } 277 if len(ta.pools) != 1 { 278 t.Fatalf("Wrong number of pools. Got %d, expected 1 in %v", len(ta.pools), ta.pools) 279 } 280 if ta.pools[0].Org != "o" { 281 t.Errorf("Wrong org in pool. Got %s, expected o in %v", ta.pools[0].Org, ta.pools) 282 } 283 handler := handleTide(ca, &ta) 284 req, err := http.NewRequest(http.MethodGet, "/tide.js", nil) 285 if err != nil { 286 t.Fatalf("Error making request: %v", err) 287 } 288 rr := httptest.NewRecorder() 289 handler.ServeHTTP(rr, req) 290 if rr.Code != http.StatusOK { 291 t.Fatalf("Bad error code: %d", rr.Code) 292 } 293 resp := rr.Result() 294 defer resp.Body.Close() 295 body, err := ioutil.ReadAll(resp.Body) 296 if err != nil { 297 t.Fatalf("Error reading response body: %v", err) 298 } 299 res := tideData{} 300 if err := yaml.Unmarshal(body, &res); err != nil { 301 t.Fatalf("Error unmarshaling: %v", err) 302 } 303 if len(res.Pools) != 1 { 304 t.Fatalf("Wrong number of pools. Got %d, expected 1 in %v", len(res.Pools), res.Pools) 305 } 306 if res.Pools[0].Org != "o" { 307 t.Errorf("Wrong org in pool. Got %s, expected o in %v", res.Pools[0].Org, res.Pools) 308 } 309 if len(res.Queries) != 1 { 310 t.Fatalf("Wrong number of pools. Got %d, expected 1 in %v", len(res.Queries), res.Queries) 311 } 312 if expected := "is:pr state:open repo:\"kubernetes/test-infra\""; res.Queries[0] != expected { 313 t.Errorf("Wrong query. Got %s, expected %s", res.Queries[0], expected) 314 } 315 } 316 317 func TestHelp(t *testing.T) { 318 hitCount := 0 319 help := pluginhelp.Help{ 320 AllRepos: []string{"org/repo"}, 321 RepoPlugins: map[string][]string{"org": {"plugin"}}, 322 RepoExternalPlugins: map[string][]string{"org/repo": {"external-plugin"}}, 323 PluginHelp: map[string]pluginhelp.PluginHelp{"plugin": {Description: "plugin"}}, 324 ExternalPluginHelp: map[string]pluginhelp.PluginHelp{"external-plugin": {Description: "external-plugin"}}, 325 } 326 s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 327 hitCount++ 328 b, err := json.Marshal(help) 329 if err != nil { 330 t.Fatalf("Marshaling: %v", err) 331 } 332 fmt.Fprintf(w, string(b)) 333 })) 334 ha := &helpAgent{ 335 path: s.URL, 336 } 337 handler := handlePluginHelp(ha) 338 handleAndCheck := func() { 339 req, err := http.NewRequest(http.MethodGet, "/plugin-help.js", nil) 340 if err != nil { 341 t.Fatalf("Error making request: %v", err) 342 } 343 rr := httptest.NewRecorder() 344 handler.ServeHTTP(rr, req) 345 if rr.Code != http.StatusOK { 346 t.Fatalf("Bad error code: %d", rr.Code) 347 } 348 resp := rr.Result() 349 defer resp.Body.Close() 350 body, err := ioutil.ReadAll(resp.Body) 351 if err != nil { 352 t.Fatalf("Error reading response body: %v", err) 353 } 354 var res pluginhelp.Help 355 if err := yaml.Unmarshal(body, &res); err != nil { 356 t.Fatalf("Error unmarshaling: %v", err) 357 } 358 if !reflect.DeepEqual(help, res) { 359 t.Errorf("Invalid plugin help. Got %v, expected %v", res, help) 360 } 361 if hitCount != 1 { 362 t.Errorf("Expected fake hook endpoint to be hit once, but endpoint was hit %d times.", hitCount) 363 } 364 } 365 handleAndCheck() 366 handleAndCheck() 367 }