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  })