github.com/google/go-github/v74@v74.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 t.Run(tc.name, func(t *testing.T) { 204 t.Parallel() 205 client, mux, _ := setup(t) 206 client.RateLimitRedirectionalEndpoints = tc.respectRateLimits 207 208 mux.HandleFunc("/repos/o/r/actions/jobs/399444496/logs", func(w http.ResponseWriter, r *http.Request) { 209 testMethod(t, r, "GET") 210 http.Redirect(w, r, "http://github.com/a", http.StatusFound) 211 }) 212 213 ctx := context.Background() 214 url, resp, err := client.Actions.GetWorkflowJobLogs(ctx, "o", "r", 399444496, 1) 215 if err != nil { 216 t.Errorf("Actions.GetWorkflowJobLogs returned error: %v", err) 217 } 218 if resp.StatusCode != http.StatusFound { 219 t.Errorf("Actions.GetWorkflowJobLogs returned status: %d, want %d", resp.StatusCode, http.StatusFound) 220 } 221 want := "http://github.com/a" 222 if url.String() != want { 223 t.Errorf("Actions.GetWorkflowJobLogs returned %+v, want %+v", url.String(), want) 224 } 225 226 const methodName = "GetWorkflowJobLogs" 227 testBadOptions(t, methodName, func() (err error) { 228 _, _, err = client.Actions.GetWorkflowJobLogs(ctx, "\n", "\n", 399444496, 1) 229 return err 230 }) 231 232 // Add custom round tripper 233 client.client.Transport = roundTripperFunc(func(*http.Request) (*http.Response, error) { 234 return nil, errors.New("failed to get workflow logs") 235 }) 236 // propagate custom round tripper to client without CheckRedirect 237 client.initialize() 238 testBadOptions(t, methodName, func() (err error) { 239 _, _, err = client.Actions.GetWorkflowJobLogs(ctx, "o", "r", 399444496, 1) 240 return err 241 }) 242 }) 243 } 244 } 245 246 func TestActionsService_GetWorkflowJobLogs_StatusMovedPermanently_dontFollowRedirects(t *testing.T) { 247 t.Parallel() 248 tcs := []struct { 249 name string 250 respectRateLimits bool 251 }{ 252 { 253 name: "withoutRateLimits", 254 respectRateLimits: false, 255 }, 256 { 257 name: "withRateLimits", 258 respectRateLimits: true, 259 }, 260 } 261 262 for _, tc := range tcs { 263 t.Run(tc.name, func(t *testing.T) { 264 t.Parallel() 265 client, mux, _ := setup(t) 266 client.RateLimitRedirectionalEndpoints = tc.respectRateLimits 267 268 mux.HandleFunc("/repos/o/r/actions/jobs/399444496/logs", func(w http.ResponseWriter, r *http.Request) { 269 testMethod(t, r, "GET") 270 http.Redirect(w, r, "http://github.com/a", http.StatusMovedPermanently) 271 }) 272 273 ctx := context.Background() 274 _, resp, _ := client.Actions.GetWorkflowJobLogs(ctx, "o", "r", 399444496, 0) 275 if resp.StatusCode != http.StatusMovedPermanently { 276 t.Errorf("Actions.GetWorkflowJobLogs returned status: %d, want %d", resp.StatusCode, http.StatusMovedPermanently) 277 } 278 }) 279 } 280 } 281 282 func TestActionsService_GetWorkflowJobLogs_StatusMovedPermanently_followRedirects(t *testing.T) { 283 t.Parallel() 284 tcs := []struct { 285 name string 286 respectRateLimits bool 287 }{ 288 { 289 name: "withoutRateLimits", 290 respectRateLimits: false, 291 }, 292 { 293 name: "withRateLimits", 294 respectRateLimits: true, 295 }, 296 } 297 298 for _, tc := range tcs { 299 t.Run(tc.name, func(t *testing.T) { 300 t.Parallel() 301 client, mux, serverURL := setup(t) 302 client.RateLimitRedirectionalEndpoints = tc.respectRateLimits 303 304 // Mock a redirect link, which leads to an archive link 305 mux.HandleFunc("/repos/o/r/actions/jobs/399444496/logs", func(w http.ResponseWriter, r *http.Request) { 306 testMethod(t, r, "GET") 307 redirectURL, _ := url.Parse(serverURL + baseURLPath + "/redirect") 308 http.Redirect(w, r, redirectURL.String(), http.StatusMovedPermanently) 309 }) 310 311 mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) { 312 testMethod(t, r, "GET") 313 http.Redirect(w, r, "http://github.com/a", http.StatusFound) 314 }) 315 316 ctx := context.Background() 317 url, resp, err := client.Actions.GetWorkflowJobLogs(ctx, "o", "r", 399444496, 1) 318 if err != nil { 319 t.Errorf("Actions.GetWorkflowJobLogs returned error: %v", err) 320 } 321 322 if resp.StatusCode != http.StatusFound { 323 t.Errorf("Actions.GetWorkflowJobLogs returned status: %d, want %d", resp.StatusCode, http.StatusFound) 324 } 325 326 want := "http://github.com/a" 327 if url.String() != want { 328 t.Errorf("Actions.GetWorkflowJobLogs returned %+v, want %+v", url.String(), want) 329 } 330 }) 331 } 332 } 333 334 func TestActionsService_GetWorkflowJobLogs_unexpectedCode(t *testing.T) { 335 t.Parallel() 336 tcs := []struct { 337 name string 338 respectRateLimits bool 339 }{ 340 { 341 name: "withoutRateLimits", 342 respectRateLimits: false, 343 }, 344 { 345 name: "withRateLimits", 346 respectRateLimits: true, 347 }, 348 } 349 350 for _, tc := range tcs { 351 t.Run(tc.name, func(t *testing.T) { 352 t.Parallel() 353 client, mux, serverURL := setup(t) 354 client.RateLimitRedirectionalEndpoints = tc.respectRateLimits 355 356 // Mock a redirect link, which leads to an archive link 357 mux.HandleFunc("/repos/o/r/actions/jobs/399444496/logs", func(w http.ResponseWriter, r *http.Request) { 358 testMethod(t, r, "GET") 359 redirectURL, _ := url.Parse(serverURL + baseURLPath + "/redirect") 360 http.Redirect(w, r, redirectURL.String(), http.StatusMovedPermanently) 361 }) 362 363 mux.HandleFunc("/redirect", func(w http.ResponseWriter, r *http.Request) { 364 testMethod(t, r, "GET") 365 w.WriteHeader(http.StatusNoContent) 366 }) 367 368 ctx := context.Background() 369 url, resp, err := client.Actions.GetWorkflowJobLogs(ctx, "o", "r", 399444496, 1) 370 if err == nil { 371 t.Fatal("Actions.GetWorkflowJobLogs should return error on unexpected code") 372 } 373 if !strings.Contains(err.Error(), "unexpected status code") { 374 t.Error("Actions.GetWorkflowJobLogs should return unexpected status code") 375 } 376 if got, want := resp.Response.StatusCode, http.StatusNoContent; got != want { 377 t.Errorf("Actions.GetWorkflowJobLogs return status %d, want %d", got, want) 378 } 379 if url != nil { 380 t.Errorf("Actions.GetWorkflowJobLogs return %+v, want nil", url) 381 } 382 }) 383 } 384 } 385 386 func TestTaskStep_Marshal(t *testing.T) { 387 t.Parallel() 388 testJSONMarshal(t, &TaskStep{}, "{}") 389 390 u := &TaskStep{ 391 Name: Ptr("n"), 392 Status: Ptr("s"), 393 Conclusion: Ptr("c"), 394 Number: Ptr(int64(1)), 395 StartedAt: &Timestamp{referenceTime}, 396 CompletedAt: &Timestamp{referenceTime}, 397 } 398 399 want := `{ 400 "name": "n", 401 "status": "s", 402 "conclusion": "c", 403 "number": 1, 404 "started_at": ` + referenceTimeStr + `, 405 "completed_at": ` + referenceTimeStr + ` 406 }` 407 408 testJSONMarshal(t, u, want) 409 } 410 411 func TestWorkflowJob_Marshal(t *testing.T) { 412 t.Parallel() 413 testJSONMarshal(t, &WorkflowJob{}, "{}") 414 415 u := &WorkflowJob{ 416 ID: Ptr(int64(1)), 417 RunID: Ptr(int64(1)), 418 RunURL: Ptr("r"), 419 NodeID: Ptr("n"), 420 HeadBranch: Ptr("b"), 421 HeadSHA: Ptr("h"), 422 URL: Ptr("u"), 423 HTMLURL: Ptr("h"), 424 Status: Ptr("s"), 425 Conclusion: Ptr("c"), 426 CreatedAt: &Timestamp{referenceTime}, 427 StartedAt: &Timestamp{referenceTime}, 428 CompletedAt: &Timestamp{referenceTime}, 429 Name: Ptr("n"), 430 Steps: []*TaskStep{ 431 { 432 Name: Ptr("n"), 433 Status: Ptr("s"), 434 Conclusion: Ptr("c"), 435 Number: Ptr(int64(1)), 436 StartedAt: &Timestamp{referenceTime}, 437 CompletedAt: &Timestamp{referenceTime}, 438 }, 439 }, 440 CheckRunURL: Ptr("c"), 441 WorkflowName: Ptr("w"), 442 } 443 444 want := `{ 445 "id": 1, 446 "run_id": 1, 447 "run_url": "r", 448 "node_id": "n", 449 "head_branch": "b", 450 "head_sha": "h", 451 "url": "u", 452 "html_url": "h", 453 "status": "s", 454 "conclusion": "c", 455 "created_at": ` + referenceTimeStr + `, 456 "started_at": ` + referenceTimeStr + `, 457 "completed_at": ` + referenceTimeStr + `, 458 "name": "n", 459 "steps": [{ 460 "name": "n", 461 "status": "s", 462 "conclusion": "c", 463 "number": 1, 464 "started_at": ` + referenceTimeStr + `, 465 "completed_at": ` + referenceTimeStr + ` 466 }], 467 "check_run_url": "c", 468 "workflow_name": "w" 469 }` 470 471 testJSONMarshal(t, u, want) 472 } 473 474 func TestJobs_Marshal(t *testing.T) { 475 t.Parallel() 476 testJSONMarshal(t, &Jobs{}, "{}") 477 478 u := &Jobs{ 479 TotalCount: Ptr(1), 480 Jobs: []*WorkflowJob{ 481 { 482 ID: Ptr(int64(1)), 483 RunID: Ptr(int64(1)), 484 RunURL: Ptr("r"), 485 NodeID: Ptr("n"), 486 HeadBranch: Ptr("b"), 487 HeadSHA: Ptr("h"), 488 URL: Ptr("u"), 489 HTMLURL: Ptr("h"), 490 Status: Ptr("s"), 491 Conclusion: Ptr("c"), 492 CreatedAt: &Timestamp{referenceTime}, 493 StartedAt: &Timestamp{referenceTime}, 494 CompletedAt: &Timestamp{referenceTime}, 495 Name: Ptr("n"), 496 Steps: []*TaskStep{ 497 { 498 Name: Ptr("n"), 499 Status: Ptr("s"), 500 Conclusion: Ptr("c"), 501 Number: Ptr(int64(1)), 502 StartedAt: &Timestamp{referenceTime}, 503 CompletedAt: &Timestamp{referenceTime}, 504 }, 505 }, 506 CheckRunURL: Ptr("c"), 507 RunAttempt: Ptr(int64(2)), 508 WorkflowName: Ptr("w"), 509 }, 510 }, 511 } 512 513 want := `{ 514 "total_count": 1, 515 "jobs": [{ 516 "id": 1, 517 "run_id": 1, 518 "run_url": "r", 519 "node_id": "n", 520 "head_branch": "b", 521 "head_sha": "h", 522 "url": "u", 523 "html_url": "h", 524 "status": "s", 525 "conclusion": "c", 526 "created_at": ` + referenceTimeStr + `, 527 "started_at": ` + referenceTimeStr + `, 528 "completed_at": ` + referenceTimeStr + `, 529 "name": "n", 530 "steps": [{ 531 "name": "n", 532 "status": "s", 533 "conclusion": "c", 534 "number": 1, 535 "started_at": ` + referenceTimeStr + `, 536 "completed_at": ` + referenceTimeStr + ` 537 }], 538 "check_run_url": "c", 539 "run_attempt": 2, 540 "workflow_name": "w" 541 }] 542 }` 543 544 testJSONMarshal(t, u, want) 545 }