github.com/google/go-github/v70@v70.0.0/github/actions_workflow_jobs_test.go (about) 1 // Copyright 2020 The go-github AUTHORS. All rights reserved. 2 // 3 // Use of this source code is governed by a BSD-style 4 // license that can be found in the LICENSE file. 5 6 package github 7 8 import ( 9 "context" 10 "errors" 11 "fmt" 12 "net/http" 13 "net/url" 14 "strings" 15 "testing" 16 "time" 17 18 "github.com/google/go-cmp/cmp" 19 ) 20 21 func TestActionsService_ListWorkflowJobs(t *testing.T) { 22 t.Parallel() 23 client, mux, _ := setup(t) 24 25 mux.HandleFunc("/repos/o/r/actions/runs/29679449/jobs", func(w http.ResponseWriter, r *http.Request) { 26 testMethod(t, r, "GET") 27 testFormValues(t, r, values{"per_page": "2", "page": "2"}) 28 fmt.Fprint(w, `{"total_count":4,"jobs":[{"id":399444496,"run_id":29679449,"started_at":"2019-01-02T15:04:05Z","completed_at":"2020-01-02T15:04:05Z"},{"id":399444497,"run_id":29679449,"started_at":"2019-01-02T15:04:05Z","completed_at":"2020-01-02T15:04:05Z"}]}`) 29 }) 30 31 opts := &ListWorkflowJobsOptions{ListOptions: ListOptions{Page: 2, PerPage: 2}} 32 ctx := context.Background() 33 jobs, _, err := client.Actions.ListWorkflowJobs(ctx, "o", "r", 29679449, opts) 34 if err != nil { 35 t.Errorf("Actions.ListWorkflowJobs returned error: %v", err) 36 } 37 38 want := &Jobs{ 39 TotalCount: Ptr(4), 40 Jobs: []*WorkflowJob{ 41 {ID: Ptr(int64(399444496)), RunID: Ptr(int64(29679449)), StartedAt: &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, CompletedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}}, 42 {ID: Ptr(int64(399444497)), RunID: Ptr(int64(29679449)), StartedAt: &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, CompletedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}}, 43 }, 44 } 45 if !cmp.Equal(jobs, want) { 46 t.Errorf("Actions.ListWorkflowJobs returned %+v, want %+v", jobs, want) 47 } 48 49 const methodName = "ListWorkflowJobs" 50 testBadOptions(t, methodName, func() (err error) { 51 _, _, err = client.Actions.ListWorkflowJobs(ctx, "\n", "\n", 29679449, opts) 52 return err 53 }) 54 55 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 56 got, resp, err := client.Actions.ListWorkflowJobs(ctx, "o", "r", 29679449, opts) 57 if got != nil { 58 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 59 } 60 return resp, err 61 }) 62 } 63 64 func TestActionsService_ListWorkflowJobs_Filter(t *testing.T) { 65 t.Parallel() 66 client, mux, _ := setup(t) 67 68 mux.HandleFunc("/repos/o/r/actions/runs/29679449/jobs", func(w http.ResponseWriter, r *http.Request) { 69 testMethod(t, r, "GET") 70 testFormValues(t, r, values{"filter": "all", "per_page": "2", "page": "2"}) 71 fmt.Fprint(w, `{"total_count":4,"jobs":[{"id":399444496,"run_id":29679449,"started_at":"2019-01-02T15:04:05Z","completed_at":"2020-01-02T15:04:05Z"},{"id":399444497,"run_id":29679449,"started_at":"2019-01-02T15:04:05Z","completed_at":"2020-01-02T15:04:05Z"}]}`) 72 }) 73 74 opts := &ListWorkflowJobsOptions{Filter: "all", ListOptions: ListOptions{Page: 2, PerPage: 2}} 75 ctx := context.Background() 76 jobs, _, err := client.Actions.ListWorkflowJobs(ctx, "o", "r", 29679449, opts) 77 if err != nil { 78 t.Errorf("Actions.ListWorkflowJobs returned error: %v", err) 79 } 80 81 want := &Jobs{ 82 TotalCount: Ptr(4), 83 Jobs: []*WorkflowJob{ 84 {ID: Ptr(int64(399444496)), RunID: Ptr(int64(29679449)), StartedAt: &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, CompletedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}}, 85 {ID: Ptr(int64(399444497)), RunID: Ptr(int64(29679449)), StartedAt: &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, CompletedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}}, 86 }, 87 } 88 if !cmp.Equal(jobs, want) { 89 t.Errorf("Actions.ListWorkflowJobs returned %+v, want %+v", jobs, want) 90 } 91 } 92 93 func TestActionsService_ListWorkflowJobsAttempt(t *testing.T) { 94 t.Parallel() 95 client, mux, _ := setup(t) 96 97 mux.HandleFunc("/repos/o/r/actions/runs/29679449/attempts/1/jobs", func(w http.ResponseWriter, r *http.Request) { 98 testMethod(t, r, "GET") 99 testFormValues(t, r, values{"per_page": "2", "page": "2"}) 100 fmt.Fprint(w, `{"total_count":4,"jobs":[{"id":399444496,"run_id":29679449,"started_at":"2019-01-02T15:04:05Z","completed_at":"2020-01-02T15:04:05Z","run_attempt":2},{"id":399444497,"run_id":29679449,"started_at":"2019-01-02T15:04:05Z","completed_at":"2020-01-02T15:04:05Z","run_attempt":2}]}`) 101 }) 102 opts := &ListOptions{Page: 2, PerPage: 2} 103 ctx := context.Background() 104 jobs, _, err := client.Actions.ListWorkflowJobsAttempt(ctx, "o", "r", 29679449, 1, opts) 105 if err != nil { 106 t.Errorf("Actions.ListWorkflowJobsAttempt returned error: %v", err) 107 } 108 109 want := &Jobs{ 110 TotalCount: Ptr(4), 111 Jobs: []*WorkflowJob{ 112 { 113 ID: Ptr(int64(399444496)), 114 RunID: Ptr(int64(29679449)), 115 StartedAt: &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, 116 CompletedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}, 117 RunAttempt: Ptr(int64(2)), 118 }, 119 { 120 ID: Ptr(int64(399444497)), 121 RunID: Ptr(int64(29679449)), 122 StartedAt: &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, 123 CompletedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}, 124 RunAttempt: Ptr(int64(2)), 125 }, 126 }, 127 } 128 if !cmp.Equal(jobs, want) { 129 t.Errorf("Actions.ListWorkflowJobsAttempt returned %+v, want %+v", jobs, want) 130 } 131 132 const methodName = "ListWorkflowJobsAttempt" 133 testBadOptions(t, methodName, func() (err error) { 134 _, _, err = client.Actions.ListWorkflowJobsAttempt(ctx, "\n", "\n", 29679449, 1, opts) 135 return err 136 }) 137 138 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 139 got, resp, err := client.Actions.ListWorkflowJobsAttempt(ctx, "o", "r", 29679449, 1, opts) 140 if got != nil { 141 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 142 } 143 return resp, err 144 }) 145 } 146 147 func TestActionsService_GetWorkflowJobByID(t *testing.T) { 148 t.Parallel() 149 client, mux, _ := setup(t) 150 151 mux.HandleFunc("/repos/o/r/actions/jobs/399444496", func(w http.ResponseWriter, r *http.Request) { 152 testMethod(t, r, "GET") 153 fmt.Fprint(w, `{"id":399444496,"started_at":"2019-01-02T15:04:05Z","completed_at":"2020-01-02T15:04:05Z"}`) 154 }) 155 156 ctx := context.Background() 157 job, _, err := client.Actions.GetWorkflowJobByID(ctx, "o", "r", 399444496) 158 if err != nil { 159 t.Errorf("Actions.GetWorkflowJobByID returned error: %v", err) 160 } 161 162 want := &WorkflowJob{ 163 ID: Ptr(int64(399444496)), 164 StartedAt: &Timestamp{time.Date(2019, time.January, 02, 15, 04, 05, 0, time.UTC)}, 165 CompletedAt: &Timestamp{time.Date(2020, time.January, 02, 15, 04, 05, 0, time.UTC)}, 166 } 167 if !cmp.Equal(job, want) { 168 t.Errorf("Actions.GetWorkflowJobByID returned %+v, want %+v", job, want) 169 } 170 171 const methodName = "GetWorkflowJobByID" 172 testBadOptions(t, methodName, func() (err error) { 173 _, _, err = client.Actions.GetWorkflowJobByID(ctx, "\n", "\n", 399444496) 174 return err 175 }) 176 177 testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) { 178 got, resp, err := client.Actions.GetWorkflowJobByID(ctx, "o", "r", 399444496) 179 if got != nil { 180 t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got) 181 } 182 return resp, err 183 }) 184 } 185 186 func TestActionsService_GetWorkflowJobLogs(t *testing.T) { 187 t.Parallel() 188 tcs := []struct { 189 name string 190 respectRateLimits bool 191 }{ 192 { 193 name: "withoutRateLimits", 194 respectRateLimits: false, 195 }, 196 { 197 name: "withRateLimits", 198 respectRateLimits: true, 199 }, 200 } 201 202 for _, tc := range tcs { 203 tc := tc 204 t.Run(tc.name, func(t *testing.T) { 205 t.Parallel() 206 client, mux, _ := setup(t) 207 client.RateLimitRedirectionalEndpoints = tc.respectRateLimits 208 209 mux.HandleFunc("/repos/o/r/actions/jobs/399444496/logs", func(w http.ResponseWriter, r *http.Request) { 210 testMethod(t, r, "GET") 211 http.Redirect(w, r, "http://github.com/a", http.StatusFound) 212 }) 213 214 ctx := context.Background() 215 url, resp, err := client.Actions.GetWorkflowJobLogs(ctx, "o", "r", 399444496, 1) 216 if err != nil { 217 t.Errorf("Actions.GetWorkflowJobLogs returned error: %v", err) 218 } 219 if resp.StatusCode != http.StatusFound { 220 t.Errorf("Actions.GetWorkflowJobLogs returned status: %d, want %d", resp.StatusCode, http.StatusFound) 221 } 222 want := "http://github.com/a" 223 if url.String() != want { 224 t.Errorf("Actions.GetWorkflowJobLogs returned %+v, want %+v", url.String(), want) 225 } 226 227 const methodName = "GetWorkflowJobLogs" 228 testBadOptions(t, methodName, func() (err error) { 229 _, _, err = client.Actions.GetWorkflowJobLogs(ctx, "\n", "\n", 399444496, 1) 230 return err 231 }) 232 233 // Add custom round tripper 234 client.client.Transport = roundTripperFunc(func(r *http.Request) (*http.Response, error) { 235 return nil, errors.New("failed to get workflow logs") 236 }) 237 // propagate custom round tripper to client without CheckRedirect 238 client.initialize() 239 testBadOptions(t, methodName, func() (err error) { 240 _, _, err = client.Actions.GetWorkflowJobLogs(ctx, "o", "r", 399444496, 1) 241 return err 242 }) 243 }) 244 } 245 } 246 247 func TestActionsService_GetWorkflowJobLogs_StatusMovedPermanently_dontFollowRedirects(t *testing.T) { 248 t.Parallel() 249 tcs := []struct { 250 name string 251 respectRateLimits bool 252 }{ 253 { 254 name: "withoutRateLimits", 255 respectRateLimits: false, 256 }, 257 { 258 name: "withRateLimits", 259 respectRateLimits: true, 260 }, 261 } 262 263 for _, tc := range tcs { 264 tc := tc 265 t.Run(tc.name, func(t *testing.T) { 266 t.Parallel() 267 client, mux, _ := setup(t) 268 client.RateLimitRedirectionalEndpoints = tc.respectRateLimits 269 270 mux.HandleFunc("/repos/o/r/actions/jobs/399444496/logs", func(w http.ResponseWriter, r *http.Request) { 271 testMethod(t, r, "GET") 272 http.Redirect(w, r, "http://github.com/a", http.StatusMovedPermanently) 273 }) 274 275 ctx := context.Background() 276 _, resp, _ := client.Actions.GetWorkflowJobLogs(ctx, "o", "r", 399444496, 0) 277 if resp.StatusCode != http.StatusMovedPermanently { 278 t.Errorf("Actions.GetWorkflowJobLogs returned status: %d, want %d", resp.StatusCode, http.StatusMovedPermanently) 279 } 280 }) 281 } 282 } 283 284 func TestActionsService_GetWorkflowJobLogs_StatusMovedPermanently_followRedirects(t *testing.T) { 285 t.Parallel() 286 tcs := []struct { 287 name string 288 respectRateLimits bool 289 }{ 290 { 291 name: "withoutRateLimits", 292 respectRateLimits: false, 293 }, 294 { 295 name: "withRateLimits", 296 respectRateLimits: true, 297 }, 298 } 299 300 for _, tc := range tcs { 301 tc := tc 302 t.Run(tc.name, func(t *testing.T) { 303 t.Parallel() 304 client, mux, serverURL := setup(t) 305 client.RateLimitRedirectionalEndpoints = tc.respectRateLimits 306 307 // Mock a redirect link, which leads to an archive link 308 mux.HandleFunc("/repos/o/r/actions/jobs/399444496/logs", func(w http.ResponseWriter, r *http.Request) { 309 testMethod(t, r, "GET") 310 redirectURL, _ := url.Parse(serverURL + baseURLPath + "/redirect") 311 http.Redirect(w, r, redirectURL.String(), http.StatusMovedPermanently) 312 }) 313 314 mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) { 315 testMethod(t, r, "GET") 316 http.Redirect(w, r, "http://github.com/a", http.StatusFound) 317 }) 318 319 ctx := context.Background() 320 url, resp, err := client.Actions.GetWorkflowJobLogs(ctx, "o", "r", 399444496, 1) 321 if err != nil { 322 t.Errorf("Actions.GetWorkflowJobLogs returned error: %v", err) 323 } 324 325 if resp.StatusCode != http.StatusFound { 326 t.Errorf("Actions.GetWorkflowJobLogs returned status: %d, want %d", resp.StatusCode, http.StatusFound) 327 } 328 329 want := "http://github.com/a" 330 if url.String() != want { 331 t.Errorf("Actions.GetWorkflowJobLogs returned %+v, want %+v", url.String(), want) 332 } 333 }) 334 } 335 } 336 337 func TestActionsService_GetWorkflowJobLogs_unexpectedCode(t *testing.T) { 338 t.Parallel() 339 tcs := []struct { 340 name string 341 respectRateLimits bool 342 }{ 343 { 344 name: "withoutRateLimits", 345 respectRateLimits: false, 346 }, 347 { 348 name: "withRateLimits", 349 respectRateLimits: true, 350 }, 351 } 352 353 for _, tc := range tcs { 354 tc := tc 355 t.Run(tc.name, func(t *testing.T) { 356 t.Parallel() 357 client, mux, serverURL := setup(t) 358 client.RateLimitRedirectionalEndpoints = tc.respectRateLimits 359 360 // Mock a redirect link, which leads to an archive link 361 mux.HandleFunc("/repos/o/r/actions/jobs/399444496/logs", func(w http.ResponseWriter, r *http.Request) { 362 testMethod(t, r, "GET") 363 redirectURL, _ := url.Parse(serverURL + baseURLPath + "/redirect") 364 http.Redirect(w, r, redirectURL.String(), http.StatusMovedPermanently) 365 }) 366 367 mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) { 368 testMethod(t, r, "GET") 369 w.WriteHeader(http.StatusNoContent) 370 }) 371 372 ctx := context.Background() 373 url, resp, err := client.Actions.GetWorkflowJobLogs(ctx, "o", "r", 399444496, 1) 374 if err == nil { 375 t.Fatalf("Actions.GetWorkflowJobLogs should return error on unexpected code") 376 } 377 if !strings.Contains(err.Error(), "unexpected status code") { 378 t.Error("Actions.GetWorkflowJobLogs should return unexpected status code") 379 } 380 if got, want := resp.Response.StatusCode, http.StatusNoContent; got != want { 381 t.Errorf("Actions.GetWorkflowJobLogs return status %d, want %d", got, want) 382 } 383 if url != nil { 384 t.Errorf("Actions.GetWorkflowJobLogs return %+v, want nil", url) 385 } 386 }) 387 } 388 } 389 390 func TestTaskStep_Marshal(t *testing.T) { 391 t.Parallel() 392 testJSONMarshal(t, &TaskStep{}, "{}") 393 394 u := &TaskStep{ 395 Name: Ptr("n"), 396 Status: Ptr("s"), 397 Conclusion: Ptr("c"), 398 Number: Ptr(int64(1)), 399 StartedAt: &Timestamp{referenceTime}, 400 CompletedAt: &Timestamp{referenceTime}, 401 } 402 403 want := `{ 404 "name": "n", 405 "status": "s", 406 "conclusion": "c", 407 "number": 1, 408 "started_at": ` + referenceTimeStr + `, 409 "completed_at": ` + referenceTimeStr + ` 410 }` 411 412 testJSONMarshal(t, u, want) 413 } 414 415 func TestWorkflowJob_Marshal(t *testing.T) { 416 t.Parallel() 417 testJSONMarshal(t, &WorkflowJob{}, "{}") 418 419 u := &WorkflowJob{ 420 ID: Ptr(int64(1)), 421 RunID: Ptr(int64(1)), 422 RunURL: Ptr("r"), 423 NodeID: Ptr("n"), 424 HeadBranch: Ptr("b"), 425 HeadSHA: Ptr("h"), 426 URL: Ptr("u"), 427 HTMLURL: Ptr("h"), 428 Status: Ptr("s"), 429 Conclusion: Ptr("c"), 430 CreatedAt: &Timestamp{referenceTime}, 431 StartedAt: &Timestamp{referenceTime}, 432 CompletedAt: &Timestamp{referenceTime}, 433 Name: Ptr("n"), 434 Steps: []*TaskStep{ 435 { 436 Name: Ptr("n"), 437 Status: Ptr("s"), 438 Conclusion: Ptr("c"), 439 Number: Ptr(int64(1)), 440 StartedAt: &Timestamp{referenceTime}, 441 CompletedAt: &Timestamp{referenceTime}, 442 }, 443 }, 444 CheckRunURL: Ptr("c"), 445 WorkflowName: Ptr("w"), 446 } 447 448 want := `{ 449 "id": 1, 450 "run_id": 1, 451 "run_url": "r", 452 "node_id": "n", 453 "head_branch": "b", 454 "head_sha": "h", 455 "url": "u", 456 "html_url": "h", 457 "status": "s", 458 "conclusion": "c", 459 "created_at": ` + referenceTimeStr + `, 460 "started_at": ` + referenceTimeStr + `, 461 "completed_at": ` + referenceTimeStr + `, 462 "name": "n", 463 "steps": [{ 464 "name": "n", 465 "status": "s", 466 "conclusion": "c", 467 "number": 1, 468 "started_at": ` + referenceTimeStr + `, 469 "completed_at": ` + referenceTimeStr + ` 470 }], 471 "check_run_url": "c", 472 "workflow_name": "w" 473 }` 474 475 testJSONMarshal(t, u, want) 476 } 477 478 func TestJobs_Marshal(t *testing.T) { 479 t.Parallel() 480 testJSONMarshal(t, &Jobs{}, "{}") 481 482 u := &Jobs{ 483 TotalCount: Ptr(1), 484 Jobs: []*WorkflowJob{ 485 { 486 ID: Ptr(int64(1)), 487 RunID: Ptr(int64(1)), 488 RunURL: Ptr("r"), 489 NodeID: Ptr("n"), 490 HeadBranch: Ptr("b"), 491 HeadSHA: Ptr("h"), 492 URL: Ptr("u"), 493 HTMLURL: Ptr("h"), 494 Status: Ptr("s"), 495 Conclusion: Ptr("c"), 496 CreatedAt: &Timestamp{referenceTime}, 497 StartedAt: &Timestamp{referenceTime}, 498 CompletedAt: &Timestamp{referenceTime}, 499 Name: Ptr("n"), 500 Steps: []*TaskStep{ 501 { 502 Name: Ptr("n"), 503 Status: Ptr("s"), 504 Conclusion: Ptr("c"), 505 Number: Ptr(int64(1)), 506 StartedAt: &Timestamp{referenceTime}, 507 CompletedAt: &Timestamp{referenceTime}, 508 }, 509 }, 510 CheckRunURL: Ptr("c"), 511 RunAttempt: Ptr(int64(2)), 512 WorkflowName: Ptr("w"), 513 }, 514 }, 515 } 516 517 want := `{ 518 "total_count": 1, 519 "jobs": [{ 520 "id": 1, 521 "run_id": 1, 522 "run_url": "r", 523 "node_id": "n", 524 "head_branch": "b", 525 "head_sha": "h", 526 "url": "u", 527 "html_url": "h", 528 "status": "s", 529 "conclusion": "c", 530 "created_at": ` + referenceTimeStr + `, 531 "started_at": ` + referenceTimeStr + `, 532 "completed_at": ` + referenceTimeStr + `, 533 "name": "n", 534 "steps": [{ 535 "name": "n", 536 "status": "s", 537 "conclusion": "c", 538 "number": 1, 539 "started_at": ` + referenceTimeStr + `, 540 "completed_at": ` + referenceTimeStr + ` 541 }], 542 "check_run_url": "c", 543 "run_attempt": 2, 544 "workflow_name": "w" 545 }] 546 }` 547 548 testJSONMarshal(t, u, want) 549 }