github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/fly/integration/builds_test.go (about) 1 package integration_test 2 3 import ( 4 "fmt" 5 "net/http" 6 "os/exec" 7 "strings" 8 "time" 9 10 "github.com/pf-qiu/concourse/v6/atc" 11 "github.com/pf-qiu/concourse/v6/fly/ui" 12 "github.com/fatih/color" 13 "github.com/onsi/gomega/gbytes" 14 "github.com/onsi/gomega/gexec" 15 "github.com/onsi/gomega/ghttp" 16 17 . "github.com/onsi/ginkgo" 18 . "github.com/onsi/gomega" 19 ) 20 21 const timeDateLayout = "2006-01-02@15:04:05-0700" 22 const timeLayout = "2006-01-02 15:04:05" 23 24 var _ = Describe("Fly CLI", func() { 25 var ( 26 runningBuildStartTime time.Time 27 pendingBuildStartTime time.Time 28 pendingBuildEndTime time.Time 29 erroredBuildStartTime time.Time 30 erroredBuildEndTime time.Time 31 succeededBuildStartTime time.Time 32 succeededBuildEndTime time.Time 33 zeroTime time.Time 34 abortedBuildEndTime time.Time 35 ) 36 37 BeforeEach(func() { 38 runningBuildStartTime = time.Date(2015, time.November, 21, 10, 30, 15, 0, time.UTC) 39 pendingBuildStartTime = time.Date(2015, time.December, 1, 1, 20, 15, 0, time.UTC) 40 pendingBuildEndTime = time.Date(2015, time.December, 1, 2, 35, 15, 0, time.UTC) 41 erroredBuildStartTime = time.Date(2015, time.July, 4, 12, 00, 15, 0, time.UTC) 42 erroredBuildEndTime = time.Date(2015, time.July, 4, 14, 45, 15, 0, time.UTC) 43 succeededBuildStartTime = time.Date(2015, time.December, 1, 1, 20, 15, 0, time.UTC) 44 succeededBuildEndTime = time.Date(2015, time.December, 1, 2, 35, 15, 0, time.UTC) 45 zeroTime = time.Unix(0, 0) 46 abortedBuildEndTime = time.Date(2015, time.July, 4, 14, 45, 15, 0, time.UTC) 47 }) 48 49 Describe("builds", func() { 50 var ( 51 session *gexec.Session 52 cmdArgs []string 53 expectedURL string 54 queryParams []string 55 returnedStatusCode int 56 returnedBuilds []atc.Build 57 expectedHeaders ui.TableRow 58 ) 59 60 BeforeEach(func() { 61 cmdArgs = []string{"-t", targetName, "builds"} 62 63 expectedHeaders = ui.TableRow{ 64 {Contents: "id", Color: color.New(color.Bold)}, 65 {Contents: "name", Color: color.New(color.Bold)}, 66 {Contents: "status", Color: color.New(color.Bold)}, 67 {Contents: "start", Color: color.New(color.Bold)}, 68 {Contents: "end", Color: color.New(color.Bold)}, 69 {Contents: "duration", Color: color.New(color.Bold)}, 70 {Contents: "team", Color: color.New(color.Bold)}, 71 } 72 }) 73 74 JustBeforeEach(func() { 75 var err error 76 77 atcServer.AppendHandlers( 78 ghttp.CombineHandlers( 79 ghttp.VerifyRequest("GET", expectedURL, strings.Join(queryParams, "&")), 80 ghttp.RespondWithJSONEncoded(returnedStatusCode, returnedBuilds), 81 ), 82 ) 83 cmd := exec.Command(flyPath, cmdArgs...) 84 session, err = gexec.Start(cmd, nil, nil) 85 Expect(err).ToNot(HaveOccurred()) 86 }) 87 88 Context("with no arguments", func() { 89 BeforeEach(func() { 90 expectedURL = "/api/v1/builds" 91 queryParams = []string{"limit=50"} 92 93 returnedStatusCode = http.StatusOK 94 returnedBuilds = []atc.Build{ 95 { 96 ID: 2, 97 PipelineID: 1, 98 PipelineName: "some-pipeline", 99 PipelineInstanceVars: atc.InstanceVars{"branch": "master"}, 100 JobName: "some-job", 101 Name: "62", 102 Status: "started", 103 StartTime: runningBuildStartTime.Unix(), 104 EndTime: 0, 105 TeamName: "team1", 106 }, 107 { 108 ID: 999, 109 TeamName: "some-team", 110 PipelineID: 1, 111 PipelineName: "some-pipeline", 112 ResourceName: "some-resource", 113 Name: "check", 114 Status: "succeeded", 115 StartTime: succeededBuildStartTime.Unix(), 116 EndTime: succeededBuildEndTime.Unix(), 117 }, 118 { 119 ID: 3, 120 PipelineID: 2, 121 PipelineName: "some-other-pipeline", 122 JobName: "some-other-job", 123 Name: "63", 124 Status: "pending", 125 StartTime: pendingBuildStartTime.Unix(), 126 EndTime: pendingBuildEndTime.Unix(), 127 TeamName: "team1", 128 }, 129 { 130 ID: 1000001, 131 Name: "one-off", 132 Status: "errored", 133 StartTime: erroredBuildStartTime.Unix(), 134 EndTime: erroredBuildEndTime.Unix(), 135 TeamName: "team1", 136 }, 137 { 138 ID: 1002, 139 Name: "one-off", 140 Status: "aborted", 141 StartTime: zeroTime.Unix(), 142 EndTime: abortedBuildEndTime.Unix(), 143 TeamName: "team1", 144 }, 145 { 146 ID: 39, 147 Name: "one-off", 148 Status: "pending", 149 StartTime: 0, 150 EndTime: 0, 151 TeamName: "team1", 152 }, 153 } 154 }) 155 156 Context("when --json is given", func() { 157 BeforeEach(func() { 158 cmdArgs = append(cmdArgs, "--json") 159 }) 160 161 It("prints response in json as stdout", func() { 162 Eventually(session).Should(gexec.Exit(0)) 163 Expect(session.Out.Contents()).To(MatchJSON(`[ 164 { 165 "id": 2, 166 "team_name": "team1", 167 "name": "62", 168 "status": "started", 169 "job_name": "some-job", 170 "api_url": "", 171 "pipeline_id": 1, 172 "pipeline_name": "some-pipeline", 173 "pipeline_instance_vars": { 174 "branch": "master" 175 }, 176 "start_time": 1448101815 177 }, 178 { 179 "id": 999, 180 "team_name": "some-team", 181 "name": "check", 182 "status": "succeeded", 183 "resource_name": "some-resource", 184 "api_url": "", 185 "pipeline_id": 1, 186 "pipeline_name": "some-pipeline", 187 "start_time": 1448932815, 188 "end_time": 1448937315 189 }, 190 { 191 "id": 3, 192 "team_name": "team1", 193 "name": "63", 194 "status": "pending", 195 "job_name": "some-other-job", 196 "api_url": "", 197 "pipeline_id": 2, 198 "pipeline_name": "some-other-pipeline", 199 "start_time": 1448932815, 200 "end_time": 1448937315 201 }, 202 { 203 "id": 1000001, 204 "team_name": "team1", 205 "name": "one-off", 206 "status": "errored", 207 "api_url": "", 208 "start_time": 1436011215, 209 "end_time": 1436021115 210 }, 211 { 212 "id": 1002, 213 "team_name": "team1", 214 "name": "one-off", 215 "status": "aborted", 216 "api_url": "", 217 "end_time": 1436021115 218 }, 219 { 220 "id": 39, 221 "team_name": "team1", 222 "name": "one-off", 223 "status": "pending", 224 "api_url": "" 225 } 226 ]`)) 227 }) 228 }) 229 230 It("returns all the builds", func() { 231 runningBuildDuration := time.Since(runningBuildStartTime) 232 233 Eventually(session.Out).Should(PrintTable(ui.Table{ 234 Headers: expectedHeaders, 235 Data: []ui.TableRow{ 236 { 237 {Contents: "2"}, 238 {Contents: "some-pipeline/branch:master/some-job/62"}, 239 {Contents: "started"}, 240 {Contents: runningBuildStartTime.Local().Format(timeDateLayout)}, 241 {Contents: "n/a"}, 242 { 243 Contents: TableDurationWithDelta{ 244 Duration: runningBuildDuration, 245 Delta: 20 * time.Second, 246 Suffix: "+", 247 }.String(), 248 }, 249 {Contents: "team1"}, 250 }, 251 { 252 {Contents: "999"}, 253 {Contents: "some-pipeline/some-resource/check"}, 254 {Contents: "succeeded"}, 255 {Contents: succeededBuildStartTime.Local().Format(timeDateLayout)}, 256 {Contents: succeededBuildEndTime.Local().Format(timeDateLayout)}, 257 {Contents: "1h15m0s"}, 258 {Contents: "some-team"}, 259 }, 260 { 261 {Contents: "3"}, 262 {Contents: "some-other-pipeline/some-other-job/63"}, 263 {Contents: "pending"}, 264 {Contents: pendingBuildStartTime.Local().Format(timeDateLayout)}, 265 {Contents: pendingBuildEndTime.Local().Format(timeDateLayout)}, 266 {Contents: "1h15m0s"}, 267 {Contents: "team1"}, 268 }, 269 { 270 {Contents: "1000001"}, 271 {Contents: "one-off"}, 272 {Contents: "errored"}, 273 {Contents: erroredBuildStartTime.Local().Format(timeDateLayout)}, 274 {Contents: erroredBuildEndTime.Local().Format(timeDateLayout)}, 275 {Contents: "2h45m0s"}, 276 {Contents: "team1"}, 277 }, 278 { 279 {Contents: "1002"}, 280 {Contents: "one-off"}, 281 {Contents: "aborted"}, 282 {Contents: "n/a"}, 283 {Contents: abortedBuildEndTime.Local().Format(timeDateLayout)}, 284 {Contents: "n/a"}, 285 {Contents: "team1"}, 286 }, 287 { 288 {Contents: "39"}, 289 {Contents: "one-off"}, 290 {Contents: "pending"}, 291 {Contents: "n/a"}, 292 {Contents: "n/a"}, 293 {Contents: "n/a"}, 294 {Contents: "team1"}, 295 }, 296 }, 297 })) 298 299 Eventually(session).Should(gexec.Exit(0)) 300 }) 301 302 Context("when the api returns an error", func() { 303 BeforeEach(func() { 304 returnedStatusCode = http.StatusInternalServerError 305 }) 306 307 It("writes an error message to stderr", func() { 308 Eventually(session.Err).Should(gbytes.Say("Unexpected Response")) 309 Eventually(session).Should(gexec.Exit(1)) 310 }) 311 }) 312 }) 313 314 Context("when validating parameters", func() { 315 Context("when specifying --all-teams and --team", func() { 316 BeforeEach(func() { 317 cmdArgs = append(cmdArgs, "--all-teams", 318 "--team", "blah") 319 }) 320 321 It("instructs the user to use either --all-teams or --team", func() { 322 Eventually(session.Err).Should(gbytes.Say("Cannot specify both --all-teams and --team")) 323 Eventually(session).Should(gexec.Exit(1)) 324 325 }) 326 }) 327 328 Context("when specifying --all-teams and --current-team", func() { 329 BeforeEach(func() { 330 cmdArgs = append(cmdArgs, "--all-teams", 331 "--current-team") 332 }) 333 334 It("instructs the user to not mix them together", func() { 335 Eventually(session.Err).Should(gbytes.Say("Cannot specify both --all-teams and --current-team")) 336 Eventually(session).Should(gexec.Exit(1)) 337 338 }) 339 }) 340 341 Context("when specifying --pipeline and --job", func() { 342 BeforeEach(func() { 343 cmdArgs = append(cmdArgs, "-j", "some-pipeline/some-job", 344 "-p", "some-other-pipeline") 345 }) 346 347 It("instructs the user to not mix them together", func() { 348 Eventually(session.Err).Should(gbytes.Say("Cannot specify both --pipeline and --job")) 349 Eventually(session).Should(gexec.Exit(1)) 350 }) 351 }) 352 }) 353 354 Context("when passing the limit argument", func() { 355 BeforeEach(func() { 356 cmdArgs = append(cmdArgs, "-c") 357 cmdArgs = append(cmdArgs, "1") 358 359 expectedURL = "/api/v1/builds" 360 queryParams = []string{"limit=1"} 361 362 returnedStatusCode = http.StatusOK 363 returnedBuilds = []atc.Build{ 364 { 365 ID: 39, 366 PipelineName: "", 367 JobName: "", 368 Name: "one-off", 369 Status: "pending", 370 StartTime: 0, 371 EndTime: 0, 372 }, 373 } 374 }) 375 376 It("limits the number of returned builds", func() { 377 Eventually(session.Out).Should(PrintTable(ui.Table{ 378 Headers: expectedHeaders, 379 Data: []ui.TableRow{ 380 { 381 {Contents: "39"}, 382 {Contents: "one-off"}, 383 {Contents: "pending"}, 384 {Contents: "n/a"}, 385 {Contents: "n/a"}, 386 {Contents: "n/a"}, 387 }, 388 }, 389 })) 390 391 Eventually(session.Out).ShouldNot(PrintTable(ui.Table{ 392 Data: []ui.TableRow{ 393 { 394 {Contents: "80"}, 395 {Contents: "one-off"}, 396 {Contents: "pending"}, 397 {Contents: "n/a"}, 398 {Contents: "n/a"}, 399 {Contents: "n/a"}, 400 }, 401 }, 402 })) 403 404 Eventually(session).Should(gexec.Exit(0)) 405 }) 406 }) 407 408 Context("when passing the job argument", func() { 409 BeforeEach(func() { 410 cmdArgs = append(cmdArgs, "-j") 411 cmdArgs = append(cmdArgs, "some-pipeline/some-job") 412 413 expectedURL = "/api/v1/teams/main/pipelines/some-pipeline/jobs/some-job/builds" 414 queryParams = []string{"limit=50"} 415 returnedStatusCode = http.StatusOK 416 returnedBuilds = []atc.Build{ 417 { 418 ID: 3, 419 PipelineName: "some-pipeline", 420 JobName: "some-job", 421 Name: "63", 422 Status: "succeeded", 423 StartTime: succeededBuildStartTime.Unix(), 424 EndTime: succeededBuildEndTime.Unix(), 425 }, 426 } 427 }) 428 429 It("returns the builds correctly", func() { 430 Eventually(session.Out).Should(PrintTable(ui.Table{ 431 Headers: expectedHeaders, 432 Data: []ui.TableRow{ 433 { 434 {Contents: "3"}, 435 {Contents: "some-pipeline/some-job/63"}, 436 {Contents: "succeeded"}, 437 {Contents: succeededBuildStartTime.Local().Format(timeDateLayout)}, 438 {Contents: succeededBuildEndTime.Local().Format(timeDateLayout)}, 439 {Contents: "1h15m0s"}, 440 }, 441 }, 442 })) 443 Eventually(session).Should(gexec.Exit(0)) 444 }) 445 446 Context("when the api returns an error", func() { 447 BeforeEach(func() { 448 returnedStatusCode = http.StatusInternalServerError 449 }) 450 451 It("writes an error message to stderr", func() { 452 Eventually(session.Err).Should(gbytes.Say("Unexpected Response")) 453 Eventually(session).Should(gexec.Exit(1)) 454 }) 455 }) 456 457 Context("when the api returns a not found", func() { 458 BeforeEach(func() { 459 returnedStatusCode = http.StatusNotFound 460 }) 461 462 It("writes an error message to stderr", func() { 463 Eventually(session.Err).Should(gbytes.Say("pipeline/job not found")) 464 Eventually(session).Should(gexec.Exit(1)) 465 }) 466 }) 467 468 Context("and time range", func() { 469 BeforeEach(func() { 470 since := time.Date(2020, 11, 1, 0, 0, 0, 0, time.Now().Location()) 471 until := time.Date(2020, 11, 2, 0, 0, 0, 0, time.Now().Location()) 472 473 cmdArgs = append(cmdArgs, "-j") 474 cmdArgs = append(cmdArgs, "some-pipeline/some-job") 475 cmdArgs = append(cmdArgs, "--since", since.Format(timeLayout)) 476 cmdArgs = append(cmdArgs, "--until", until.Format(timeLayout)) 477 478 queryParams = []string{"limit=50", fmt.Sprintf("from=%d", since.Unix()), fmt.Sprintf("to=%d", until.Unix()), "timestamps=true"} 479 }) 480 481 It("returns the builds correctly", func() { 482 Eventually(session).Should(gexec.Exit(0)) 483 }) 484 }) 485 486 Context("and the count argument", func() { 487 BeforeEach(func() { 488 cmdArgs = append(cmdArgs, "-j") 489 cmdArgs = append(cmdArgs, "some-pipeline/some-job") 490 cmdArgs = append(cmdArgs, "-c") 491 cmdArgs = append(cmdArgs, "98") 492 493 queryParams = []string{"limit=98"} 494 returnedStatusCode = http.StatusOK 495 returnedBuilds = []atc.Build{ 496 { 497 ID: 3, 498 PipelineName: "some-pipeline", 499 JobName: "some-job", 500 Name: "63", 501 Status: "succeeded", 502 StartTime: succeededBuildStartTime.Unix(), 503 EndTime: succeededBuildEndTime.Unix(), 504 }, 505 } 506 }) 507 508 It("returns the builds correctly", func() { 509 Eventually(session.Out).Should(PrintTable(ui.Table{ 510 Headers: expectedHeaders, 511 Data: []ui.TableRow{ 512 { 513 {Contents: "3"}, 514 {Contents: "some-pipeline/some-job/63"}, 515 {Contents: "succeeded"}, 516 {Contents: succeededBuildStartTime.Local().Format(timeDateLayout)}, 517 {Contents: succeededBuildEndTime.Local().Format(timeDateLayout)}, 518 {Contents: "1h15m0s"}, 519 }, 520 }, 521 })) 522 Eventually(session).Should(gexec.Exit(0)) 523 }) 524 }) 525 }) 526 527 Context("when passing the current-team argument", func() { 528 BeforeEach(func() { 529 cmdArgs = append(cmdArgs, "--current-team") 530 531 expectedURL = "/api/v1/teams/main/builds" 532 queryParams = []string{"limit=50"} 533 returnedStatusCode = http.StatusOK 534 returnedBuilds = []atc.Build{ 535 { 536 ID: 3, 537 PipelineName: "some-pipeline", 538 JobName: "some-job", 539 Name: "63", 540 Status: "succeeded", 541 StartTime: succeededBuildStartTime.Unix(), 542 EndTime: succeededBuildEndTime.Unix(), 543 }, 544 } 545 }) 546 547 It("returns the builds correctly", func() { 548 Eventually(session.Out).Should(PrintTable(ui.Table{ 549 Headers: expectedHeaders, 550 Data: []ui.TableRow{ 551 { 552 {Contents: "3"}, 553 {Contents: "some-pipeline/some-job/63"}, 554 {Contents: "succeeded"}, 555 {Contents: succeededBuildStartTime.Local().Format(timeDateLayout)}, 556 {Contents: succeededBuildEndTime.Local().Format(timeDateLayout)}, 557 {Contents: "1h15m0s"}, 558 }, 559 }, 560 })) 561 Eventually(session).Should(gexec.Exit(0)) 562 }) 563 564 Context("when the api returns an error", func() { 565 BeforeEach(func() { 566 returnedStatusCode = http.StatusInternalServerError 567 }) 568 569 It("writes an error message to stderr", func() { 570 Eventually(session.Err).Should(gbytes.Say("Unexpected Response")) 571 Eventually(session).Should(gexec.Exit(1)) 572 }) 573 }) 574 575 Context("and the count argument", func() { 576 BeforeEach(func() { 577 cmdArgs = append(cmdArgs, "-c") 578 cmdArgs = append(cmdArgs, "98") 579 580 queryParams = []string{"limit=98"} 581 returnedStatusCode = http.StatusOK 582 returnedBuilds = []atc.Build{ 583 { 584 ID: 3, 585 PipelineName: "some-pipeline", 586 JobName: "some-job", 587 Name: "63", 588 Status: "succeeded", 589 StartTime: succeededBuildStartTime.Unix(), 590 EndTime: succeededBuildEndTime.Unix(), 591 }, 592 } 593 }) 594 595 It("returns the builds correctly", func() { 596 Eventually(session.Out).Should(PrintTable(ui.Table{ 597 Headers: expectedHeaders, 598 Data: []ui.TableRow{ 599 { 600 {Contents: "3"}, 601 {Contents: "some-pipeline/some-job/63"}, 602 {Contents: "succeeded"}, 603 {Contents: succeededBuildStartTime.Local().Format(timeDateLayout)}, 604 {Contents: succeededBuildEndTime.Local().Format(timeDateLayout)}, 605 {Contents: "1h15m0s"}, 606 }, 607 }, 608 })) 609 Eventually(session).Should(gexec.Exit(0)) 610 }) 611 }) 612 }) 613 614 Context("when passing teams argument", func() { 615 616 Context("when passing one team filter", func() { 617 BeforeEach(func() { 618 cmdArgs = append(cmdArgs, "--team", "team1") 619 620 expectedURL = "/api/v1/teams/team1/builds" 621 queryParams = []string{"limit=50"} 622 returnedStatusCode = http.StatusOK 623 returnedBuilds = []atc.Build{ 624 { 625 ID: 3, 626 PipelineName: "some-pipeline", 627 JobName: "some-job", 628 Name: "63", 629 Status: "succeeded", 630 StartTime: succeededBuildStartTime.Unix(), 631 EndTime: succeededBuildEndTime.Unix(), 632 TeamName: "team1", 633 }, 634 } 635 }) 636 637 It("returns the builds correctly", func() { 638 Eventually(session.Out).Should(PrintTable(ui.Table{ 639 Headers: expectedHeaders, 640 Data: []ui.TableRow{ 641 { 642 {Contents: "3"}, 643 {Contents: "some-pipeline/some-job/63"}, 644 {Contents: "succeeded"}, 645 {Contents: succeededBuildStartTime.Local().Format(timeDateLayout)}, 646 {Contents: succeededBuildEndTime.Local().Format(timeDateLayout)}, 647 {Contents: "1h15m0s"}, 648 {Contents: "team1"}, 649 }, 650 }, 651 })) 652 Eventually(session).Should(gexec.Exit(0)) 653 }) 654 655 }) 656 657 Context("when passing multiple team filters", func() { 658 BeforeEach(func() { 659 cmdArgs = append(cmdArgs, "--team", "team1", 660 "--team", "team2") 661 662 expectedURL = "/api/v1/teams/team1/builds" 663 queryParams = []string{"limit=50"} 664 returnedStatusCode = http.StatusOK 665 returnedBuilds = []atc.Build{ 666 { 667 ID: 3, 668 PipelineName: "some-pipeline", 669 JobName: "some-job", 670 Name: "63", 671 Status: "succeeded", 672 StartTime: succeededBuildStartTime.Unix(), 673 EndTime: succeededBuildEndTime.Unix(), 674 TeamName: "team1", 675 }, 676 } 677 }) 678 679 JustBeforeEach(func() { 680 expectedURL = "/api/v1/teams/team2/builds" 681 queryParams = []string{"limit=50"} 682 returnedStatusCode = http.StatusOK 683 returnedBuilds = []atc.Build{ 684 { 685 ID: 4, 686 PipelineName: "some-pipeline", 687 JobName: "some-job", 688 Name: "63", 689 Status: "succeeded", 690 StartTime: succeededBuildStartTime.Unix(), 691 EndTime: succeededBuildEndTime.Unix(), 692 TeamName: "team2", 693 }, 694 } 695 696 atcServer.AppendHandlers( 697 ghttp.CombineHandlers( 698 ghttp.VerifyRequest("GET", expectedURL, strings.Join(queryParams, "&")), 699 ghttp.RespondWithJSONEncoded(returnedStatusCode, returnedBuilds), 700 ), 701 ) 702 }) 703 704 It("returns the builds correctly", func() { 705 Eventually(session.Out).Should(PrintTable(ui.Table{ 706 Headers: expectedHeaders, 707 Data: []ui.TableRow{ 708 { 709 {Contents: "3"}, 710 {Contents: "some-pipeline/some-job/63"}, 711 {Contents: "succeeded"}, 712 {Contents: succeededBuildStartTime.Local().Format(timeDateLayout)}, 713 {Contents: succeededBuildEndTime.Local().Format(timeDateLayout)}, 714 {Contents: "1h15m0s"}, 715 {Contents: "team1"}, 716 }, 717 718 { 719 {Contents: "4"}, 720 {Contents: "some-pipeline/some-job/63"}, 721 {Contents: "succeeded"}, 722 {Contents: succeededBuildStartTime.Local().Format(timeDateLayout)}, 723 {Contents: succeededBuildEndTime.Local().Format(timeDateLayout)}, 724 {Contents: "1h15m0s"}, 725 {Contents: "team2"}, 726 }, 727 }, 728 })) 729 Eventually(session).Should(gexec.Exit(0)) 730 }) 731 732 }) 733 }) 734 735 Context("when passing all-teams argument", func() { 736 737 BeforeEach(func() { 738 expectedURL = "/api/v1/teams" 739 returnedStatusCode = http.StatusOK 740 returnedTeams := []atc.Team{ 741 { 742 ID: 1, 743 Name: "team1", 744 }, 745 { 746 ID: 1, 747 Name: "team2", 748 }, 749 } 750 751 atcServer.AppendHandlers( 752 ghttp.CombineHandlers( 753 ghttp.VerifyRequest("GET", expectedURL), 754 ghttp.RespondWithJSONEncoded(returnedStatusCode, returnedTeams), 755 ), 756 ) 757 cmdArgs = append(cmdArgs, "--all-teams") 758 759 expectedURL = "/api/v1/teams/team1/builds" 760 queryParams = []string{"limit=50"} 761 returnedStatusCode = http.StatusOK 762 returnedBuilds = []atc.Build{ 763 { 764 ID: 3, 765 PipelineName: "some-pipeline", 766 JobName: "some-job", 767 Name: "63", 768 Status: "succeeded", 769 StartTime: succeededBuildStartTime.Unix(), 770 EndTime: succeededBuildEndTime.Unix(), 771 TeamName: "team1", 772 }, 773 } 774 }) 775 776 JustBeforeEach(func() { 777 expectedURL = "/api/v1/teams/team2/builds" 778 queryParams = []string{"limit=50"} 779 returnedStatusCode = http.StatusOK 780 returnedBuilds = []atc.Build{ 781 { 782 ID: 4, 783 PipelineName: "some-pipeline", 784 JobName: "some-job", 785 Name: "63", 786 Status: "succeeded", 787 StartTime: succeededBuildStartTime.Unix(), 788 EndTime: succeededBuildEndTime.Unix(), 789 TeamName: "team2", 790 }, 791 } 792 793 atcServer.AppendHandlers( 794 ghttp.CombineHandlers( 795 ghttp.VerifyRequest("GET", expectedURL, strings.Join(queryParams, "&")), 796 ghttp.RespondWithJSONEncoded(returnedStatusCode, returnedBuilds), 797 ), 798 ) 799 }) 800 801 It("returns the builds correctly", func() { 802 Eventually(session.Out).Should(PrintTable(ui.Table{ 803 Headers: expectedHeaders, 804 Data: []ui.TableRow{ 805 { 806 {Contents: "3"}, 807 {Contents: "some-pipeline/some-job/63"}, 808 {Contents: "succeeded"}, 809 {Contents: succeededBuildStartTime.Local().Format(timeDateLayout)}, 810 {Contents: succeededBuildEndTime.Local().Format(timeDateLayout)}, 811 {Contents: "1h15m0s"}, 812 {Contents: "team1"}, 813 }, 814 { 815 {Contents: "4"}, 816 {Contents: "some-pipeline/some-job/63"}, 817 {Contents: "succeeded"}, 818 {Contents: succeededBuildStartTime.Local().Format(timeDateLayout)}, 819 {Contents: succeededBuildEndTime.Local().Format(timeDateLayout)}, 820 {Contents: "1h15m0s"}, 821 {Contents: "team2"}, 822 }, 823 }, 824 })) 825 Eventually(session).Should(gexec.Exit(0)) 826 }) 827 828 }) 829 830 Context("when passing a time range", func() { 831 var ( 832 since time.Time 833 until time.Time 834 ) 835 836 BeforeEach(func() { 837 since = time.Date(2020, 11, 1, 0, 0, 0, 0, time.Now().Location()) 838 until = time.Date(2020, 11, 2, 0, 0, 0, 0, time.Now().Location()) 839 840 expectedURL = "/api/v1/builds" 841 queryParams = []string{"limit=50", fmt.Sprintf("from=%d", since.Unix()), fmt.Sprintf("to=%d", until.Unix()), "timestamps=true"} 842 returnedStatusCode = http.StatusOK 843 returnedBuilds = []atc.Build{ 844 { 845 ID: 3, 846 PipelineName: "some-pipeline", 847 JobName: "some-job", 848 Name: "63", 849 Status: "succeeded", 850 StartTime: succeededBuildStartTime.Unix(), 851 EndTime: succeededBuildEndTime.Unix(), 852 TeamName: "team1", 853 }, 854 } 855 856 cmdArgs = append(cmdArgs, "--since", since.Format(timeLayout)) 857 cmdArgs = append(cmdArgs, "--until", until.Format(timeLayout)) 858 }) 859 860 It("returns the correct builds", func() { 861 Eventually(session.Out).Should(PrintTable(ui.Table{ 862 Headers: expectedHeaders, 863 Data: []ui.TableRow{ 864 { 865 {Contents: "3"}, 866 {Contents: "some-pipeline/some-job/63"}, 867 {Contents: "succeeded"}, 868 {Contents: succeededBuildStartTime.Local().Format(timeDateLayout)}, 869 {Contents: succeededBuildEndTime.Local().Format(timeDateLayout)}, 870 {Contents: "1h15m0s"}, 871 {Contents: "team1"}, 872 }, 873 }, 874 })) 875 Eventually(session).Should(gexec.Exit(0)) 876 }) 877 }) 878 879 Context("when passing the pipeline argument", func() { 880 BeforeEach(func() { 881 cmdArgs = append(cmdArgs, "-p") 882 cmdArgs = append(cmdArgs, "some-pipeline/branch:master") 883 884 expectedURL = "/api/v1/teams/main/pipelines/some-pipeline/builds" 885 queryParams = []string{"instance_vars=%7B%22branch%22%3A%22master%22%7D", "limit=50"} 886 returnedStatusCode = http.StatusOK 887 returnedBuilds = []atc.Build{ 888 { 889 ID: 3, 890 PipelineName: "some-pipeline", 891 PipelineInstanceVars: atc.InstanceVars{"branch": "master"}, 892 JobName: "some-job", 893 Name: "63", 894 Status: "succeeded", 895 StartTime: succeededBuildStartTime.Unix(), 896 EndTime: succeededBuildEndTime.Unix(), 897 }, 898 } 899 }) 900 901 It("returns the builds correctly", func() { 902 Eventually(session.Out).Should(PrintTable(ui.Table{ 903 Headers: expectedHeaders, 904 Data: []ui.TableRow{ 905 { 906 {Contents: "3"}, 907 {Contents: "some-pipeline/branch:master/some-job/63"}, 908 {Contents: "succeeded"}, 909 {Contents: succeededBuildStartTime.Local().Format(timeDateLayout)}, 910 {Contents: succeededBuildEndTime.Local().Format(timeDateLayout)}, 911 {Contents: "1h15m0s"}, 912 }, 913 }, 914 })) 915 Eventually(session).Should(gexec.Exit(0)) 916 }) 917 918 Context("when the api returns an error", func() { 919 BeforeEach(func() { 920 returnedStatusCode = http.StatusInternalServerError 921 }) 922 923 It("writes an error message to stderr", func() { 924 Eventually(session.Err).Should(gbytes.Say("Unexpected Response")) 925 Eventually(session).Should(gexec.Exit(1)) 926 }) 927 }) 928 929 Context("when the api returns a not found", func() { 930 BeforeEach(func() { 931 returnedStatusCode = http.StatusNotFound 932 }) 933 934 It("writes an error message to stderr", func() { 935 Eventually(session.Err).Should(gbytes.Say("pipeline not found")) 936 Eventually(session).Should(gexec.Exit(1)) 937 }) 938 }) 939 940 Context("and the count argument", func() { 941 BeforeEach(func() { 942 cmdArgs = append(cmdArgs, "-c") 943 cmdArgs = append(cmdArgs, "98") 944 945 queryParams = []string{"instance_vars=%7B%22branch%22%3A%22master%22%7D", "limit=98"} 946 returnedStatusCode = http.StatusOK 947 returnedBuilds = []atc.Build{ 948 { 949 ID: 3, 950 PipelineName: "some-pipeline", 951 PipelineInstanceVars: atc.InstanceVars{"branch": "master"}, 952 JobName: "some-job", 953 Name: "63", 954 Status: "succeeded", 955 StartTime: succeededBuildStartTime.Unix(), 956 EndTime: succeededBuildEndTime.Unix(), 957 }, 958 } 959 }) 960 961 It("returns the builds correctly", func() { 962 Eventually(session.Out).Should(PrintTable(ui.Table{ 963 Headers: expectedHeaders, 964 Data: []ui.TableRow{ 965 { 966 {Contents: "3"}, 967 {Contents: "some-pipeline/branch:master/some-job/63"}, 968 {Contents: "succeeded"}, 969 {Contents: succeededBuildStartTime.Local().Format(timeDateLayout)}, 970 {Contents: succeededBuildEndTime.Local().Format(timeDateLayout)}, 971 {Contents: "1h15m0s"}, 972 }, 973 }, 974 })) 975 Eventually(session).Should(gexec.Exit(0)) 976 }) 977 }) 978 979 Context("and time range", func() { 980 BeforeEach(func() { 981 since := time.Date(2020, 11, 1, 0, 0, 0, 0, time.Now().Location()) 982 until := time.Date(2020, 11, 2, 0, 0, 0, 0, time.Now().Location()) 983 984 cmdArgs = append(cmdArgs, "-p") 985 cmdArgs = append(cmdArgs, "some-pipeline/branch:master") 986 cmdArgs = append(cmdArgs, "--since", since.Format(timeLayout)) 987 cmdArgs = append(cmdArgs, "--until", until.Format(timeLayout)) 988 989 queryParams = []string{"instance_vars=%7B%22branch%22%3A%22master%22%7D", "limit=50", fmt.Sprintf("from=%d", since.Unix()), fmt.Sprintf("to=%d", until.Unix()), "timestamps=true"} 990 }) 991 992 It("returns the builds correctly", func() { 993 Eventually(session).Should(gexec.Exit(0)) 994 }) 995 }) 996 }) 997 }) 998 })