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&timestamps=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&timestamps=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&timestamps=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  })