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