github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/db/job_test.go (about)

     1  package db_test
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	"github.com/pf-qiu/concourse/v6/atc"
     9  	"github.com/pf-qiu/concourse/v6/atc/db"
    10  	"github.com/pf-qiu/concourse/v6/atc/db/dbtest"
    11  	"github.com/pf-qiu/concourse/v6/tracing"
    12  	. "github.com/onsi/ginkgo"
    13  	. "github.com/onsi/gomega"
    14  	"go.opentelemetry.io/otel/api/trace/tracetest"
    15  )
    16  
    17  var _ = Describe("Job", func() {
    18  	var (
    19  		job      db.Job
    20  		pipeline db.Pipeline
    21  		team     db.Team
    22  	)
    23  
    24  	BeforeEach(func() {
    25  		var err error
    26  		team, err = teamFactory.CreateTeam(atc.Team{Name: "some-team"})
    27  		Expect(err).ToNot(HaveOccurred())
    28  
    29  		var created bool
    30  		pipeline, created, err = team.SavePipeline(atc.PipelineRef{Name: "fake-pipeline"}, atc.Config{
    31  			Jobs: atc.JobConfigs{
    32  				{
    33  					Name: "some-job",
    34  
    35  					Public: true,
    36  
    37  					PlanSequence: []atc.Step{
    38  						{
    39  							Config: &atc.PutStep{
    40  								Name: "some-resource",
    41  								Params: atc.Params{
    42  									"some-param": "some-value",
    43  								},
    44  							},
    45  						},
    46  						{
    47  							Config: &atc.GetStep{
    48  								Name:     "some-input",
    49  								Resource: "some-resource",
    50  								Params: atc.Params{
    51  									"some-param": "some-value",
    52  								},
    53  								Passed:  []string{"job-1", "job-2"},
    54  								Trigger: true,
    55  							},
    56  						},
    57  						{
    58  							Config: &atc.TaskStep{
    59  								Name:       "some-task",
    60  								Privileged: true,
    61  								ConfigPath: "some/config/path.yml",
    62  								Config: &atc.TaskConfig{
    63  									RootfsURI: "some-image",
    64  								},
    65  							},
    66  						},
    67  					},
    68  				},
    69  				{
    70  					Name: "some-other-job",
    71  				},
    72  				{
    73  					Name:   "some-private-job",
    74  					Public: false,
    75  				},
    76  				{
    77  					Name: "other-serial-group-job",
    78  				},
    79  				{
    80  					Name: "different-serial-group-job",
    81  				},
    82  				{
    83  					Name: "job-1",
    84  				},
    85  				{
    86  					Name: "job-2",
    87  				},
    88  				{
    89  					Name:                 "non-triggerable-job",
    90  					DisableManualTrigger: true,
    91  				},
    92  			},
    93  			Resources: atc.ResourceConfigs{
    94  				{
    95  					Name: "some-resource",
    96  					Type: "some-type",
    97  				},
    98  				{
    99  					Name: "some-other-resource",
   100  					Type: "some-type",
   101  				},
   102  			},
   103  		}, db.ConfigVersion(0), false)
   104  		Expect(err).ToNot(HaveOccurred())
   105  		Expect(created).To(BeTrue())
   106  
   107  		var found bool
   108  		job, found, err = pipeline.Job("some-job")
   109  		Expect(err).ToNot(HaveOccurred())
   110  		Expect(found).To(BeTrue())
   111  	})
   112  
   113  	Describe("Public", func() {
   114  		Context("when the config has public set to true", func() {
   115  			It("returns true", func() {
   116  				Expect(job.Public()).To(BeTrue())
   117  			})
   118  		})
   119  
   120  		Context("when the config has public set to false", func() {
   121  			It("returns false", func() {
   122  				privateJob, found, err := pipeline.Job("some-private-job")
   123  				Expect(err).ToNot(HaveOccurred())
   124  				Expect(found).To(BeTrue())
   125  				Expect(privateJob.Public()).To(BeFalse())
   126  			})
   127  		})
   128  
   129  		Context("when the config does not have public set", func() {
   130  			It("returns false", func() {
   131  				otherJob, found, err := pipeline.Job("some-other-job")
   132  				Expect(err).ToNot(HaveOccurred())
   133  				Expect(found).To(BeTrue())
   134  				Expect(otherJob.Public()).To(BeFalse())
   135  			})
   136  		})
   137  	})
   138  
   139  	Describe("DisableManualTrigger", func() {
   140  		Context("when the config has disable_manual_trigger set to true", func() {
   141  			It("returns true", func() {
   142  				nonTriggerableJob, found, err := pipeline.Job("non-triggerable-job")
   143  				Expect(err).ToNot(HaveOccurred())
   144  				Expect(found).To(BeTrue())
   145  				Expect(nonTriggerableJob.DisableManualTrigger()).To(BeTrue())
   146  			})
   147  		})
   148  
   149  		Context("when the config does not have disable_manual_trigger set", func() {
   150  			It("returns false", func() {
   151  				otherJob, found, err := pipeline.Job("some-other-job")
   152  				Expect(err).ToNot(HaveOccurred())
   153  				Expect(found).To(BeTrue())
   154  				Expect(otherJob.DisableManualTrigger()).To(BeFalse())
   155  			})
   156  		})
   157  	})
   158  
   159  	Describe("Pause and Unpause", func() {
   160  		var initialRequestedTime time.Time
   161  		It("starts out as unpaused", func() {
   162  			Expect(job.Paused()).To(BeFalse())
   163  		})
   164  
   165  		Context("when pausing job", func() {
   166  			BeforeEach(func() {
   167  				initialRequestedTime = job.ScheduleRequestedTime()
   168  
   169  				err := job.Pause()
   170  				Expect(err).ToNot(HaveOccurred())
   171  
   172  				found, err := job.Reload()
   173  				Expect(err).ToNot(HaveOccurred())
   174  				Expect(found).To(BeTrue())
   175  			})
   176  
   177  			It("job is succesfully paused", func() {
   178  				Expect(job.Paused()).To(BeTrue())
   179  			})
   180  
   181  			It("does not request schedule on job", func() {
   182  				Expect(job.ScheduleRequestedTime()).Should(BeTemporally("==", initialRequestedTime))
   183  			})
   184  		})
   185  
   186  		Context("when unpausing job", func() {
   187  			BeforeEach(func() {
   188  				initialRequestedTime = job.ScheduleRequestedTime()
   189  
   190  				err := job.Unpause()
   191  				Expect(err).ToNot(HaveOccurred())
   192  
   193  				found, err := job.Reload()
   194  				Expect(err).ToNot(HaveOccurred())
   195  				Expect(found).To(BeTrue())
   196  			})
   197  
   198  			It("job is successfully unpaused", func() {
   199  				Expect(job.Paused()).To(BeFalse())
   200  			})
   201  
   202  			It("requests schedule on job", func() {
   203  				Expect(job.ScheduleRequestedTime()).Should(BeTemporally(">", initialRequestedTime))
   204  			})
   205  		})
   206  
   207  	})
   208  
   209  	Describe("FinishedAndNextBuild", func() {
   210  		var otherPipeline db.Pipeline
   211  		var otherJob db.Job
   212  
   213  		BeforeEach(func() {
   214  			var created bool
   215  			var err error
   216  			otherPipeline, created, err = team.SavePipeline(atc.PipelineRef{Name: "other-pipeline"}, atc.Config{
   217  				Jobs: atc.JobConfigs{
   218  					{Name: "some-job"},
   219  				},
   220  			}, db.ConfigVersion(0), false)
   221  			Expect(err).ToNot(HaveOccurred())
   222  			Expect(created).To(BeTrue())
   223  
   224  			var found bool
   225  			otherJob, found, err = otherPipeline.Job("some-job")
   226  			Expect(err).ToNot(HaveOccurred())
   227  			Expect(found).To(BeTrue())
   228  		})
   229  
   230  		It("can report a job's latest running and finished builds", func() {
   231  			finished, next, err := job.FinishedAndNextBuild()
   232  			Expect(err).NotTo(HaveOccurred())
   233  
   234  			Expect(next).To(BeNil())
   235  			Expect(finished).To(BeNil())
   236  
   237  			finishedBuild, err := job.CreateBuild()
   238  			Expect(err).NotTo(HaveOccurred())
   239  
   240  			err = finishedBuild.Finish(db.BuildStatusSucceeded)
   241  			Expect(err).NotTo(HaveOccurred())
   242  
   243  			otherFinishedBuild, err := otherJob.CreateBuild()
   244  			Expect(err).NotTo(HaveOccurred())
   245  
   246  			err = otherFinishedBuild.Finish(db.BuildStatusSucceeded)
   247  			Expect(err).NotTo(HaveOccurred())
   248  
   249  			finished, next, err = job.FinishedAndNextBuild()
   250  			Expect(err).NotTo(HaveOccurred())
   251  
   252  			Expect(next).To(BeNil())
   253  			Expect(finished.ID()).To(Equal(finishedBuild.ID()))
   254  
   255  			nextBuild, err := job.CreateBuild()
   256  			Expect(err).NotTo(HaveOccurred())
   257  
   258  			started, err := nextBuild.Start(atc.Plan{})
   259  			Expect(err).NotTo(HaveOccurred())
   260  			Expect(started).To(BeTrue())
   261  
   262  			otherNextBuild, err := otherJob.CreateBuild()
   263  			Expect(err).NotTo(HaveOccurred())
   264  
   265  			otherStarted, err := otherNextBuild.Start(atc.Plan{})
   266  			Expect(err).NotTo(HaveOccurred())
   267  			Expect(otherStarted).To(BeTrue())
   268  
   269  			finished, next, err = job.FinishedAndNextBuild()
   270  			Expect(err).NotTo(HaveOccurred())
   271  
   272  			Expect(next.ID()).To(Equal(nextBuild.ID()))
   273  			Expect(finished.ID()).To(Equal(finishedBuild.ID()))
   274  
   275  			anotherRunningBuild, err := job.CreateBuild()
   276  			Expect(err).NotTo(HaveOccurred())
   277  
   278  			finished, next, err = job.FinishedAndNextBuild()
   279  			Expect(err).NotTo(HaveOccurred())
   280  
   281  			Expect(next.ID()).To(Equal(nextBuild.ID())) // not anotherRunningBuild
   282  			Expect(finished.ID()).To(Equal(finishedBuild.ID()))
   283  
   284  			started, err = anotherRunningBuild.Start(atc.Plan{})
   285  			Expect(err).NotTo(HaveOccurred())
   286  			Expect(started).To(BeTrue())
   287  
   288  			finished, next, err = job.FinishedAndNextBuild()
   289  			Expect(err).NotTo(HaveOccurred())
   290  
   291  			Expect(next.ID()).To(Equal(nextBuild.ID())) // not anotherRunningBuild
   292  			Expect(finished.ID()).To(Equal(finishedBuild.ID()))
   293  
   294  			err = nextBuild.Finish(db.BuildStatusSucceeded)
   295  			Expect(err).NotTo(HaveOccurred())
   296  
   297  			finished, next, err = job.FinishedAndNextBuild()
   298  			Expect(err).NotTo(HaveOccurred())
   299  
   300  			Expect(next.ID()).To(Equal(anotherRunningBuild.ID()))
   301  			Expect(finished.ID()).To(Equal(nextBuild.ID()))
   302  		})
   303  	})
   304  
   305  	Describe("UpdateFirstLoggedBuildID", func() {
   306  		It("updates FirstLoggedBuildID on a job", func() {
   307  			By("starting out as 0")
   308  			Expect(job.FirstLoggedBuildID()).To(BeZero())
   309  
   310  			By("increasing it to 57")
   311  			err := job.UpdateFirstLoggedBuildID(57)
   312  			Expect(err).NotTo(HaveOccurred())
   313  
   314  			found, err := job.Reload()
   315  			Expect(err).NotTo(HaveOccurred())
   316  			Expect(found).To(BeTrue())
   317  			Expect(job.FirstLoggedBuildID()).To(Equal(57))
   318  
   319  			By("not erroring when it's called with the same number")
   320  			err = job.UpdateFirstLoggedBuildID(57)
   321  			Expect(err).NotTo(HaveOccurred())
   322  
   323  			By("erroring when the number decreases")
   324  			err = job.UpdateFirstLoggedBuildID(56)
   325  			Expect(err).To(Equal(db.FirstLoggedBuildIDDecreasedError{
   326  				Job:   "some-job",
   327  				OldID: 57,
   328  				NewID: 56,
   329  			}))
   330  		})
   331  	})
   332  
   333  	Describe("Builds", func() {
   334  		var (
   335  			builds       [10]db.Build
   336  			someJob      db.Job
   337  			someOtherJob db.Job
   338  		)
   339  
   340  		BeforeEach(func() {
   341  			for i := 0; i < 10; i++ {
   342  				var found bool
   343  				var err error
   344  				someJob, found, err = pipeline.Job("some-job")
   345  				Expect(err).NotTo(HaveOccurred())
   346  				Expect(found).To(BeTrue())
   347  
   348  				someOtherJob, found, err = pipeline.Job("some-other-job")
   349  				Expect(err).NotTo(HaveOccurred())
   350  				Expect(found).To(BeTrue())
   351  
   352  				build, err := someJob.CreateBuild()
   353  				Expect(err).NotTo(HaveOccurred())
   354  
   355  				_, err = someOtherJob.CreateBuild()
   356  				Expect(err).NotTo(HaveOccurred())
   357  
   358  				builds[i] = build
   359  			}
   360  		})
   361  
   362  		Context("when there are no builds to be found", func() {
   363  			It("returns the builds, with previous/next pages", func() {
   364  				buildsPage, pagination, err := someOtherJob.Builds(db.Page{})
   365  				Expect(err).ToNot(HaveOccurred())
   366  				Expect(buildsPage).To(Equal([]db.Build{}))
   367  				Expect(pagination).To(Equal(db.Pagination{}))
   368  			})
   369  		})
   370  
   371  		Context("with no from/to", func() {
   372  			It("returns the first page, with the given limit, and a next page", func() {
   373  				buildsPage, pagination, err := someJob.Builds(db.Page{Limit: 2})
   374  				Expect(err).ToNot(HaveOccurred())
   375  				Expect(buildsPage).To(Equal([]db.Build{builds[9], builds[8]}))
   376  				Expect(pagination.Newer).To(BeNil())
   377  				Expect(pagination.Older).To(Equal(&db.Page{To: db.NewIntPtr(builds[7].ID()), Limit: 2}))
   378  			})
   379  		})
   380  
   381  		Context("with a to that places it in the middle of the builds", func() {
   382  			It("returns the builds, with previous/next pages", func() {
   383  				buildsPage, pagination, err := someJob.Builds(db.Page{To: db.NewIntPtr(builds[6].ID()), Limit: 2})
   384  				Expect(err).ToNot(HaveOccurred())
   385  				Expect(buildsPage).To(Equal([]db.Build{builds[6], builds[5]}))
   386  				Expect(pagination.Newer).To(Equal(&db.Page{From: db.NewIntPtr(builds[7].ID()), Limit: 2}))
   387  				Expect(pagination.Older).To(Equal(&db.Page{To: db.NewIntPtr(builds[4].ID()), Limit: 2}))
   388  			})
   389  		})
   390  
   391  		Context("with a to that places it at the end of the builds", func() {
   392  			It("returns the builds, with previous/next pages", func() {
   393  				buildsPage, pagination, err := someJob.Builds(db.Page{To: db.NewIntPtr(builds[1].ID()), Limit: 2})
   394  				Expect(err).ToNot(HaveOccurred())
   395  				Expect(buildsPage).To(Equal([]db.Build{builds[1], builds[0]}))
   396  				Expect(pagination.Newer).To(Equal(&db.Page{From: db.NewIntPtr(builds[2].ID()), Limit: 2}))
   397  				Expect(pagination.Older).To(BeNil())
   398  			})
   399  		})
   400  
   401  		Context("with a from that places it in the middle of the builds", func() {
   402  			It("returns the builds, with previous/next pages", func() {
   403  				buildsPage, pagination, err := someJob.Builds(db.Page{From: db.NewIntPtr(builds[6].ID()), Limit: 2})
   404  				Expect(err).ToNot(HaveOccurred())
   405  				Expect(buildsPage).To(Equal([]db.Build{builds[7], builds[6]}))
   406  				Expect(pagination.Newer).To(Equal(&db.Page{From: db.NewIntPtr(builds[8].ID()), Limit: 2}))
   407  				Expect(pagination.Older).To(Equal(&db.Page{To: db.NewIntPtr(builds[5].ID()), Limit: 2}))
   408  			})
   409  		})
   410  
   411  		Context("with a from that places it at the beginning of the builds", func() {
   412  			It("returns the builds, with previous/next pages", func() {
   413  				buildsPage, pagination, err := someJob.Builds(db.Page{From: db.NewIntPtr(builds[8].ID()), Limit: 2})
   414  				Expect(err).ToNot(HaveOccurred())
   415  				Expect(buildsPage).To(Equal([]db.Build{builds[9], builds[8]}))
   416  				Expect(pagination.Newer).To(BeNil())
   417  				Expect(pagination.Older).To(Equal(&db.Page{To: db.NewIntPtr(builds[7].ID()), Limit: 2}))
   418  			})
   419  		})
   420  	})
   421  
   422  	Describe("BuildsWithTime", func() {
   423  
   424  		var (
   425  			pipeline db.Pipeline
   426  			builds   = make([]db.Build, 4)
   427  			job      db.Job
   428  		)
   429  
   430  		BeforeEach(func() {
   431  			var (
   432  				err   error
   433  				found bool
   434  			)
   435  
   436  			config := atc.Config{
   437  				Jobs: atc.JobConfigs{
   438  					{
   439  						Name: "some-job",
   440  					},
   441  					{
   442  						Name: "some-other-job",
   443  					},
   444  				},
   445  			}
   446  			pipeline, _, err = team.SavePipeline(atc.PipelineRef{Name: "some-pipeline"}, config, db.ConfigVersion(1), false)
   447  			Expect(err).ToNot(HaveOccurred())
   448  
   449  			job, found, err = pipeline.Job("some-job")
   450  			Expect(err).ToNot(HaveOccurred())
   451  			Expect(found).To(BeTrue())
   452  
   453  			for i := range builds {
   454  				builds[i], err = job.CreateBuild()
   455  				Expect(err).ToNot(HaveOccurred())
   456  
   457  				buildStart := time.Date(2020, 11, i+1, 0, 0, 0, 0, time.UTC)
   458  				_, err = dbConn.Exec("UPDATE builds SET start_time = to_timestamp($1) WHERE id = $2", buildStart.Unix(), builds[i].ID())
   459  				Expect(err).NotTo(HaveOccurred())
   460  
   461  				builds[i], found, err = job.Build(builds[i].Name())
   462  				Expect(err).ToNot(HaveOccurred())
   463  				Expect(found).To(BeTrue())
   464  			}
   465  		})
   466  
   467  		Context("when not providing boundaries", func() {
   468  			Context("without a limit specified", func() {
   469  				It("returns no builds", func() {
   470  					returnedBuilds, _, err := job.BuildsWithTime(db.Page{})
   471  					Expect(err).NotTo(HaveOccurred())
   472  
   473  					Expect(returnedBuilds).To(BeEmpty())
   474  				})
   475  			})
   476  
   477  			Context("when a limit specified", func() {
   478  				It("returns a subset of the builds", func() {
   479  					returnedBuilds, _, err := job.BuildsWithTime(db.Page{
   480  						Limit: 2,
   481  					})
   482  					Expect(err).NotTo(HaveOccurred())
   483  					Expect(returnedBuilds).To(ConsistOf(builds[3], builds[2]))
   484  				})
   485  			})
   486  		})
   487  
   488  		Context("when providing boundaries", func() {
   489  			Context("only to", func() {
   490  				It("returns only those before to", func() {
   491  					returnedBuilds, _, err := job.BuildsWithTime(db.Page{
   492  						To:    db.NewIntPtr(int(builds[2].StartTime().Unix())),
   493  						Limit: 50,
   494  					})
   495  
   496  					Expect(err).NotTo(HaveOccurred())
   497  					Expect(returnedBuilds).To(ConsistOf(builds[0], builds[1], builds[2]))
   498  				})
   499  			})
   500  
   501  			Context("only from", func() {
   502  				It("returns only those after from", func() {
   503  					returnedBuilds, _, err := job.BuildsWithTime(db.Page{
   504  						From:  db.NewIntPtr(int(builds[1].StartTime().Unix())),
   505  						Limit: 50,
   506  					})
   507  
   508  					Expect(err).NotTo(HaveOccurred())
   509  					Expect(returnedBuilds).To(ConsistOf(builds[1], builds[2], builds[3]))
   510  				})
   511  			})
   512  
   513  			Context("from and to", func() {
   514  				It("returns only elements in the range", func() {
   515  					returnedBuilds, _, err := job.BuildsWithTime(db.Page{
   516  						From:  db.NewIntPtr(int(builds[1].StartTime().Unix())),
   517  						To:    db.NewIntPtr(int(builds[2].StartTime().Unix())),
   518  						Limit: 50,
   519  					})
   520  					Expect(err).NotTo(HaveOccurred())
   521  					Expect(returnedBuilds).To(ConsistOf(builds[1], builds[2]))
   522  				})
   523  			})
   524  		})
   525  	})
   526  
   527  	Describe("Build", func() {
   528  		var firstBuild db.Build
   529  
   530  		Context("when a build exists", func() {
   531  			BeforeEach(func() {
   532  				var err error
   533  				firstBuild, err = job.CreateBuild()
   534  				Expect(err).NotTo(HaveOccurred())
   535  			})
   536  
   537  			It("finds the latest build", func() {
   538  				secondBuild, err := job.CreateBuild()
   539  				Expect(err).NotTo(HaveOccurred())
   540  
   541  				build, found, err := job.Build("latest")
   542  				Expect(err).NotTo(HaveOccurred())
   543  				Expect(found).To(BeTrue())
   544  				Expect(build.ID()).To(Equal(secondBuild.ID()))
   545  				Expect(build.Status()).To(Equal(secondBuild.Status()))
   546  			})
   547  
   548  			It("finds the build", func() {
   549  				build, found, err := job.Build(firstBuild.Name())
   550  				Expect(err).NotTo(HaveOccurred())
   551  				Expect(found).To(BeTrue())
   552  				Expect(build.ID()).To(Equal(firstBuild.ID()))
   553  				Expect(build.Status()).To(Equal(firstBuild.Status()))
   554  			})
   555  		})
   556  
   557  		Context("when the build does not exist", func() {
   558  			It("does not error", func() {
   559  				build, found, err := job.Build("bogus-build")
   560  				Expect(err).NotTo(HaveOccurred())
   561  				Expect(found).To(BeFalse())
   562  				Expect(build).To(BeNil())
   563  			})
   564  
   565  			It("does not error finding the latest", func() {
   566  				build, found, err := job.Build("latest")
   567  				Expect(err).NotTo(HaveOccurred())
   568  				Expect(found).To(BeFalse())
   569  				Expect(build).To(BeNil())
   570  			})
   571  		})
   572  
   573  		Context("creating a build", func() {
   574  			It("requests schedule on the job", func() {
   575  				requestedSchedule := job.ScheduleRequestedTime()
   576  
   577  				_, err := job.CreateBuild()
   578  				Expect(err).NotTo(HaveOccurred())
   579  
   580  				found, err := job.Reload()
   581  				Expect(err).NotTo(HaveOccurred())
   582  				Expect(found).To(BeTrue())
   583  
   584  				Expect(job.ScheduleRequestedTime()).Should(BeTemporally(">", requestedSchedule))
   585  			})
   586  		})
   587  	})
   588  
   589  	Describe("RerunBuild", func() {
   590  		var firstBuild db.Build
   591  		var rerunErr error
   592  		var rerunBuild db.Build
   593  		var buildToRerun db.Build
   594  
   595  		JustBeforeEach(func() {
   596  			rerunBuild, rerunErr = job.RerunBuild(buildToRerun)
   597  		})
   598  
   599  		Context("when the first build exists", func() {
   600  			BeforeEach(func() {
   601  				var err error
   602  				firstBuild, err = job.CreateBuild()
   603  				Expect(err).NotTo(HaveOccurred())
   604  
   605  				buildToRerun = firstBuild
   606  			})
   607  
   608  			It("finds the build", func() {
   609  				Expect(rerunErr).ToNot(HaveOccurred())
   610  				Expect(rerunBuild.Name()).To(Equal(fmt.Sprintf("%s.1", firstBuild.Name())))
   611  				Expect(rerunBuild.RerunNumber()).To(Equal(1))
   612  
   613  				build, found, err := job.Build(rerunBuild.Name())
   614  				Expect(err).NotTo(HaveOccurred())
   615  				Expect(found).To(BeTrue())
   616  				Expect(build.ID()).To(Equal(rerunBuild.ID()))
   617  				Expect(build.Status()).To(Equal(rerunBuild.Status()))
   618  			})
   619  
   620  			It("requests schedule on the job", func() {
   621  				requestedSchedule := job.ScheduleRequestedTime()
   622  
   623  				_, err := job.RerunBuild(buildToRerun)
   624  				Expect(err).NotTo(HaveOccurred())
   625  
   626  				found, err := job.Reload()
   627  				Expect(err).NotTo(HaveOccurred())
   628  				Expect(found).To(BeTrue())
   629  
   630  				Expect(job.ScheduleRequestedTime()).Should(BeTemporally(">", requestedSchedule))
   631  			})
   632  
   633  			Context("when there is an existing rerun build", func() {
   634  				var rerun1 db.Build
   635  
   636  				BeforeEach(func() {
   637  					var err error
   638  					rerun1, err = job.RerunBuild(buildToRerun)
   639  					Expect(err).ToNot(HaveOccurred())
   640  					Expect(rerun1.Name()).To(Equal(fmt.Sprintf("%s.1", firstBuild.Name())))
   641  					Expect(rerun1.RerunNumber()).To(Equal(1))
   642  				})
   643  
   644  				It("increments the rerun build number", func() {
   645  					Expect(rerunErr).ToNot(HaveOccurred())
   646  					Expect(rerunBuild.Name()).To(Equal(fmt.Sprintf("%s.2", firstBuild.Name())))
   647  					Expect(rerunBuild.RerunNumber()).To(Equal(rerun1.RerunNumber() + 1))
   648  				})
   649  			})
   650  
   651  			Context("when we try to rerun a rerun build", func() {
   652  				var rerun1 db.Build
   653  
   654  				BeforeEach(func() {
   655  					var err error
   656  					rerun1, err = job.RerunBuild(buildToRerun)
   657  					Expect(err).ToNot(HaveOccurred())
   658  					Expect(rerun1.Name()).To(Equal(fmt.Sprintf("%s.1", firstBuild.Name())))
   659  					Expect(rerun1.RerunNumber()).To(Equal(1))
   660  
   661  					buildToRerun = rerun1
   662  				})
   663  
   664  				It("keeps the name of original build and increments the rerun build number", func() {
   665  					Expect(rerunErr).ToNot(HaveOccurred())
   666  					Expect(rerunBuild.Name()).To(Equal(fmt.Sprintf("%s.2", firstBuild.Name())))
   667  					Expect(rerunBuild.RerunNumber()).To(Equal(rerun1.RerunNumber() + 1))
   668  				})
   669  			})
   670  		})
   671  	})
   672  
   673  	Describe("ScheduleBuild", func() {
   674  		var (
   675  			schedulingBuild            db.Build
   676  			scheduleFound, reloadFound bool
   677  			schedulingErr              error
   678  		)
   679  
   680  		saveMaxInFlightPipeline := func() {
   681  			BeforeEach(func() {
   682  				var err error
   683  				pipeline, _, err = team.SavePipeline(atc.PipelineRef{Name: "fake-pipeline"}, atc.Config{
   684  					Jobs: atc.JobConfigs{
   685  						{
   686  							Name: "some-job",
   687  
   688  							Public: true,
   689  
   690  							RawMaxInFlight: 2,
   691  
   692  							PlanSequence: []atc.Step{
   693  								{
   694  									Config: &atc.PutStep{
   695  										Name: "some-resource",
   696  										Params: atc.Params{
   697  											"some-param": "some-value",
   698  										},
   699  									},
   700  								},
   701  								{
   702  									Config: &atc.GetStep{
   703  										Name:     "some-input",
   704  										Resource: "some-resource",
   705  										Params: atc.Params{
   706  											"some-param": "some-value",
   707  										},
   708  										Passed:  []string{"job-1", "job-2"},
   709  										Trigger: true,
   710  									},
   711  								},
   712  								{
   713  									Config: &atc.TaskStep{
   714  										Name:       "some-task",
   715  										Privileged: true,
   716  										ConfigPath: "some/config/path.yml",
   717  										Config: &atc.TaskConfig{
   718  											RootfsURI: "some-image",
   719  										},
   720  									},
   721  								},
   722  							},
   723  						},
   724  						{
   725  							Name: "some-other-job",
   726  						},
   727  						{
   728  							Name:   "some-private-job",
   729  							Public: false,
   730  						},
   731  						{
   732  							Name: "other-serial-group-job",
   733  						},
   734  						{
   735  							Name: "different-serial-group-job",
   736  						},
   737  						{
   738  							Name: "job-1",
   739  						},
   740  						{
   741  							Name: "job-2",
   742  						},
   743  					},
   744  					Resources: atc.ResourceConfigs{
   745  						{
   746  							Name: "some-resource",
   747  							Type: "some-type",
   748  						},
   749  						{
   750  							Name: "some-other-resource",
   751  							Type: "some-type",
   752  						},
   753  					},
   754  				}, pipeline.ConfigVersion(), false)
   755  				Expect(err).ToNot(HaveOccurred())
   756  			})
   757  		}
   758  
   759  		saveSerialGroupsPipeline := func() {
   760  			BeforeEach(func() {
   761  				var err error
   762  				pipeline, _, err = team.SavePipeline(atc.PipelineRef{Name: "fake-pipeline"}, atc.Config{
   763  					Jobs: atc.JobConfigs{
   764  						{
   765  							Name: "some-job",
   766  
   767  							Public: true,
   768  
   769  							Serial: true,
   770  
   771  							SerialGroups: []string{"serial-group"},
   772  
   773  							RawMaxInFlight: 2,
   774  
   775  							PlanSequence: []atc.Step{
   776  								{
   777  									Config: &atc.PutStep{
   778  										Name: "some-resource",
   779  										Params: atc.Params{
   780  											"some-param": "some-value",
   781  										},
   782  									},
   783  								},
   784  								{
   785  									Config: &atc.GetStep{
   786  										Name:     "some-input",
   787  										Resource: "some-resource",
   788  										Params: atc.Params{
   789  											"some-param": "some-value",
   790  										},
   791  										Passed:  []string{"job-1", "job-2"},
   792  										Trigger: true,
   793  									},
   794  								},
   795  								{
   796  									Config: &atc.TaskStep{
   797  										Name:       "some-task",
   798  										Privileged: true,
   799  										ConfigPath: "some/config/path.yml",
   800  										Config: &atc.TaskConfig{
   801  											RootfsURI: "some-image",
   802  										},
   803  									},
   804  								},
   805  							},
   806  						},
   807  						{
   808  							Name: "some-other-job",
   809  						},
   810  						{
   811  							Name:   "some-private-job",
   812  							Public: false,
   813  						},
   814  						{
   815  							Name:         "other-serial-group-job",
   816  							SerialGroups: []string{"serial-group", "really-different-group"},
   817  						},
   818  						{
   819  							Name:         "different-serial-group-job",
   820  							SerialGroups: []string{"different-serial-group"},
   821  						},
   822  						{
   823  							Name: "job-1",
   824  						},
   825  						{
   826  							Name: "job-2",
   827  						},
   828  					},
   829  					Resources: atc.ResourceConfigs{
   830  						{
   831  							Name: "some-resource",
   832  							Type: "some-type",
   833  						},
   834  						{
   835  							Name: "some-other-resource",
   836  							Type: "some-type",
   837  						},
   838  					},
   839  				}, pipeline.ConfigVersion(), false)
   840  				Expect(err).ToNot(HaveOccurred())
   841  			})
   842  		}
   843  
   844  		JustBeforeEach(func() {
   845  			var found bool
   846  			var err error
   847  			job, found, err = pipeline.Job("some-job")
   848  			Expect(err).ToNot(HaveOccurred())
   849  			Expect(found).To(BeTrue())
   850  
   851  			scheduleFound, schedulingErr = job.ScheduleBuild(schedulingBuild)
   852  
   853  			reloadFound, err = schedulingBuild.Reload()
   854  			Expect(err).ToNot(HaveOccurred())
   855  		})
   856  
   857  		Context("when the scheduling build is created first", func() {
   858  			BeforeEach(func() {
   859  				var err error
   860  				schedulingBuild, err = job.CreateBuild()
   861  				Expect(err).ToNot(HaveOccurred())
   862  			})
   863  
   864  			Context("when the job config doesn't specify max in flight", func() {
   865  				BeforeEach(func() {
   866  					var created bool
   867  					var err error
   868  					pipeline, created, err = team.SavePipeline(atc.PipelineRef{Name: "other-pipeline"}, atc.Config{
   869  						Jobs: atc.JobConfigs{
   870  							{
   871  								Name: "some-job",
   872  							},
   873  						},
   874  					}, db.ConfigVersion(0), false)
   875  					Expect(err).ToNot(HaveOccurred())
   876  					Expect(created).To(BeTrue())
   877  
   878  					var found bool
   879  					job, found, err = pipeline.Job("some-job")
   880  					Expect(err).ToNot(HaveOccurred())
   881  					Expect(found).To(BeTrue())
   882  				})
   883  
   884  				It("schedules the build", func() {
   885  					Expect(schedulingErr).ToNot(HaveOccurred())
   886  					Expect(scheduleFound).To(BeTrue())
   887  				})
   888  
   889  				Context("when build exists", func() {
   890  					Context("when the pipeline is paused", func() {
   891  						BeforeEach(func() {
   892  							err := pipeline.Pause()
   893  							Expect(err).ToNot(HaveOccurred())
   894  						})
   895  
   896  						It("returns false", func() {
   897  							Expect(schedulingErr).ToNot(HaveOccurred())
   898  							Expect(scheduleFound).To(BeFalse())
   899  							Expect(reloadFound).To(BeTrue())
   900  							Expect(schedulingBuild.IsScheduled()).To(BeFalse())
   901  						})
   902  					})
   903  
   904  					Context("when the job is paused", func() {
   905  						BeforeEach(func() {
   906  							err := job.Pause()
   907  							Expect(err).ToNot(HaveOccurred())
   908  						})
   909  
   910  						It("returns false", func() {
   911  							Expect(schedulingErr).ToNot(HaveOccurred())
   912  							Expect(scheduleFound).To(BeFalse())
   913  							Expect(reloadFound).To(BeTrue())
   914  							Expect(schedulingBuild.IsScheduled()).To(BeFalse())
   915  						})
   916  					})
   917  
   918  					Context("when the pipeline and job is not paused", func() {
   919  						It("sets the build to scheduled", func() {
   920  							Expect(schedulingErr).ToNot(HaveOccurred())
   921  							Expect(scheduleFound).To(BeTrue())
   922  							Expect(reloadFound).To(BeTrue())
   923  							Expect(schedulingBuild.IsScheduled()).To(BeTrue())
   924  						})
   925  					})
   926  				})
   927  
   928  				Context("when the build does not exist", func() {
   929  					var deleteFound bool
   930  					BeforeEach(func() {
   931  						var err error
   932  						deleteFound, err = schedulingBuild.Delete()
   933  						Expect(err).ToNot(HaveOccurred())
   934  					})
   935  
   936  					It("returns false", func() {
   937  						Expect(schedulingErr).To(HaveOccurred())
   938  						Expect(scheduleFound).To(BeFalse())
   939  						Expect(reloadFound).To(BeFalse())
   940  						Expect(deleteFound).To(BeTrue())
   941  					})
   942  				})
   943  			})
   944  
   945  			Context("when the job config specifies max in flight = 2", func() {
   946  				Context("when there are 2 builds running", func() {
   947  					var startedBuild, scheduledBuild db.Build
   948  
   949  					BeforeEach(func() {
   950  						var err error
   951  						startedBuild, err = job.CreateBuild()
   952  						Expect(err).ToNot(HaveOccurred())
   953  						scheduled, err := job.ScheduleBuild(startedBuild)
   954  						Expect(err).ToNot(HaveOccurred())
   955  						Expect(scheduled).To(BeTrue())
   956  						_, err = startedBuild.Start(atc.Plan{})
   957  						Expect(err).NotTo(HaveOccurred())
   958  
   959  						scheduledBuild, err = job.CreateBuild()
   960  						Expect(err).NotTo(HaveOccurred())
   961  						scheduled, err = job.ScheduleBuild(scheduledBuild)
   962  						Expect(err).ToNot(HaveOccurred())
   963  						Expect(scheduled).To(BeTrue())
   964  						_, err = startedBuild.Start(atc.Plan{})
   965  						Expect(err).NotTo(HaveOccurred())
   966  
   967  						for _, s := range []db.BuildStatus{db.BuildStatusSucceeded, db.BuildStatusFailed, db.BuildStatusErrored, db.BuildStatusAborted} {
   968  							finishedBuild, err := job.CreateBuild()
   969  							Expect(err).NotTo(HaveOccurred())
   970  
   971  							scheduled, err = job.ScheduleBuild(finishedBuild)
   972  							Expect(err).NotTo(HaveOccurred())
   973  							Expect(scheduled).To(BeTrue())
   974  
   975  							err = finishedBuild.Finish(s)
   976  							Expect(err).NotTo(HaveOccurred())
   977  						}
   978  
   979  						otherJob, found, err := pipeline.Job("some-other-job")
   980  						Expect(err).NotTo(HaveOccurred())
   981  						Expect(found).To(BeTrue())
   982  
   983  						_, err = otherJob.CreateBuild()
   984  						Expect(err).NotTo(HaveOccurred())
   985  					})
   986  
   987  					saveMaxInFlightPipeline()
   988  
   989  					It("returns max in flight reached so it does not schedule", func() {
   990  						Expect(schedulingErr).ToNot(HaveOccurred())
   991  						Expect(scheduleFound).To(BeFalse())
   992  						Expect(reloadFound).To(BeTrue())
   993  					})
   994  				})
   995  
   996  				Context("when there is 1 build running", func() {
   997  					BeforeEach(func() {
   998  						startedBuild, err := job.CreateBuild()
   999  						Expect(err).NotTo(HaveOccurred())
  1000  						scheduled, err := job.ScheduleBuild(startedBuild)
  1001  						Expect(err).NotTo(HaveOccurred())
  1002  						Expect(scheduled).To(BeTrue())
  1003  						_, err = startedBuild.Start(atc.Plan{})
  1004  						Expect(err).NotTo(HaveOccurred())
  1005  
  1006  						for _, s := range []db.BuildStatus{db.BuildStatusSucceeded, db.BuildStatusFailed, db.BuildStatusErrored, db.BuildStatusAborted} {
  1007  							finishedBuild, err := job.CreateBuild()
  1008  							Expect(err).NotTo(HaveOccurred())
  1009  
  1010  							scheduled, err = job.ScheduleBuild(finishedBuild)
  1011  							Expect(err).NotTo(HaveOccurred())
  1012  							Expect(scheduled).To(BeTrue())
  1013  
  1014  							err = finishedBuild.Finish(s)
  1015  							Expect(err).NotTo(HaveOccurred())
  1016  						}
  1017  
  1018  						err = job.SaveNextInputMapping(nil, true)
  1019  						Expect(err).NotTo(HaveOccurred())
  1020  					})
  1021  
  1022  					saveMaxInFlightPipeline()
  1023  
  1024  					It("schedules the build", func() {
  1025  						Expect(schedulingErr).ToNot(HaveOccurred())
  1026  						Expect(scheduleFound).To(BeTrue())
  1027  						Expect(reloadFound).To(BeTrue())
  1028  					})
  1029  				})
  1030  			})
  1031  
  1032  			Context("when the job is in serial groups", func() {
  1033  				Context("when multiple jobs in the serial group is running", func() {
  1034  					BeforeEach(func() {
  1035  						var err error
  1036  						_, err = job.CreateBuild()
  1037  						Expect(err).NotTo(HaveOccurred())
  1038  
  1039  						otherSerialJob, found, err := pipeline.Job("other-serial-group-job")
  1040  						Expect(err).NotTo(HaveOccurred())
  1041  						Expect(found).To(BeTrue())
  1042  
  1043  						serialGroupBuild, err := otherSerialJob.CreateBuild()
  1044  						Expect(err).NotTo(HaveOccurred())
  1045  
  1046  						scheduled, err := otherSerialJob.ScheduleBuild(serialGroupBuild)
  1047  						Expect(err).NotTo(HaveOccurred())
  1048  						Expect(scheduled).To(BeTrue())
  1049  
  1050  						differentSerialJob, found, err := pipeline.Job("different-serial-group-job")
  1051  						Expect(err).NotTo(HaveOccurred())
  1052  						Expect(found).To(BeTrue())
  1053  
  1054  						differentSerialGroupBuild, err := differentSerialJob.CreateBuild()
  1055  						Expect(err).NotTo(HaveOccurred())
  1056  
  1057  						scheduled, err = differentSerialJob.ScheduleBuild(differentSerialGroupBuild)
  1058  						Expect(err).NotTo(HaveOccurred())
  1059  						Expect(scheduled).To(BeTrue())
  1060  					})
  1061  
  1062  					saveSerialGroupsPipeline()
  1063  
  1064  					It("does not schedule the build", func() {
  1065  						Expect(schedulingErr).ToNot(HaveOccurred())
  1066  						Expect(scheduleFound).To(BeFalse())
  1067  					})
  1068  				})
  1069  
  1070  				Context("when no jobs in the serial groups are running", func() {
  1071  					BeforeEach(func() {
  1072  						otherSerialJob, found, err := pipeline.Job("other-serial-group-job")
  1073  						Expect(err).NotTo(HaveOccurred())
  1074  						Expect(found).To(BeTrue())
  1075  
  1076  						serialGroupBuild, err := otherSerialJob.CreateBuild()
  1077  						Expect(err).NotTo(HaveOccurred())
  1078  
  1079  						scheduled, err := otherSerialJob.ScheduleBuild(serialGroupBuild)
  1080  						Expect(err).NotTo(HaveOccurred())
  1081  						Expect(scheduled).To(BeTrue())
  1082  
  1083  						err = serialGroupBuild.Finish(db.BuildStatusSucceeded)
  1084  						Expect(err).NotTo(HaveOccurred())
  1085  
  1086  						differentSerialJob, found, err := pipeline.Job("different-serial-group-job")
  1087  						Expect(err).NotTo(HaveOccurred())
  1088  						Expect(found).To(BeTrue())
  1089  
  1090  						differentSerialGroupBuild, err := differentSerialJob.CreateBuild()
  1091  						Expect(err).NotTo(HaveOccurred())
  1092  
  1093  						scheduled, err = differentSerialJob.ScheduleBuild(differentSerialGroupBuild)
  1094  						Expect(err).NotTo(HaveOccurred())
  1095  						Expect(scheduled).To(BeTrue())
  1096  
  1097  						err = job.SaveNextInputMapping(nil, true)
  1098  						Expect(err).NotTo(HaveOccurred())
  1099  					})
  1100  
  1101  					saveSerialGroupsPipeline()
  1102  
  1103  					It("does schedule the build", func() {
  1104  						Expect(schedulingErr).ToNot(HaveOccurred())
  1105  						Expect(scheduleFound).To(BeTrue())
  1106  						Expect(reloadFound).To(BeTrue())
  1107  					})
  1108  				})
  1109  			})
  1110  		})
  1111  
  1112  		Context("when the scheduling build is not the first one created (with serial groups)", func() {
  1113  			Context("when the scheduling build has inputs determined as false", func() {
  1114  				BeforeEach(func() {
  1115  					var err error
  1116  					schedulingBuild, err = job.CreateBuild()
  1117  					Expect(err).NotTo(HaveOccurred())
  1118  
  1119  					err = job.SaveNextInputMapping(nil, false)
  1120  					Expect(err).NotTo(HaveOccurred())
  1121  				})
  1122  
  1123  				saveSerialGroupsPipeline()
  1124  
  1125  				It("does not schedule because the inputs determined is false", func() {
  1126  					Expect(schedulingErr).ToNot(HaveOccurred())
  1127  					Expect(scheduleFound).To(BeFalse())
  1128  					Expect(reloadFound).To(BeTrue())
  1129  				})
  1130  			})
  1131  
  1132  			Context("when another build within the serial group is scheduled first", func() {
  1133  				BeforeEach(func() {
  1134  					otherSerialJob, found, err := pipeline.Job("other-serial-group-job")
  1135  					Expect(err).NotTo(HaveOccurred())
  1136  					Expect(found).To(BeTrue())
  1137  
  1138  					_, err = otherSerialJob.CreateBuild()
  1139  					Expect(err).NotTo(HaveOccurred())
  1140  
  1141  					err = otherSerialJob.SaveNextInputMapping(nil, true)
  1142  					Expect(err).NotTo(HaveOccurred())
  1143  
  1144  					schedulingBuild, err = job.CreateBuild()
  1145  					Expect(err).NotTo(HaveOccurred())
  1146  
  1147  					err = job.SaveNextInputMapping(nil, true)
  1148  					Expect(err).NotTo(HaveOccurred())
  1149  				})
  1150  
  1151  				saveSerialGroupsPipeline()
  1152  
  1153  				It("does not schedule because the build we are trying to schedule is not the next most pending build in the serial group", func() {
  1154  					Expect(schedulingErr).ToNot(HaveOccurred())
  1155  					Expect(scheduleFound).To(BeFalse())
  1156  					Expect(reloadFound).To(BeTrue())
  1157  				})
  1158  			})
  1159  
  1160  			Context("when the scheduling build has it's inputs determined and created earlier", func() {
  1161  				BeforeEach(func() {
  1162  					var err error
  1163  					schedulingBuild, err = job.CreateBuild()
  1164  					Expect(err).NotTo(HaveOccurred())
  1165  
  1166  					otherSerialJob, found, err := pipeline.Job("other-serial-group-job")
  1167  					Expect(err).NotTo(HaveOccurred())
  1168  					Expect(found).To(BeTrue())
  1169  
  1170  					_, err = otherSerialJob.CreateBuild()
  1171  					Expect(err).NotTo(HaveOccurred())
  1172  
  1173  					err = job.SaveNextInputMapping(nil, true)
  1174  					Expect(err).NotTo(HaveOccurred())
  1175  					err = otherSerialJob.SaveNextInputMapping(nil, true)
  1176  					Expect(err).NotTo(HaveOccurred())
  1177  				})
  1178  
  1179  				saveSerialGroupsPipeline()
  1180  
  1181  				It("does schedule the build", func() {
  1182  					Expect(schedulingErr).ToNot(HaveOccurred())
  1183  					Expect(scheduleFound).To(BeTrue())
  1184  					Expect(reloadFound).To(BeTrue())
  1185  				})
  1186  			})
  1187  
  1188  			Context("when the job is paused but has inputs determined", func() {
  1189  				BeforeEach(func() {
  1190  					var err error
  1191  					schedulingBuild, err = job.CreateBuild()
  1192  					Expect(err).NotTo(HaveOccurred())
  1193  
  1194  					otherSerialJob, found, err := pipeline.Job("other-serial-group-job")
  1195  					Expect(err).NotTo(HaveOccurred())
  1196  					Expect(found).To(BeTrue())
  1197  
  1198  					_, err = otherSerialJob.CreateBuild()
  1199  					Expect(err).NotTo(HaveOccurred())
  1200  
  1201  					err = job.SaveNextInputMapping(nil, true)
  1202  					Expect(err).NotTo(HaveOccurred())
  1203  					err = otherSerialJob.SaveNextInputMapping(nil, true)
  1204  					Expect(err).NotTo(HaveOccurred())
  1205  
  1206  					err = job.Pause()
  1207  					Expect(err).NotTo(HaveOccurred())
  1208  				})
  1209  
  1210  				saveSerialGroupsPipeline()
  1211  
  1212  				It("does not schedule the build", func() {
  1213  					Expect(schedulingErr).ToNot(HaveOccurred())
  1214  					Expect(scheduleFound).To(BeFalse())
  1215  					Expect(reloadFound).To(BeTrue())
  1216  				})
  1217  			})
  1218  
  1219  			Context("when there are other succeeded builds within the same serial group", func() {
  1220  				BeforeEach(func() {
  1221  					otherSerialJob, found, err := pipeline.Job("other-serial-group-job")
  1222  					Expect(err).NotTo(HaveOccurred())
  1223  					Expect(found).To(BeTrue())
  1224  
  1225  					succeededBuild, err := otherSerialJob.CreateBuild()
  1226  					Expect(err).NotTo(HaveOccurred())
  1227  
  1228  					err = succeededBuild.Finish(db.BuildStatusSucceeded)
  1229  					Expect(err).NotTo(HaveOccurred())
  1230  
  1231  					err = job.SaveNextInputMapping(nil, true)
  1232  					Expect(err).NotTo(HaveOccurred())
  1233  					err = otherSerialJob.SaveNextInputMapping(nil, true)
  1234  					Expect(err).NotTo(HaveOccurred())
  1235  
  1236  					schedulingBuild, err = job.CreateBuild()
  1237  					Expect(err).NotTo(HaveOccurred())
  1238  				})
  1239  
  1240  				saveSerialGroupsPipeline()
  1241  
  1242  				It("does schedule builds because we only care about running or pending builds", func() {
  1243  					Expect(schedulingErr).ToNot(HaveOccurred())
  1244  					Expect(scheduleFound).To(BeTrue())
  1245  					Expect(reloadFound).To(BeTrue())
  1246  				})
  1247  			})
  1248  
  1249  			Context("when the job we are trying to schedule has multiple serial groups", func() {
  1250  				BeforeEach(func() {
  1251  					otherSerialJob, found, err := pipeline.Job("some-job")
  1252  					Expect(err).NotTo(HaveOccurred())
  1253  					Expect(found).To(BeTrue())
  1254  
  1255  					_, err = otherSerialJob.CreateBuild()
  1256  					Expect(err).NotTo(HaveOccurred())
  1257  
  1258  					job, found, err = pipeline.Job("other-serial-group-job")
  1259  					Expect(err).NotTo(HaveOccurred())
  1260  					Expect(found).To(BeTrue())
  1261  
  1262  					schedulingBuild, err = job.CreateBuild()
  1263  					Expect(err).NotTo(HaveOccurred())
  1264  
  1265  					err = job.SaveNextInputMapping(nil, true)
  1266  					Expect(err).NotTo(HaveOccurred())
  1267  					err = otherSerialJob.SaveNextInputMapping(nil, true)
  1268  					Expect(err).NotTo(HaveOccurred())
  1269  				})
  1270  
  1271  				saveSerialGroupsPipeline()
  1272  
  1273  				It("does not schedule a build because the a build within one of the serial groups was created earlier", func() {
  1274  					Expect(schedulingErr).ToNot(HaveOccurred())
  1275  					Expect(scheduleFound).To(BeFalse())
  1276  					Expect(reloadFound).To(BeTrue())
  1277  				})
  1278  			})
  1279  		})
  1280  	})
  1281  
  1282  	Describe("GetNextBuildInputs", func() {
  1283  		var (
  1284  			versions    []atc.ResourceVersion
  1285  			spanContext db.SpanContext
  1286  			scenario    *dbtest.Scenario
  1287  		)
  1288  
  1289  		BeforeEach(func() {
  1290  			spanContext = db.SpanContext{"fake": "version"}
  1291  
  1292  			scenario = dbtest.Setup(
  1293  				builder.WithPipeline(atc.Config{
  1294  					Jobs: atc.JobConfigs{
  1295  						{
  1296  							Name: "some-job",
  1297  							PlanSequence: []atc.Step{
  1298  								{
  1299  									Config: &atc.GetStep{
  1300  										Name:     "some-input",
  1301  										Resource: "some-resource",
  1302  										Passed:   []string{"job-1", "job-2"},
  1303  										Trigger:  true,
  1304  									},
  1305  								},
  1306  								{
  1307  									Config: &atc.GetStep{
  1308  										Name:     "some-input-2",
  1309  										Resource: "some-resource",
  1310  										Passed:   []string{"job-1"},
  1311  										Trigger:  true,
  1312  									},
  1313  								},
  1314  								{
  1315  									Config: &atc.GetStep{
  1316  										Name:     "some-input-3",
  1317  										Resource: "some-resource",
  1318  										Trigger:  true,
  1319  									},
  1320  								},
  1321  							},
  1322  						},
  1323  						{
  1324  							Name: "job-1",
  1325  						},
  1326  						{
  1327  							Name: "job-2",
  1328  						},
  1329  					},
  1330  					Resources: atc.ResourceConfigs{
  1331  						{
  1332  							Name: "some-resource",
  1333  							Type: "some-base-resource-type",
  1334  						},
  1335  					},
  1336  				}),
  1337  				builder.WithSpanContext(spanContext),
  1338  				builder.WithResourceVersions(
  1339  					"some-resource",
  1340  					atc.Version{"version": "v1"},
  1341  					atc.Version{"version": "v2"},
  1342  					atc.Version{"version": "v3"},
  1343  				),
  1344  			)
  1345  
  1346  			reversions, _, found, err := scenario.Resource("some-resource").Versions(db.Page{Limit: 3}, nil)
  1347  			Expect(err).NotTo(HaveOccurred())
  1348  			Expect(found).To(BeTrue())
  1349  
  1350  			versions = []atc.ResourceVersion{reversions[2], reversions[1], reversions[0]}
  1351  		})
  1352  
  1353  		Describe("partial next build inputs", func() {
  1354  			It("gets partial next build inputs for the given job name", func() {
  1355  				inputVersions := db.InputMapping{
  1356  					"some-input-2": db.InputResult{
  1357  						ResolveError: "disaster",
  1358  					},
  1359  				}
  1360  
  1361  				err := scenario.Job("some-job").SaveNextInputMapping(inputVersions, false)
  1362  				Expect(err).NotTo(HaveOccurred())
  1363  
  1364  				buildInputs := []db.BuildInput{
  1365  					{
  1366  						Name:         "some-input-2",
  1367  						ResolveError: "disaster",
  1368  					},
  1369  				}
  1370  
  1371  				actualBuildInputs, err := scenario.Job("some-job").GetNextBuildInputs()
  1372  				Expect(err).NotTo(HaveOccurred())
  1373  
  1374  				Expect(actualBuildInputs).To(ConsistOf(buildInputs))
  1375  			})
  1376  
  1377  			It("gets full next build inputs for the given job name", func() {
  1378  				inputVersions := db.InputMapping{
  1379  					"some-input-1": db.InputResult{
  1380  						Input: &db.AlgorithmInput{
  1381  							AlgorithmVersion: db.AlgorithmVersion{
  1382  								Version:    db.ResourceVersion(convertToMD5(versions[0].Version)),
  1383  								ResourceID: scenario.Resource("some-resource").ID(),
  1384  							},
  1385  							FirstOccurrence: false,
  1386  						},
  1387  						PassedBuildIDs: []int{},
  1388  					},
  1389  					"some-input-2": db.InputResult{
  1390  						Input: &db.AlgorithmInput{
  1391  							AlgorithmVersion: db.AlgorithmVersion{
  1392  								Version:    db.ResourceVersion(convertToMD5(versions[1].Version)),
  1393  								ResourceID: scenario.Resource("some-resource").ID(),
  1394  							},
  1395  							FirstOccurrence: false,
  1396  						},
  1397  						PassedBuildIDs: []int{},
  1398  					},
  1399  					"some-input-3": db.InputResult{
  1400  						Input: &db.AlgorithmInput{
  1401  							AlgorithmVersion: db.AlgorithmVersion{
  1402  								Version:    db.ResourceVersion(convertToMD5(versions[2].Version)),
  1403  								ResourceID: scenario.Resource("some-resource").ID(),
  1404  							},
  1405  							FirstOccurrence: false,
  1406  						},
  1407  						PassedBuildIDs: []int{},
  1408  					},
  1409  				}
  1410  
  1411  				err := scenario.Job("some-job").SaveNextInputMapping(inputVersions, true)
  1412  				Expect(err).NotTo(HaveOccurred())
  1413  
  1414  				buildInputs := []db.BuildInput{
  1415  					{
  1416  						Name:            "some-input-1",
  1417  						ResourceID:      scenario.Resource("some-resource").ID(),
  1418  						Version:         atc.Version{"version": "v1"},
  1419  						FirstOccurrence: false,
  1420  						Context:         spanContext,
  1421  					},
  1422  					{
  1423  						Name:            "some-input-2",
  1424  						ResourceID:      scenario.Resource("some-resource").ID(),
  1425  						Version:         atc.Version{"version": "v2"},
  1426  						FirstOccurrence: false,
  1427  						Context:         spanContext,
  1428  					},
  1429  					{
  1430  						Name:            "some-input-3",
  1431  						ResourceID:      scenario.Resource("some-resource").ID(),
  1432  						Version:         atc.Version{"version": "v3"},
  1433  						FirstOccurrence: false,
  1434  						Context:         spanContext,
  1435  					},
  1436  				}
  1437  
  1438  				actualBuildInputs, err := scenario.Job("some-job").GetNextBuildInputs()
  1439  				Expect(err).NotTo(HaveOccurred())
  1440  
  1441  				Expect(actualBuildInputs).To(ConsistOf(buildInputs))
  1442  			})
  1443  		})
  1444  	})
  1445  
  1446  	Describe("GetFullNextBuildInputs", func() {
  1447  		var (
  1448  			versions          []atc.ResourceVersion
  1449  			scenarioPipeline1 *dbtest.Scenario
  1450  			scenarioPipeline2 *dbtest.Scenario
  1451  		)
  1452  
  1453  		BeforeEach(func() {
  1454  			scenarioPipeline1 = dbtest.Setup(
  1455  				builder.WithPipeline(atc.Config{
  1456  					Jobs: atc.JobConfigs{
  1457  						{
  1458  							Name: "some-job",
  1459  							PlanSequence: []atc.Step{
  1460  								{
  1461  									Config: &atc.GetStep{
  1462  										Name:     "some-input",
  1463  										Resource: "some-resource",
  1464  									},
  1465  								},
  1466  							},
  1467  						},
  1468  					},
  1469  					Resources: atc.ResourceConfigs{
  1470  						{
  1471  							Name: "some-resource",
  1472  							Type: "some-base-resource-type",
  1473  						},
  1474  					},
  1475  				}),
  1476  				builder.WithResourceVersions(
  1477  					"some-resource",
  1478  					atc.Version{"version": "v1"},
  1479  					atc.Version{"version": "v2"},
  1480  					atc.Version{"version": "v3"},
  1481  				),
  1482  				builder.WithVersionMetadata("some-resource", atc.Version{"version": "v1"}, db.ResourceConfigMetadataFields{
  1483  					db.ResourceConfigMetadataField{
  1484  						Name:  "name1",
  1485  						Value: "value1",
  1486  					},
  1487  				}),
  1488  			)
  1489  
  1490  			reversions, _, found, err := scenarioPipeline1.Resource("some-resource").Versions(db.Page{Limit: 3}, nil)
  1491  			Expect(err).NotTo(HaveOccurred())
  1492  			Expect(found).To(BeTrue())
  1493  
  1494  			versions = []atc.ResourceVersion{reversions[2], reversions[1], reversions[0]}
  1495  
  1496  			scenarioPipeline2 = dbtest.Setup(
  1497  				builder.WithPipeline(atc.Config{
  1498  					Jobs: atc.JobConfigs{
  1499  						{
  1500  							Name: "some-job",
  1501  						},
  1502  						{
  1503  							Name: "some-other-job",
  1504  						},
  1505  					},
  1506  					Resources: atc.ResourceConfigs{
  1507  						{
  1508  							Name: "some-resource",
  1509  							Type: "some-type",
  1510  						},
  1511  					},
  1512  				}),
  1513  			)
  1514  		})
  1515  
  1516  		It("gets next build inputs for the given job name", func() {
  1517  			inputVersions := db.InputMapping{
  1518  				"some-input-1": db.InputResult{
  1519  					Input: &db.AlgorithmInput{
  1520  						AlgorithmVersion: db.AlgorithmVersion{
  1521  							Version:    db.ResourceVersion(convertToMD5(versions[0].Version)),
  1522  							ResourceID: scenarioPipeline1.Resource("some-resource").ID(),
  1523  						},
  1524  						FirstOccurrence: false,
  1525  					},
  1526  					PassedBuildIDs: []int{},
  1527  				},
  1528  				"some-input-2": db.InputResult{
  1529  					Input: &db.AlgorithmInput{
  1530  						AlgorithmVersion: db.AlgorithmVersion{
  1531  							Version:    db.ResourceVersion(convertToMD5(versions[1].Version)),
  1532  							ResourceID: scenarioPipeline1.Resource("some-resource").ID(),
  1533  						},
  1534  						FirstOccurrence: true,
  1535  					},
  1536  					PassedBuildIDs: []int{},
  1537  				},
  1538  			}
  1539  			err := scenarioPipeline1.Job("some-job").SaveNextInputMapping(inputVersions, true)
  1540  			Expect(err).NotTo(HaveOccurred())
  1541  
  1542  			pipeline2InputVersions := db.InputMapping{
  1543  				"some-input-3": db.InputResult{
  1544  					Input: &db.AlgorithmInput{
  1545  						AlgorithmVersion: db.AlgorithmVersion{
  1546  							Version:    db.ResourceVersion(convertToMD5(versions[2].Version)),
  1547  							ResourceID: scenarioPipeline2.Resource("some-resource").ID(),
  1548  						},
  1549  						FirstOccurrence: false,
  1550  					},
  1551  					PassedBuildIDs: []int{},
  1552  				},
  1553  			}
  1554  			err = scenarioPipeline2.Job("some-job").SaveNextInputMapping(pipeline2InputVersions, true)
  1555  			Expect(err).NotTo(HaveOccurred())
  1556  
  1557  			buildInputs := []db.BuildInput{
  1558  				{
  1559  					Name:            "some-input-1",
  1560  					ResourceID:      scenarioPipeline1.Resource("some-resource").ID(),
  1561  					Version:         atc.Version{"version": "v1"},
  1562  					FirstOccurrence: false,
  1563  				},
  1564  				{
  1565  					Name:            "some-input-2",
  1566  					ResourceID:      scenarioPipeline1.Resource("some-resource").ID(),
  1567  					Version:         atc.Version{"version": "v2"},
  1568  					FirstOccurrence: true,
  1569  				},
  1570  			}
  1571  
  1572  			actualBuildInputs, found, err := scenarioPipeline1.Job("some-job").GetFullNextBuildInputs()
  1573  			Expect(err).NotTo(HaveOccurred())
  1574  			Expect(found).To(BeTrue())
  1575  
  1576  			Expect(actualBuildInputs).To(ConsistOf(buildInputs))
  1577  
  1578  			By("updating the set of next build inputs")
  1579  			inputVersions2 := db.InputMapping{
  1580  				"some-input-2": db.InputResult{
  1581  					Input: &db.AlgorithmInput{
  1582  						AlgorithmVersion: db.AlgorithmVersion{
  1583  							Version:    db.ResourceVersion(convertToMD5(versions[2].Version)),
  1584  							ResourceID: scenarioPipeline1.Resource("some-resource").ID(),
  1585  						},
  1586  						FirstOccurrence: false,
  1587  					},
  1588  					PassedBuildIDs: []int{},
  1589  				},
  1590  				"some-input-3": db.InputResult{
  1591  					Input: &db.AlgorithmInput{
  1592  						AlgorithmVersion: db.AlgorithmVersion{
  1593  							Version:    db.ResourceVersion(convertToMD5(versions[2].Version)),
  1594  							ResourceID: scenarioPipeline1.Resource("some-resource").ID(),
  1595  						},
  1596  						FirstOccurrence: true,
  1597  					},
  1598  					PassedBuildIDs: []int{},
  1599  				},
  1600  			}
  1601  			err = scenarioPipeline1.Job("some-job").SaveNextInputMapping(inputVersions2, true)
  1602  			Expect(err).NotTo(HaveOccurred())
  1603  
  1604  			buildInputs2 := []db.BuildInput{
  1605  				{
  1606  					Name:            "some-input-2",
  1607  					ResourceID:      scenarioPipeline1.Resource("some-resource").ID(),
  1608  					Version:         atc.Version{"version": "v3"},
  1609  					FirstOccurrence: false,
  1610  				},
  1611  				{
  1612  					Name:            "some-input-3",
  1613  					ResourceID:      scenarioPipeline1.Resource("some-resource").ID(),
  1614  					Version:         atc.Version{"version": "v3"},
  1615  					FirstOccurrence: true,
  1616  				},
  1617  			}
  1618  
  1619  			actualBuildInputs2, found, err := scenarioPipeline1.Job("some-job").GetFullNextBuildInputs()
  1620  			Expect(err).NotTo(HaveOccurred())
  1621  			Expect(found).To(BeTrue())
  1622  
  1623  			Expect(actualBuildInputs2).To(ConsistOf(buildInputs2))
  1624  
  1625  			By("updating next build inputs to an empty set when the mapping is nil")
  1626  			err = scenarioPipeline1.Job("some-job").SaveNextInputMapping(nil, true)
  1627  			Expect(err).NotTo(HaveOccurred())
  1628  
  1629  			actualBuildInputs3, found, err := scenarioPipeline1.Job("some-job").GetFullNextBuildInputs()
  1630  			Expect(err).NotTo(HaveOccurred())
  1631  			Expect(found).To(BeTrue())
  1632  			Expect(actualBuildInputs3).To(BeEmpty())
  1633  		})
  1634  
  1635  		It("distinguishes between a job with no inputs and a job with missing inputs", func() {
  1636  			By("initially returning not found")
  1637  			_, found, err := scenarioPipeline1.Job("some-job").GetFullNextBuildInputs()
  1638  			Expect(err).NotTo(HaveOccurred())
  1639  			Expect(found).To(BeFalse())
  1640  
  1641  			By("returning found when an empty input mapping is saved")
  1642  			err = scenarioPipeline1.Job("some-job").SaveNextInputMapping(db.InputMapping{}, true)
  1643  			Expect(err).NotTo(HaveOccurred())
  1644  
  1645  			_, found, err = scenarioPipeline1.Job("some-job").GetFullNextBuildInputs()
  1646  			Expect(err).NotTo(HaveOccurred())
  1647  			Expect(found).To(BeTrue())
  1648  		})
  1649  
  1650  		It("does not grab inputs if inputs were not successfully determined", func() {
  1651  			inputVersions := db.InputMapping{
  1652  				"some-input-1": db.InputResult{
  1653  					ResolveError: "disaster",
  1654  				},
  1655  			}
  1656  			err := scenarioPipeline1.Job("some-job").SaveNextInputMapping(inputVersions, false)
  1657  			Expect(err).NotTo(HaveOccurred())
  1658  
  1659  			_, found, err := scenarioPipeline1.Job("some-job").GetFullNextBuildInputs()
  1660  			Expect(err).NotTo(HaveOccurred())
  1661  			Expect(found).To(BeFalse())
  1662  		})
  1663  	})
  1664  
  1665  	Describe("a build is created for a job", func() {
  1666  		var (
  1667  			build1DB      db.Build
  1668  			otherPipeline db.Pipeline
  1669  			otherJob      db.Job
  1670  		)
  1671  
  1672  		BeforeEach(func() {
  1673  			pipelineConfig := atc.Config{
  1674  				Jobs: atc.JobConfigs{
  1675  					{
  1676  						Name: "some-job",
  1677  					},
  1678  				},
  1679  				Resources: atc.ResourceConfigs{
  1680  					{
  1681  						Name: "some-other-resource",
  1682  						Type: "some-type",
  1683  					},
  1684  				},
  1685  			}
  1686  			var err error
  1687  			otherPipeline, _, err = team.SavePipeline(atc.PipelineRef{Name: "some-other-pipeline"}, pipelineConfig, db.ConfigVersion(1), false)
  1688  			Expect(err).ToNot(HaveOccurred())
  1689  
  1690  			build1DB, err = job.CreateBuild()
  1691  			Expect(err).ToNot(HaveOccurred())
  1692  
  1693  			Expect(build1DB.ID()).NotTo(BeZero())
  1694  			Expect(build1DB.JobName()).To(Equal("some-job"))
  1695  			Expect(build1DB.Name()).To(Equal("1"))
  1696  			Expect(build1DB.Status()).To(Equal(db.BuildStatusPending))
  1697  			Expect(build1DB.IsScheduled()).To(BeFalse())
  1698  
  1699  			var found bool
  1700  			otherJob, found, err = otherPipeline.Job("some-job")
  1701  			Expect(err).ToNot(HaveOccurred())
  1702  			Expect(found).To(BeTrue())
  1703  		})
  1704  
  1705  		It("becomes the next pending build for job", func() {
  1706  			nextPendings, err := job.GetPendingBuilds()
  1707  			Expect(err).NotTo(HaveOccurred())
  1708  			//time.Sleep(10 * time.Hour)
  1709  			Expect(nextPendings).NotTo(BeEmpty())
  1710  			Expect(nextPendings[0].ID()).To(Equal(build1DB.ID()))
  1711  		})
  1712  
  1713  		Context("and another build for a different pipeline is created with the same job name", func() {
  1714  			BeforeEach(func() {
  1715  				otherBuild, err := otherJob.CreateBuild()
  1716  				Expect(err).NotTo(HaveOccurred())
  1717  
  1718  				Expect(otherBuild.ID()).NotTo(BeZero())
  1719  				Expect(otherBuild.JobName()).To(Equal("some-job"))
  1720  				Expect(otherBuild.Name()).To(Equal("1"))
  1721  				Expect(otherBuild.Status()).To(Equal(db.BuildStatusPending))
  1722  				Expect(otherBuild.IsScheduled()).To(BeFalse())
  1723  			})
  1724  
  1725  			It("does not change the next pending build for job", func() {
  1726  				nextPendingBuilds, err := job.GetPendingBuilds()
  1727  				Expect(err).NotTo(HaveOccurred())
  1728  				Expect(nextPendingBuilds).To(Equal([]db.Build{build1DB}))
  1729  			})
  1730  		})
  1731  
  1732  		Context("when scheduled", func() {
  1733  			BeforeEach(func() {
  1734  				var err error
  1735  				var found bool
  1736  				found, err = job.ScheduleBuild(build1DB)
  1737  				Expect(err).NotTo(HaveOccurred())
  1738  				Expect(found).To(BeTrue())
  1739  			})
  1740  
  1741  			It("remains the next pending build for job", func() {
  1742  				nextPendingBuilds, err := job.GetPendingBuilds()
  1743  				Expect(err).NotTo(HaveOccurred())
  1744  				Expect(nextPendingBuilds).NotTo(BeEmpty())
  1745  				Expect(nextPendingBuilds[0].ID()).To(Equal(build1DB.ID()))
  1746  			})
  1747  		})
  1748  
  1749  		Context("when started", func() {
  1750  			BeforeEach(func() {
  1751  				started, err := build1DB.Start(atc.Plan{ID: "some-id"})
  1752  				Expect(err).NotTo(HaveOccurred())
  1753  				Expect(started).To(BeTrue())
  1754  			})
  1755  
  1756  			It("saves the updated status, and the schema and private plan", func() {
  1757  				found, err := build1DB.Reload()
  1758  				Expect(err).NotTo(HaveOccurred())
  1759  				Expect(found).To(BeTrue())
  1760  				Expect(build1DB.Status()).To(Equal(db.BuildStatusStarted))
  1761  				Expect(build1DB.Schema()).To(Equal("exec.v2"))
  1762  				Expect(build1DB.PrivatePlan()).To(Equal(atc.Plan{ID: "some-id"}))
  1763  			})
  1764  
  1765  			It("saves the build's start time", func() {
  1766  				found, err := build1DB.Reload()
  1767  				Expect(err).NotTo(HaveOccurred())
  1768  				Expect(found).To(BeTrue())
  1769  				Expect(build1DB.StartTime().Unix()).To(BeNumerically("~", time.Now().Unix(), 3))
  1770  			})
  1771  		})
  1772  
  1773  		Context("when the build finishes", func() {
  1774  			BeforeEach(func() {
  1775  				err := build1DB.Finish(db.BuildStatusSucceeded)
  1776  				Expect(err).NotTo(HaveOccurred())
  1777  			})
  1778  
  1779  			It("sets the build's status and end time", func() {
  1780  				found, err := build1DB.Reload()
  1781  				Expect(err).NotTo(HaveOccurred())
  1782  				Expect(found).To(BeTrue())
  1783  				Expect(build1DB.Status()).To(Equal(db.BuildStatusSucceeded))
  1784  				Expect(build1DB.EndTime().Unix()).To(BeNumerically("~", time.Now().Unix(), 3))
  1785  			})
  1786  		})
  1787  
  1788  		Context("and another is created for the same job", func() {
  1789  			var build2DB db.Build
  1790  
  1791  			BeforeEach(func() {
  1792  				var err error
  1793  				build2DB, err = job.CreateBuild()
  1794  				Expect(err).NotTo(HaveOccurred())
  1795  
  1796  				Expect(build2DB.ID()).NotTo(BeZero())
  1797  				Expect(build2DB.ID()).NotTo(Equal(build1DB.ID()))
  1798  				Expect(build2DB.Name()).To(Equal("2"))
  1799  				Expect(build2DB.Status()).To(Equal(db.BuildStatusPending))
  1800  			})
  1801  
  1802  			Describe("the first build", func() {
  1803  				It("remains the next pending build", func() {
  1804  					nextPendingBuilds, err := job.GetPendingBuilds()
  1805  					Expect(err).NotTo(HaveOccurred())
  1806  					Expect(nextPendingBuilds).To(HaveLen(2))
  1807  					Expect(nextPendingBuilds[0].ID()).To(Equal(build1DB.ID()))
  1808  					Expect(nextPendingBuilds[1].ID()).To(Equal(build2DB.ID()))
  1809  				})
  1810  			})
  1811  		})
  1812  
  1813  		Context("when there is a rerun build created for an old build", func() {
  1814  			var rerunBuild db.Build
  1815  			var newBuild db.Build
  1816  			var newerBuild db.Build
  1817  
  1818  			BeforeEach(func() {
  1819  				var err error
  1820  				newBuild, err = job.CreateBuild()
  1821  				Expect(err).NotTo(HaveOccurred())
  1822  
  1823  				newerBuild, err = job.CreateBuild()
  1824  				Expect(err).NotTo(HaveOccurred())
  1825  
  1826  				err = newBuild.Finish(db.BuildStatusSucceeded)
  1827  				Expect(err).NotTo(HaveOccurred())
  1828  
  1829  				rerunBuild, err = job.RerunBuild(newBuild)
  1830  				Expect(err).NotTo(HaveOccurred())
  1831  
  1832  				Expect(rerunBuild.ID()).NotTo(BeZero())
  1833  				Expect(rerunBuild.ID()).NotTo(Equal(newBuild.ID()))
  1834  				Expect(rerunBuild.Name()).To(Equal("2.1"))
  1835  				Expect(rerunBuild.Status()).To(Equal(db.BuildStatusPending))
  1836  			})
  1837  
  1838  			It("orders the builds with regular build first and then rerun of old build", func() {
  1839  				nextPendingBuilds, err := job.GetPendingBuilds()
  1840  				Expect(err).NotTo(HaveOccurred())
  1841  				Expect(len(nextPendingBuilds)).To(Equal(3))
  1842  				Expect(nextPendingBuilds[0].Name()).To(Equal(build1DB.Name()))
  1843  				Expect(nextPendingBuilds[1].Name()).To(Equal(rerunBuild.Name()))
  1844  				Expect(nextPendingBuilds[2].Name()).To(Equal(newerBuild.Name()))
  1845  			})
  1846  		})
  1847  
  1848  		Context("when there is a rerun build created for the newest build", func() {
  1849  			var rerunBuild db.Build
  1850  			var newBuild db.Build
  1851  			var newerBuild db.Build
  1852  
  1853  			BeforeEach(func() {
  1854  				var err error
  1855  				newBuild, err = job.CreateBuild()
  1856  				Expect(err).NotTo(HaveOccurred())
  1857  
  1858  				rerunBuild, err = job.RerunBuild(newBuild)
  1859  				Expect(err).NotTo(HaveOccurred())
  1860  
  1861  				newerBuild, err = job.CreateBuild()
  1862  				Expect(err).NotTo(HaveOccurred())
  1863  
  1864  				Expect(rerunBuild.ID()).NotTo(BeZero())
  1865  				Expect(rerunBuild.ID()).NotTo(Equal(newBuild.ID()))
  1866  				Expect(rerunBuild.Name()).To(Equal("2.1"))
  1867  				Expect(rerunBuild.Status()).To(Equal(db.BuildStatusPending))
  1868  			})
  1869  
  1870  			It("orders the builds with rerun of new build", func() {
  1871  				nextPendingBuilds, err := job.GetPendingBuilds()
  1872  				Expect(err).NotTo(HaveOccurred())
  1873  				Expect(len(nextPendingBuilds)).To(Equal(4))
  1874  				Expect(nextPendingBuilds[0].ID()).To(Equal(build1DB.ID()))
  1875  				Expect(nextPendingBuilds[1].ID()).To(Equal(newBuild.ID()))
  1876  				Expect(nextPendingBuilds[2].ID()).To(Equal(rerunBuild.ID()))
  1877  				Expect(nextPendingBuilds[3].ID()).To(Equal(newerBuild.ID()))
  1878  			})
  1879  		})
  1880  
  1881  		Context("when there are multiple reruns for multiple pending builds", func() {
  1882  			var rerunBuild db.Build
  1883  			var rerunBuild2 db.Build
  1884  			var rerunBuild3 db.Build
  1885  			var newBuild db.Build
  1886  			var newerBuild db.Build
  1887  
  1888  			BeforeEach(func() {
  1889  				var err error
  1890  				newBuild, err = job.CreateBuild()
  1891  				Expect(err).NotTo(HaveOccurred())
  1892  
  1893  				newerBuild, err = job.CreateBuild()
  1894  				Expect(err).NotTo(HaveOccurred())
  1895  
  1896  				rerunBuild3, err = job.RerunBuild(newerBuild)
  1897  				Expect(err).NotTo(HaveOccurred())
  1898  
  1899  				rerunBuild, err = job.RerunBuild(newBuild)
  1900  				Expect(err).NotTo(HaveOccurred())
  1901  
  1902  				rerunBuild2, err = job.RerunBuild(rerunBuild)
  1903  				Expect(err).NotTo(HaveOccurred())
  1904  
  1905  				Expect(rerunBuild.ID()).NotTo(BeZero())
  1906  				Expect(rerunBuild.ID()).NotTo(Equal(newBuild.ID()))
  1907  				Expect(rerunBuild.Name()).To(Equal("2.1"))
  1908  				Expect(rerunBuild.Status()).To(Equal(db.BuildStatusPending))
  1909  			})
  1910  
  1911  			It("orders the builds with ascending reruns following original builds", func() {
  1912  				nextPendingBuilds, err := job.GetPendingBuilds()
  1913  				Expect(err).NotTo(HaveOccurred())
  1914  				Expect(len(nextPendingBuilds)).To(Equal(6))
  1915  				Expect(nextPendingBuilds[0].Name()).To(Equal(build1DB.Name()))
  1916  				Expect(nextPendingBuilds[1].Name()).To(Equal(newBuild.Name()))
  1917  				Expect(nextPendingBuilds[2].Name()).To(Equal(rerunBuild.Name()))
  1918  				Expect(nextPendingBuilds[3].Name()).To(Equal(rerunBuild2.Name()))
  1919  				Expect(nextPendingBuilds[4].Name()).To(Equal(newerBuild.Name()))
  1920  				Expect(nextPendingBuilds[5].Name()).To(Equal(rerunBuild3.Name()))
  1921  			})
  1922  		})
  1923  	})
  1924  
  1925  	Describe("EnsurePendingBuildExists", func() {
  1926  		Context("when only a started build exists", func() {
  1927  			It("creates a build and updates the next build for the job", func() {
  1928  				err := job.EnsurePendingBuildExists(context.TODO())
  1929  				Expect(err).NotTo(HaveOccurred())
  1930  
  1931  				pendingBuilds, err := job.GetPendingBuilds()
  1932  				Expect(err).NotTo(HaveOccurred())
  1933  				Expect(pendingBuilds).To(HaveLen(1))
  1934  
  1935  				_, nextBuild, err := job.FinishedAndNextBuild()
  1936  				Expect(err).NotTo(HaveOccurred())
  1937  				Expect(pendingBuilds[0].ID()).To(Equal(nextBuild.ID()))
  1938  			})
  1939  
  1940  			Context("when tracing is configured", func() {
  1941  				BeforeEach(func() {
  1942  					tracing.ConfigureTraceProvider(tracetest.NewProvider())
  1943  				})
  1944  
  1945  				AfterEach(func() {
  1946  					tracing.Configured = false
  1947  				})
  1948  
  1949  				It("propagates span context", func() {
  1950  					ctx, span := tracing.StartSpan(context.Background(), "fake-operation", nil)
  1951  					traceID := span.SpanContext().TraceID.String()
  1952  
  1953  					job.EnsurePendingBuildExists(ctx)
  1954  
  1955  					pendingBuilds, _ := job.GetPendingBuilds()
  1956  					spanContext := pendingBuilds[0].SpanContext()
  1957  					traceParent := spanContext.Get("traceparent")
  1958  					Expect(traceParent).To(ContainSubstring(traceID))
  1959  				})
  1960  			})
  1961  
  1962  			It("doesn't create another build the second time it's called", func() {
  1963  				err := job.EnsurePendingBuildExists(context.TODO())
  1964  				Expect(err).NotTo(HaveOccurred())
  1965  
  1966  				err = job.EnsurePendingBuildExists(context.TODO())
  1967  				Expect(err).NotTo(HaveOccurred())
  1968  
  1969  				builds2, err := job.GetPendingBuilds()
  1970  				Expect(err).NotTo(HaveOccurred())
  1971  				Expect(builds2).To(HaveLen(1))
  1972  
  1973  				started, err := builds2[0].Start(atc.Plan{})
  1974  				Expect(err).NotTo(HaveOccurred())
  1975  				Expect(started).To(BeTrue())
  1976  
  1977  				builds2, err = job.GetPendingBuilds()
  1978  				Expect(err).NotTo(HaveOccurred())
  1979  				Expect(builds2).To(HaveLen(0))
  1980  			})
  1981  		})
  1982  	})
  1983  
  1984  	Describe("Clear task cache", func() {
  1985  		Context("when task cache exists", func() {
  1986  			var (
  1987  				someOtherJob db.Job
  1988  				rowsDeleted  int64
  1989  			)
  1990  
  1991  			BeforeEach(func() {
  1992  				var (
  1993  					err   error
  1994  					found bool
  1995  				)
  1996  
  1997  				usedTaskCache, err := taskCacheFactory.FindOrCreate(job.ID(), "some-task", "some-path")
  1998  				Expect(err).ToNot(HaveOccurred())
  1999  
  2000  				_, err = workerTaskCacheFactory.FindOrCreate(db.WorkerTaskCache{
  2001  					TaskCache:  usedTaskCache,
  2002  					WorkerName: defaultWorker.Name(),
  2003  				})
  2004  				Expect(err).ToNot(HaveOccurred())
  2005  
  2006  				someOtherJob, found, err = pipeline.Job("some-other-job")
  2007  				Expect(err).NotTo(HaveOccurred())
  2008  				Expect(found).To(BeTrue())
  2009  				Expect(someOtherJob).ToNot(BeNil())
  2010  
  2011  				otherUsedTaskCache, err := taskCacheFactory.FindOrCreate(someOtherJob.ID(), "some-other-task", "some-other-path")
  2012  				Expect(err).ToNot(HaveOccurred())
  2013  
  2014  				_, err = workerTaskCacheFactory.FindOrCreate(db.WorkerTaskCache{
  2015  					TaskCache:  otherUsedTaskCache,
  2016  					WorkerName: defaultWorker.Name(),
  2017  				})
  2018  				Expect(err).ToNot(HaveOccurred())
  2019  
  2020  			})
  2021  
  2022  			Context("when a path is provided", func() {
  2023  				BeforeEach(func() {
  2024  					var err error
  2025  					rowsDeleted, err = job.ClearTaskCache("some-task", "some-path")
  2026  					Expect(err).NotTo(HaveOccurred())
  2027  				})
  2028  
  2029  				It("deletes a row from the task_caches table", func() {
  2030  					Expect(rowsDeleted).To(Equal(int64(1)))
  2031  				})
  2032  
  2033  				It("removes the task cache", func() {
  2034  					usedTaskCache, found, err := taskCacheFactory.Find(job.ID(), "some-task", "some-path")
  2035  					Expect(err).ToNot(HaveOccurred())
  2036  					Expect(usedTaskCache).To(BeNil())
  2037  					Expect(found).To(BeFalse())
  2038  				})
  2039  
  2040  				It("doesn't remove other jobs caches", func() {
  2041  					otherUsedTaskCache, found, err := taskCacheFactory.Find(someOtherJob.ID(), "some-other-task", "some-other-path")
  2042  					Expect(err).ToNot(HaveOccurred())
  2043  					Expect(found).To(BeTrue())
  2044  					Expect(err).ToNot(HaveOccurred())
  2045  
  2046  					_, err = workerTaskCacheFactory.FindOrCreate(db.WorkerTaskCache{
  2047  						TaskCache:  otherUsedTaskCache,
  2048  						WorkerName: defaultWorker.Name(),
  2049  					})
  2050  					Expect(err).ToNot(HaveOccurred())
  2051  				})
  2052  
  2053  				Context("but the cache path doesn't exist", func() {
  2054  					BeforeEach(func() {
  2055  						var err error
  2056  						rowsDeleted, err = job.ClearTaskCache("some-task", "some-nonexistent-path")
  2057  						Expect(err).NotTo(HaveOccurred())
  2058  
  2059  					})
  2060  					It("deletes 0 rows", func() {
  2061  						Expect(rowsDeleted).To(Equal(int64(0)))
  2062  					})
  2063  				})
  2064  			})
  2065  
  2066  			Context("when a path is not provided", func() {
  2067  				Context("when a non-existent step-name is provided", func() {
  2068  					BeforeEach(func() {
  2069  						var err error
  2070  						rowsDeleted, err = job.ClearTaskCache("some-nonexistent-task", "")
  2071  						Expect(err).NotTo(HaveOccurred())
  2072  					})
  2073  
  2074  					It("does not delete any rows from the task_caches table", func() {
  2075  						Expect(rowsDeleted).To(BeZero())
  2076  					})
  2077  
  2078  					It("should not delete any task steps", func() {
  2079  						usedTaskCache, found, err := taskCacheFactory.Find(job.ID(), "some-task", "some-path")
  2080  						Expect(err).ToNot(HaveOccurred())
  2081  						Expect(found).To(BeTrue())
  2082  						Expect(err).ToNot(HaveOccurred())
  2083  
  2084  						_, found, err = workerTaskCacheFactory.Find(db.WorkerTaskCache{
  2085  							TaskCache:  usedTaskCache,
  2086  							WorkerName: defaultWorker.Name(),
  2087  						})
  2088  						Expect(found).To(BeTrue())
  2089  						Expect(err).ToNot(HaveOccurred())
  2090  					})
  2091  
  2092  				})
  2093  
  2094  				Context("when an existing step-name is provided", func() {
  2095  					BeforeEach(func() {
  2096  						var err error
  2097  						rowsDeleted, err = job.ClearTaskCache("some-task", "")
  2098  						Expect(err).NotTo(HaveOccurred())
  2099  					})
  2100  
  2101  					It("deletes a row from the task_caches table", func() {
  2102  						Expect(rowsDeleted).To(Equal(int64(1)))
  2103  					})
  2104  
  2105  					It("removes the task cache", func() {
  2106  						_, found, err := taskCacheFactory.Find(job.ID(), "some-task", "some-path")
  2107  						Expect(found).To(BeFalse())
  2108  						Expect(err).ToNot(HaveOccurred())
  2109  					})
  2110  
  2111  					It("doesn't remove other jobs caches", func() {
  2112  						_, found, err := taskCacheFactory.Find(someOtherJob.ID(), "some-other-task", "some-other-path")
  2113  						Expect(found).To(BeTrue())
  2114  						Expect(err).ToNot(HaveOccurred())
  2115  					})
  2116  				})
  2117  			})
  2118  		})
  2119  	})
  2120  
  2121  	Describe("New Inputs", func() {
  2122  		It("starts out as false", func() {
  2123  			Expect(job.HasNewInputs()).To(BeFalse())
  2124  		})
  2125  
  2126  		It("can be set to true then back to false", func() {
  2127  			job.SetHasNewInputs(true)
  2128  
  2129  			found, err := job.Reload()
  2130  
  2131  			Expect(err).NotTo(HaveOccurred())
  2132  			Expect(found).To(BeTrue())
  2133  
  2134  			Expect(job.HasNewInputs()).To(BeTrue())
  2135  
  2136  			job.SetHasNewInputs(false)
  2137  
  2138  			found, err = job.Reload()
  2139  
  2140  			Expect(err).NotTo(HaveOccurred())
  2141  			Expect(found).To(BeTrue())
  2142  
  2143  			Expect(job.HasNewInputs()).To(BeFalse())
  2144  		})
  2145  	})
  2146  
  2147  	Describe("AlgorithmInputs", func() {
  2148  		var scenario *dbtest.Scenario
  2149  		var inputs db.InputConfigs
  2150  
  2151  		JustBeforeEach(func() {
  2152  			var err error
  2153  			inputs, err = scenario.Job("some-job").AlgorithmInputs()
  2154  			Expect(err).ToNot(HaveOccurred())
  2155  		})
  2156  
  2157  		Context("when there is an input configured for the job", func() {
  2158  			BeforeEach(func() {
  2159  				scenario = dbtest.Setup(
  2160  					builder.WithPipeline(atc.Config{
  2161  						Jobs: atc.JobConfigs{
  2162  							{
  2163  								Name: "some-job",
  2164  								PlanSequence: []atc.Step{
  2165  									{
  2166  										Config: &atc.GetStep{
  2167  											Name:     "some-input",
  2168  											Resource: "some-resource",
  2169  											Params: atc.Params{
  2170  												"some-param": "some-value",
  2171  											},
  2172  											Passed:  []string{"job-1", "job-2"},
  2173  											Trigger: true,
  2174  											Version: &atc.VersionConfig{Every: true},
  2175  										},
  2176  									},
  2177  								},
  2178  							},
  2179  							{
  2180  								Name: "job-1",
  2181  							},
  2182  							{
  2183  								Name: "job-2",
  2184  							},
  2185  						},
  2186  						Resources: atc.ResourceConfigs{
  2187  							{
  2188  								Name: "some-resource",
  2189  								Type: "some-type",
  2190  							},
  2191  						},
  2192  					}),
  2193  				)
  2194  			})
  2195  
  2196  			It("returns the input for the job", func() {
  2197  				Expect(inputs).To(Equal(db.InputConfigs{
  2198  					{
  2199  						Name:       "some-input",
  2200  						JobID:      scenario.Job("some-job").ID(),
  2201  						ResourceID: scenario.Resource("some-resource").ID(),
  2202  						Passed: db.JobSet{
  2203  							scenario.Job("job-1").ID(): true,
  2204  							scenario.Job("job-2").ID(): true,
  2205  						},
  2206  						UseEveryVersion: true,
  2207  						Trigger:         true,
  2208  					},
  2209  				}))
  2210  			})
  2211  		})
  2212  
  2213  		Context("when the input is pinned through the get step", func() {
  2214  			BeforeEach(func() {
  2215  				scenario = dbtest.Setup(
  2216  					builder.WithPipeline(atc.Config{
  2217  						Jobs: atc.JobConfigs{
  2218  							{
  2219  								Name: "some-job",
  2220  								PlanSequence: []atc.Step{
  2221  									{
  2222  										Config: &atc.GetStep{
  2223  											Name:     "some-pinned-input",
  2224  											Resource: "some-resource",
  2225  											Version:  &atc.VersionConfig{Pinned: atc.Version{"input": "pinned"}},
  2226  										},
  2227  									},
  2228  								},
  2229  							},
  2230  						},
  2231  						Resources: atc.ResourceConfigs{
  2232  							{
  2233  								Name:   "some-resource",
  2234  								Type:   "some-base-resource-type",
  2235  								Source: atc.Source{"some": "source"},
  2236  							},
  2237  						},
  2238  					}),
  2239  				)
  2240  			})
  2241  
  2242  			It("pins the inputs to that version", func() {
  2243  				Expect(inputs).To(Equal(db.InputConfigs{
  2244  					{
  2245  						Name:          "some-pinned-input",
  2246  						JobID:         scenario.Job("some-job").ID(),
  2247  						ResourceID:    scenario.Resource("some-resource").ID(),
  2248  						PinnedVersion: atc.Version{"input": "pinned"},
  2249  					},
  2250  				}))
  2251  			})
  2252  
  2253  			Context("when the input is also pinned through the api", func() {
  2254  				BeforeEach(func() {
  2255  					scenario.Run(
  2256  						builder.WithPinnedVersion("some-resource", atc.Version{"api": "pinned"}),
  2257  					)
  2258  				})
  2259  
  2260  				It("resolves the pinned version to the version pinned through the get step", func() {
  2261  					Expect(inputs).To(Equal(db.InputConfigs{
  2262  						{
  2263  							Name:          "some-pinned-input",
  2264  							JobID:         scenario.Job("some-job").ID(),
  2265  							ResourceID:    scenario.Resource("some-resource").ID(),
  2266  							PinnedVersion: atc.Version{"input": "pinned"},
  2267  						},
  2268  					}))
  2269  				})
  2270  			})
  2271  		})
  2272  
  2273  		Context("when the input is pinned through the resource config", func() {
  2274  			BeforeEach(func() {
  2275  				scenario = dbtest.Setup(
  2276  					builder.WithPipeline(atc.Config{
  2277  						Jobs: atc.JobConfigs{
  2278  							{
  2279  								Name: "some-job",
  2280  								PlanSequence: []atc.Step{
  2281  									{
  2282  										Config: &atc.GetStep{
  2283  											Name:     "some-pinned-input",
  2284  											Resource: "some-resource",
  2285  										},
  2286  									},
  2287  								},
  2288  							},
  2289  						},
  2290  						Resources: atc.ResourceConfigs{
  2291  							{
  2292  								Name:    "some-resource",
  2293  								Type:    "some-type",
  2294  								Source:  atc.Source{"some": "source"},
  2295  								Version: atc.Version{"some": "version"},
  2296  							},
  2297  						},
  2298  					}),
  2299  				)
  2300  			})
  2301  
  2302  			It("pins the inputs to that version", func() {
  2303  				Expect(inputs).To(Equal(db.InputConfigs{
  2304  					{
  2305  						Name:          "some-pinned-input",
  2306  						JobID:         scenario.Job("some-job").ID(),
  2307  						ResourceID:    scenario.Resource("some-resource").ID(),
  2308  						PinnedVersion: atc.Version{"some": "version"},
  2309  					},
  2310  				}))
  2311  			})
  2312  		})
  2313  
  2314  		Context("when the input is pinned through the api", func() {
  2315  			BeforeEach(func() {
  2316  				scenario = dbtest.Setup(
  2317  					builder.WithPipeline(atc.Config{
  2318  						Jobs: atc.JobConfigs{
  2319  							{
  2320  								Name: "some-job",
  2321  								PlanSequence: []atc.Step{
  2322  									{
  2323  										Config: &atc.GetStep{
  2324  											Name:     "some-pinned-input",
  2325  											Resource: "some-resource",
  2326  										},
  2327  									},
  2328  								},
  2329  							},
  2330  						},
  2331  						Resources: atc.ResourceConfigs{
  2332  							{
  2333  								Name:   "some-resource",
  2334  								Type:   "some-base-resource-type",
  2335  								Source: atc.Source{"some": "source"},
  2336  							},
  2337  						},
  2338  					}),
  2339  					builder.WithPinnedVersion("some-resource", atc.Version{"some": "version"}),
  2340  				)
  2341  			})
  2342  
  2343  			It("pins the inputs to that version", func() {
  2344  				Expect(inputs).To(Equal(db.InputConfigs{
  2345  					{
  2346  						Name:          "some-pinned-input",
  2347  						JobID:         scenario.Job("some-job").ID(),
  2348  						ResourceID:    scenario.Resource("some-resource").ID(),
  2349  						PinnedVersion: atc.Version{"some": "version"},
  2350  					},
  2351  				}))
  2352  			})
  2353  		})
  2354  
  2355  		Context("when there are multiple inputs", func() {
  2356  			BeforeEach(func() {
  2357  				scenario = dbtest.Setup(
  2358  					builder.WithPipeline(atc.Config{
  2359  						Jobs: atc.JobConfigs{
  2360  							{
  2361  								Name: "some-job",
  2362  								PlanSequence: []atc.Step{
  2363  									{
  2364  										Config: &atc.GetStep{
  2365  											Name:     "some-input",
  2366  											Resource: "some-resource",
  2367  											Trigger:  true,
  2368  											Version:  &atc.VersionConfig{Every: true},
  2369  										},
  2370  									},
  2371  									{
  2372  										Config: &atc.GetStep{
  2373  											Name: "some-resource",
  2374  										},
  2375  									},
  2376  									{
  2377  										Config: &atc.GetStep{
  2378  											Name:    "some-other-resource",
  2379  											Trigger: true,
  2380  											Version: &atc.VersionConfig{Latest: true},
  2381  										},
  2382  									},
  2383  								},
  2384  							},
  2385  							{
  2386  								Name: "some-other-job",
  2387  								PlanSequence: []atc.Step{
  2388  									{
  2389  										Config: &atc.GetStep{
  2390  											Name:     "other-job-resource",
  2391  											Resource: "some-resource",
  2392  										},
  2393  									},
  2394  								},
  2395  							},
  2396  						},
  2397  						Resources: atc.ResourceConfigs{
  2398  							{
  2399  								Name: "some-resource",
  2400  								Type: "some-type",
  2401  							},
  2402  							{
  2403  								Name: "some-other-resource",
  2404  								Type: "some-type",
  2405  							},
  2406  						},
  2407  					}),
  2408  				)
  2409  			})
  2410  
  2411  			It("returns all the inputs correctly", func() {
  2412  				Expect(inputs).To(HaveLen(3))
  2413  				Expect(inputs).To(ConsistOf(
  2414  					db.InputConfig{
  2415  						Name:            "some-input",
  2416  						JobID:           scenario.Job("some-job").ID(),
  2417  						ResourceID:      scenario.Resource("some-resource").ID(),
  2418  						UseEveryVersion: true,
  2419  						Trigger:         true,
  2420  					},
  2421  					db.InputConfig{
  2422  						Name:       "some-resource",
  2423  						JobID:      scenario.Job("some-job").ID(),
  2424  						ResourceID: scenario.Resource("some-resource").ID(),
  2425  					},
  2426  					db.InputConfig{
  2427  						Name:       "some-other-resource",
  2428  						JobID:      scenario.Job("some-job").ID(),
  2429  						ResourceID: scenario.Resource("some-other-resource").ID(),
  2430  						Trigger:    true,
  2431  					}))
  2432  			})
  2433  		})
  2434  
  2435  		Context("when the job has puts and tasks", func() {
  2436  			BeforeEach(func() {
  2437  				scenario = dbtest.Setup(
  2438  					builder.WithPipeline(atc.Config{
  2439  						Jobs: atc.JobConfigs{
  2440  							{
  2441  								Name: "some-job",
  2442  								PlanSequence: []atc.Step{
  2443  									{
  2444  										Config: &atc.PutStep{
  2445  											Name: "some-resource",
  2446  										},
  2447  									},
  2448  									{
  2449  										Config: &atc.TaskStep{
  2450  											Name:       "some-task",
  2451  											Privileged: true,
  2452  											ConfigPath: "some/config/path.yml",
  2453  											Config: &atc.TaskConfig{
  2454  												RootfsURI: "some-image",
  2455  											},
  2456  										},
  2457  									},
  2458  									{
  2459  										Config: &atc.GetStep{
  2460  											Name: "some-resource",
  2461  										},
  2462  									},
  2463  								},
  2464  							},
  2465  						},
  2466  						Resources: atc.ResourceConfigs{
  2467  							{
  2468  								Name: "some-resource",
  2469  								Type: "some-type",
  2470  							},
  2471  						},
  2472  					}),
  2473  				)
  2474  			})
  2475  
  2476  			It("only returns the gets (inputs to the job)", func() {
  2477  				Expect(inputs).To(Equal(db.InputConfigs{
  2478  					{
  2479  						Name:       "some-resource",
  2480  						JobID:      scenario.Job("some-job").ID(),
  2481  						ResourceID: scenario.Resource("some-resource").ID(),
  2482  					},
  2483  				}))
  2484  			})
  2485  		})
  2486  	})
  2487  
  2488  	Describe("Inputs", func() {
  2489  		var inputsJob db.Job
  2490  
  2491  		BeforeEach(func() {
  2492  			inputsPipeline, _, err := team.SavePipeline(atc.PipelineRef{Name: "inputs-pipeline"}, atc.Config{
  2493  				Jobs: atc.JobConfigs{
  2494  					{
  2495  						Name: "some-job",
  2496  						PlanSequence: []atc.Step{
  2497  							{
  2498  								Config: &atc.PutStep{
  2499  									Name: "some-resource",
  2500  								},
  2501  							},
  2502  							{
  2503  								Config: &atc.GetStep{
  2504  									Name:     "some-input",
  2505  									Resource: "some-resource",
  2506  									Params: atc.Params{
  2507  										"some-param": "some-value",
  2508  									},
  2509  									Passed:  []string{"job-1", "job-2"},
  2510  									Trigger: true,
  2511  									Version: &atc.VersionConfig{Every: true},
  2512  								},
  2513  							},
  2514  							{
  2515  								Config: &atc.TaskStep{
  2516  									Name:       "some-task",
  2517  									Privileged: true,
  2518  									ConfigPath: "some/config/path.yml",
  2519  									Config: &atc.TaskConfig{
  2520  										RootfsURI: "some-image",
  2521  									},
  2522  								},
  2523  							},
  2524  							{
  2525  								Config: &atc.GetStep{
  2526  									Name: "some-resource",
  2527  								},
  2528  							},
  2529  							{
  2530  								Config: &atc.GetStep{
  2531  									Name:     "some-other-input",
  2532  									Resource: "some-resource",
  2533  									Version:  &atc.VersionConfig{Latest: true},
  2534  								},
  2535  							},
  2536  							{
  2537  								Config: &atc.GetStep{
  2538  									Name:    "some-other-resource",
  2539  									Trigger: true,
  2540  									Version: &atc.VersionConfig{Pinned: atc.Version{"pinned": "version"}},
  2541  								},
  2542  							},
  2543  						},
  2544  					},
  2545  					{
  2546  						Name: "some-other-job",
  2547  						PlanSequence: []atc.Step{
  2548  							{
  2549  								Config: &atc.GetStep{
  2550  									Name:     "other-job-resource",
  2551  									Resource: "some-resource",
  2552  								},
  2553  							},
  2554  						},
  2555  					},
  2556  					{
  2557  						Name: "job-1",
  2558  					},
  2559  					{
  2560  						Name: "job-2",
  2561  					},
  2562  				},
  2563  				Resources: atc.ResourceConfigs{
  2564  					{
  2565  						Name: "some-resource",
  2566  						Type: "some-type",
  2567  					},
  2568  					{
  2569  						Name: "some-other-resource",
  2570  						Type: "some-type",
  2571  					},
  2572  				},
  2573  			}, db.ConfigVersion(0), false)
  2574  			Expect(err).ToNot(HaveOccurred())
  2575  
  2576  			var found bool
  2577  			inputsJob, found, err = inputsPipeline.Job("some-job")
  2578  			Expect(err).ToNot(HaveOccurred())
  2579  			Expect(found).To(BeTrue())
  2580  		})
  2581  
  2582  		It("returns inputs for the job", func() {
  2583  			inputs, err := inputsJob.Inputs()
  2584  			Expect(err).ToNot(HaveOccurred())
  2585  
  2586  			Expect(inputs).To(Equal([]atc.JobInput{
  2587  				{
  2588  					Name:     "some-input",
  2589  					Resource: "some-resource",
  2590  					Passed:   []string{"job-1", "job-2"},
  2591  					Trigger:  true,
  2592  					Version:  &atc.VersionConfig{Every: true},
  2593  				},
  2594  				{
  2595  					Name:     "some-other-input",
  2596  					Resource: "some-resource",
  2597  					Version:  &atc.VersionConfig{Latest: true},
  2598  				},
  2599  				{
  2600  					Name:     "some-other-resource",
  2601  					Resource: "some-other-resource",
  2602  					Trigger:  true,
  2603  					Version:  &atc.VersionConfig{Pinned: atc.Version{"pinned": "version"}},
  2604  				},
  2605  				{
  2606  					Name:     "some-resource",
  2607  					Resource: "some-resource",
  2608  				},
  2609  			}))
  2610  		})
  2611  	})
  2612  
  2613  	Describe("Outputs", func() {
  2614  		var outputsJob db.Job
  2615  
  2616  		BeforeEach(func() {
  2617  			outputsPipeline, _, err := team.SavePipeline(atc.PipelineRef{Name: "outputs-pipeline"}, atc.Config{
  2618  				Jobs: atc.JobConfigs{
  2619  					{
  2620  						Name: "some-job",
  2621  						PlanSequence: []atc.Step{
  2622  							{
  2623  								Config: &atc.PutStep{
  2624  									Name: "some-other-resource",
  2625  								},
  2626  							},
  2627  							{
  2628  								Config: &atc.TaskStep{
  2629  									Name:       "some-task",
  2630  									Privileged: true,
  2631  									ConfigPath: "some/config/path.yml",
  2632  									Config: &atc.TaskConfig{
  2633  										RootfsURI: "some-image",
  2634  									},
  2635  								},
  2636  							},
  2637  							{
  2638  								Config: &atc.GetStep{
  2639  									Name: "some-resource",
  2640  								},
  2641  							},
  2642  							{
  2643  								Config: &atc.PutStep{
  2644  									Name:     "some-output",
  2645  									Resource: "some-resource",
  2646  								},
  2647  							},
  2648  							{
  2649  								Config: &atc.PutStep{
  2650  									Name:     "some-other-output",
  2651  									Resource: "some-resource",
  2652  								},
  2653  							},
  2654  						},
  2655  					},
  2656  					{
  2657  						Name: "some-other-job",
  2658  						PlanSequence: []atc.Step{
  2659  							{
  2660  								Config: &atc.PutStep{
  2661  									Name:     "other-job-resource",
  2662  									Resource: "some-resource",
  2663  								},
  2664  							},
  2665  						},
  2666  					},
  2667  				},
  2668  				Resources: atc.ResourceConfigs{
  2669  					{
  2670  						Name: "some-resource",
  2671  						Type: "some-type",
  2672  					},
  2673  					{
  2674  						Name: "some-other-resource",
  2675  						Type: "some-type",
  2676  					},
  2677  				},
  2678  			}, db.ConfigVersion(0), false)
  2679  			Expect(err).ToNot(HaveOccurred())
  2680  
  2681  			var found bool
  2682  			outputsJob, found, err = outputsPipeline.Job("some-job")
  2683  			Expect(err).ToNot(HaveOccurred())
  2684  			Expect(found).To(BeTrue())
  2685  		})
  2686  
  2687  		It("returns outputs for the job", func() {
  2688  			outputs, err := outputsJob.Outputs()
  2689  			Expect(err).ToNot(HaveOccurred())
  2690  
  2691  			Expect(outputs).To(Equal([]atc.JobOutput{
  2692  				{
  2693  					Name:     "some-other-output",
  2694  					Resource: "some-resource",
  2695  				},
  2696  				{
  2697  					Name:     "some-other-resource",
  2698  					Resource: "some-other-resource",
  2699  				},
  2700  				{
  2701  					Name:     "some-output",
  2702  					Resource: "some-resource",
  2703  				},
  2704  			}))
  2705  		})
  2706  	})
  2707  })