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

     1  package db_test
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"time"
     7  
     8  	"code.cloudfoundry.org/clock"
     9  	"github.com/pf-qiu/concourse/v6/atc/creds"
    10  	"github.com/pf-qiu/concourse/v6/atc/creds/credsfakes"
    11  	"github.com/pf-qiu/concourse/v6/atc/db/dbtest"
    12  	"github.com/pf-qiu/concourse/v6/vars"
    13  
    14  	"github.com/pf-qiu/concourse/v6/atc"
    15  	"github.com/pf-qiu/concourse/v6/atc/db"
    16  	"github.com/pf-qiu/concourse/v6/atc/event"
    17  
    18  	// load dummy credential manager
    19  	_ "github.com/pf-qiu/concourse/v6/atc/creds/dummy"
    20  
    21  	. "github.com/onsi/ginkgo"
    22  	. "github.com/onsi/gomega"
    23  )
    24  
    25  var _ = Describe("Pipeline", func() {
    26  	var (
    27  		pipeline       db.Pipeline
    28  		team           db.Team
    29  		pipelineConfig atc.Config
    30  	)
    31  
    32  	BeforeEach(func() {
    33  		var err error
    34  		team, err = teamFactory.CreateTeam(atc.Team{Name: "some-team"})
    35  		Expect(err).ToNot(HaveOccurred())
    36  
    37  		pipelineConfig = atc.Config{
    38  			Groups: atc.GroupConfigs{
    39  				{
    40  					Name:      "some-group",
    41  					Jobs:      []string{"job-1", "job-2"},
    42  					Resources: []string{"some-resource", "some-other-resource"},
    43  				},
    44  			},
    45  			VarSources: atc.VarSourceConfigs{
    46  				{
    47  					Name: "some-var-source",
    48  					Type: "dummy",
    49  					Config: map[string]interface{}{
    50  						"vars": map[string]interface{}{"pk": "pv"},
    51  					},
    52  				},
    53  			},
    54  			Display: &atc.DisplayConfig{
    55  				BackgroundImage: "background.jpg",
    56  			},
    57  			Jobs: atc.JobConfigs{
    58  				{
    59  					Name: "job-name",
    60  
    61  					Public: true,
    62  
    63  					Serial: true,
    64  
    65  					SerialGroups: []string{"serial-group"},
    66  
    67  					PlanSequence: []atc.Step{
    68  						{
    69  							Config: &atc.PutStep{
    70  								Name: "some-resource",
    71  								Params: atc.Params{
    72  									"some-param": "some-value",
    73  								},
    74  							},
    75  						},
    76  						{
    77  							Config: &atc.GetStep{
    78  								Name:     "some-input",
    79  								Resource: "some-resource",
    80  								Params: atc.Params{
    81  									"some-param": "some-value",
    82  								},
    83  								Passed:  []string{"job-1", "job-2"},
    84  								Trigger: true,
    85  							},
    86  						},
    87  						{
    88  							Config: &atc.TaskStep{
    89  								Name:       "some-task",
    90  								Privileged: true,
    91  								ConfigPath: "some/config/path.yml",
    92  								Config: &atc.TaskConfig{
    93  									RootfsURI: "some-image",
    94  								},
    95  							},
    96  						},
    97  						{
    98  							Config: &atc.SetPipelineStep{
    99  								Name:     "some-pipeline",
   100  								File:     "some-file",
   101  								VarFiles: []string{"var-file1", "var-file2"},
   102  								Vars: map[string]interface{}{
   103  									"k1": "v1",
   104  									"k2": "v2",
   105  								},
   106  							},
   107  						},
   108  					},
   109  				},
   110  				{
   111  					Name:   "some-other-job",
   112  					Serial: true,
   113  				},
   114  				{
   115  					Name: "a-job",
   116  				},
   117  				{
   118  					Name: "shared-job",
   119  				},
   120  				{
   121  					Name: "random-job",
   122  				},
   123  				{
   124  					Name: "job-1",
   125  				},
   126  				{
   127  					Name: "job-2",
   128  				},
   129  				{
   130  					Name:         "other-serial-group-job",
   131  					SerialGroups: []string{"serial-group", "really-different-group"},
   132  				},
   133  				{
   134  					Name:         "different-serial-group-job",
   135  					SerialGroups: []string{"different-serial-group"},
   136  				},
   137  			},
   138  			Resources: atc.ResourceConfigs{
   139  				{
   140  					Name:   "some-other-resource",
   141  					Type:   "some-type",
   142  					Source: atc.Source{"some": "other-source"},
   143  				},
   144  				{
   145  					Name:   "some-resource",
   146  					Type:   "some-type",
   147  					Source: atc.Source{"some": "source"},
   148  				},
   149  			},
   150  			ResourceTypes: atc.ResourceTypes{
   151  				{
   152  					Name:   "some-other-resource-type",
   153  					Type:   "base-type",
   154  					Source: atc.Source{"some": "other-type-soure"},
   155  				},
   156  				{
   157  					Name:   "some-resource-type",
   158  					Type:   "base-type",
   159  					Source: atc.Source{"some": "type-soure"},
   160  				},
   161  			},
   162  		}
   163  		var created bool
   164  		pipeline, created, err = team.SavePipeline(atc.PipelineRef{Name: "fake-pipeline"}, pipelineConfig, db.ConfigVersion(0), false)
   165  		Expect(err).ToNot(HaveOccurred())
   166  		Expect(created).To(BeTrue())
   167  
   168  		_, found, err := pipeline.Job("job-name")
   169  		Expect(err).ToNot(HaveOccurred())
   170  		Expect(found).To(BeTrue())
   171  
   172  		setupTx, err := dbConn.Begin()
   173  		Expect(err).ToNot(HaveOccurred())
   174  
   175  		brt := db.BaseResourceType{
   176  			Name: "some-type",
   177  		}
   178  
   179  		_, err = brt.FindOrCreate(setupTx, false)
   180  		Expect(err).NotTo(HaveOccurred())
   181  		Expect(setupTx.Commit()).To(Succeed())
   182  	})
   183  
   184  	Describe("CheckPaused", func() {
   185  		var paused bool
   186  		JustBeforeEach(func() {
   187  			var err error
   188  			paused, err = pipeline.CheckPaused()
   189  			Expect(err).ToNot(HaveOccurred())
   190  		})
   191  
   192  		Context("when the pipeline is unpaused", func() {
   193  			BeforeEach(func() {
   194  				Expect(pipeline.Unpause()).To(Succeed())
   195  			})
   196  
   197  			It("returns the pipeline is paused", func() {
   198  				Expect(paused).To(BeFalse())
   199  			})
   200  		})
   201  
   202  		Context("when the pipeline is paused", func() {
   203  			BeforeEach(func() {
   204  				Expect(pipeline.Pause()).To(Succeed())
   205  			})
   206  
   207  			It("returns the pipeline is paused", func() {
   208  				Expect(paused).To(BeTrue())
   209  			})
   210  		})
   211  	})
   212  
   213  	Describe("Pause", func() {
   214  		JustBeforeEach(func() {
   215  			Expect(pipeline.Pause()).To(Succeed())
   216  
   217  			found, err := pipeline.Reload()
   218  			Expect(err).ToNot(HaveOccurred())
   219  			Expect(found).To(BeTrue())
   220  		})
   221  
   222  		Context("when the pipeline is unpaused", func() {
   223  			BeforeEach(func() {
   224  				Expect(pipeline.Unpause()).To(Succeed())
   225  			})
   226  
   227  			It("pauses the pipeline", func() {
   228  				Expect(pipeline.Paused()).To(BeTrue())
   229  			})
   230  		})
   231  	})
   232  
   233  	Describe("Archive", func() {
   234  		var initialLastUpdated time.Time
   235  
   236  		BeforeEach(func() {
   237  			initialLastUpdated = pipeline.LastUpdated()
   238  		})
   239  
   240  		JustBeforeEach(func() {
   241  			pipeline.Archive()
   242  			pipeline.Reload()
   243  		})
   244  
   245  		It("archives the pipeline", func() {
   246  			Expect(pipeline.Archived()).To(BeTrue(), "pipeline was not archived")
   247  		})
   248  
   249  		It("updates last updated", func() {
   250  			lastUpdated := pipeline.LastUpdated()
   251  
   252  			Expect(lastUpdated).To(BeTemporally(">", initialLastUpdated))
   253  		})
   254  
   255  		It("resets the pipeline version to zero", func() {
   256  			version := pipeline.ConfigVersion()
   257  
   258  			Expect(version).To(Equal(db.ConfigVersion(0)))
   259  		})
   260  
   261  		It("removes the config of each job", func() {
   262  			jobs, err := pipeline.Jobs()
   263  			Expect(err).ToNot(HaveOccurred())
   264  
   265  			jobConfigs, err := jobs.Configs()
   266  			emptyJobConfigs := make(atc.JobConfigs, len(pipelineConfig.Jobs))
   267  			Expect(jobConfigs).To(Equal(emptyJobConfigs))
   268  		})
   269  
   270  		It("removes the config of each resource", func() {
   271  			resources, err := pipeline.Resources()
   272  			Expect(err).ToNot(HaveOccurred())
   273  
   274  			resourceConfigs := resources.Configs()
   275  
   276  			emptyResourceConfigs := make(atc.ResourceConfigs, len(pipelineConfig.Resources))
   277  			Expect(resourceConfigs).To(Equal(emptyResourceConfigs))
   278  		})
   279  
   280  		It("removes the config of each resource_type", func() {
   281  			resourceTypes, err := pipeline.ResourceTypes()
   282  			Expect(err).ToNot(HaveOccurred())
   283  
   284  			resourceTypeConfigs := resourceTypes.Configs()
   285  
   286  			emptyResourceTypeConfigs := atc.ResourceTypes{
   287  				{Name: "some-other-resource-type", Type: "base-type"},
   288  				{Name: "some-resource-type", Type: "base-type"},
   289  			}
   290  			Expect(resourceTypeConfigs).To(Equal(emptyResourceTypeConfigs))
   291  		})
   292  	})
   293  
   294  	Describe("Unpause", func() {
   295  		JustBeforeEach(func() {
   296  			Expect(pipeline.Unpause()).To(Succeed())
   297  
   298  			found, err := pipeline.Reload()
   299  			Expect(err).ToNot(HaveOccurred())
   300  			Expect(found).To(BeTrue())
   301  		})
   302  
   303  		Context("when the pipeline is paused", func() {
   304  			BeforeEach(func() {
   305  				Expect(pipeline.Pause()).To(Succeed())
   306  			})
   307  
   308  			It("unpauses the pipeline", func() {
   309  				Expect(pipeline.Paused()).To(BeFalse())
   310  			})
   311  		})
   312  
   313  		Context("when requesting schedule for unpausing pipeline", func() {
   314  			var found bool
   315  			var err error
   316  			var job1, job2, job3, job4, job5, job6, job7, job8, job9 db.Job
   317  			var initialRequestedTime1, initialRequestedTime2, initialRequestedTime3, initialRequestedTime4, initialRequestedTime5, initialRequestedTime6, initialRequestedTime7, initialRequestedTime8, initialRequestedTime9 time.Time
   318  
   319  			BeforeEach(func() {
   320  				job1, found, err = pipeline.Job("job-name")
   321  				Expect(err).ToNot(HaveOccurred())
   322  				Expect(found).To(BeTrue())
   323  				initialRequestedTime1 = job1.ScheduleRequestedTime()
   324  
   325  				job2, found, err = pipeline.Job("some-other-job")
   326  				Expect(err).ToNot(HaveOccurred())
   327  				Expect(found).To(BeTrue())
   328  				initialRequestedTime2 = job2.ScheduleRequestedTime()
   329  
   330  				job3, found, err = pipeline.Job("a-job")
   331  				Expect(err).ToNot(HaveOccurred())
   332  				Expect(found).To(BeTrue())
   333  				initialRequestedTime3 = job3.ScheduleRequestedTime()
   334  
   335  				job4, found, err = pipeline.Job("shared-job")
   336  				Expect(err).ToNot(HaveOccurred())
   337  				Expect(found).To(BeTrue())
   338  				initialRequestedTime4 = job4.ScheduleRequestedTime()
   339  
   340  				job5, found, err = pipeline.Job("random-job")
   341  				Expect(err).ToNot(HaveOccurred())
   342  				Expect(found).To(BeTrue())
   343  				initialRequestedTime5 = job5.ScheduleRequestedTime()
   344  
   345  				job6, found, err = pipeline.Job("job-1")
   346  				Expect(err).ToNot(HaveOccurred())
   347  				Expect(found).To(BeTrue())
   348  				initialRequestedTime6 = job6.ScheduleRequestedTime()
   349  
   350  				job7, found, err = pipeline.Job("job-2")
   351  				Expect(err).ToNot(HaveOccurred())
   352  				Expect(found).To(BeTrue())
   353  				initialRequestedTime7 = job7.ScheduleRequestedTime()
   354  
   355  				job8, found, err = pipeline.Job("other-serial-group-job")
   356  				Expect(err).ToNot(HaveOccurred())
   357  				Expect(found).To(BeTrue())
   358  				initialRequestedTime8 = job8.ScheduleRequestedTime()
   359  
   360  				job9, found, err = pipeline.Job("different-serial-group-job")
   361  				Expect(err).ToNot(HaveOccurred())
   362  				Expect(found).To(BeTrue())
   363  				initialRequestedTime9 = job9.ScheduleRequestedTime()
   364  			})
   365  
   366  			It("requests schedule on all the jobs in the pipeline", func() {
   367  				found, err = job1.Reload()
   368  				Expect(err).ToNot(HaveOccurred())
   369  				Expect(found).To(BeTrue())
   370  
   371  				found, err = job2.Reload()
   372  				Expect(err).ToNot(HaveOccurred())
   373  				Expect(found).To(BeTrue())
   374  
   375  				found, err = job3.Reload()
   376  				Expect(err).ToNot(HaveOccurred())
   377  				Expect(found).To(BeTrue())
   378  
   379  				found, err = job4.Reload()
   380  				Expect(err).ToNot(HaveOccurred())
   381  				Expect(found).To(BeTrue())
   382  
   383  				found, err = job5.Reload()
   384  				Expect(err).ToNot(HaveOccurred())
   385  				Expect(found).To(BeTrue())
   386  
   387  				found, err = job6.Reload()
   388  				Expect(err).ToNot(HaveOccurred())
   389  				Expect(found).To(BeTrue())
   390  
   391  				found, err = job7.Reload()
   392  				Expect(err).ToNot(HaveOccurred())
   393  				Expect(found).To(BeTrue())
   394  
   395  				found, err = job8.Reload()
   396  				Expect(err).ToNot(HaveOccurred())
   397  				Expect(found).To(BeTrue())
   398  
   399  				found, err = job9.Reload()
   400  				Expect(err).ToNot(HaveOccurred())
   401  				Expect(found).To(BeTrue())
   402  
   403  				Expect(job1.ScheduleRequestedTime()).Should(BeTemporally(">", initialRequestedTime1))
   404  				Expect(job2.ScheduleRequestedTime()).Should(BeTemporally(">", initialRequestedTime2))
   405  				Expect(job3.ScheduleRequestedTime()).Should(BeTemporally(">", initialRequestedTime3))
   406  				Expect(job4.ScheduleRequestedTime()).Should(BeTemporally(">", initialRequestedTime4))
   407  				Expect(job5.ScheduleRequestedTime()).Should(BeTemporally(">", initialRequestedTime5))
   408  				Expect(job6.ScheduleRequestedTime()).Should(BeTemporally(">", initialRequestedTime6))
   409  				Expect(job7.ScheduleRequestedTime()).Should(BeTemporally(">", initialRequestedTime7))
   410  				Expect(job8.ScheduleRequestedTime()).Should(BeTemporally(">", initialRequestedTime8))
   411  				Expect(job9.ScheduleRequestedTime()).Should(BeTemporally(">", initialRequestedTime9))
   412  			})
   413  		})
   414  	})
   415  
   416  	Describe("Rename", func() {
   417  		JustBeforeEach(func() {
   418  			Expect(pipeline.Rename("oopsies")).To(Succeed())
   419  		})
   420  
   421  		It("renames the pipeline", func() {
   422  			pipeline, found, err := team.Pipeline(atc.PipelineRef{Name: "oopsies"})
   423  			Expect(pipeline.Name()).To(Equal("oopsies"))
   424  			Expect(found).To(BeTrue())
   425  			Expect(err).ToNot(HaveOccurred())
   426  		})
   427  	})
   428  
   429  	Describe("Resource Config Versions", func() {
   430  		resourceName := "some-resource"
   431  		otherResourceName := "some-other-resource"
   432  		reallyOtherResourceName := "some-really-other-resource"
   433  
   434  		var (
   435  			scenarioPipeline1 *dbtest.Scenario
   436  			scenarioPipeline2 *dbtest.Scenario
   437  
   438  			resource              db.Resource
   439  			otherResource         db.Resource
   440  			reallyOtherResource   db.Resource
   441  			otherPipelineResource db.Resource
   442  		)
   443  
   444  		BeforeEach(func() {
   445  			pipelineConfig := atc.Config{
   446  				Groups: atc.GroupConfigs{
   447  					{
   448  						Name:      "some-group",
   449  						Jobs:      []string{"job-1", "job-2"},
   450  						Resources: []string{"some-resource", "some-other-resource"},
   451  					},
   452  				},
   453  
   454  				Resources: atc.ResourceConfigs{
   455  					{
   456  						Name: "some-resource",
   457  						Type: "some-type",
   458  						Source: atc.Source{
   459  							"source-config": "some-value",
   460  						},
   461  					},
   462  					{
   463  						Name: "some-other-resource",
   464  						Type: "some-type",
   465  						Source: atc.Source{
   466  							"source-config": "some-other-value",
   467  						},
   468  					},
   469  					{
   470  						Name: "some-really-other-resource",
   471  						Type: "some-type",
   472  						Source: atc.Source{
   473  							"source-config": "some-really-other-value",
   474  						},
   475  					},
   476  				},
   477  
   478  				ResourceTypes: atc.ResourceTypes{
   479  					{
   480  						Name: "some-resource-type",
   481  						Type: "some-type",
   482  						Source: atc.Source{
   483  							"source-config": "some-value",
   484  						},
   485  					},
   486  				},
   487  
   488  				Jobs: atc.JobConfigs{
   489  					{
   490  						Name: "some-job",
   491  
   492  						Public: true,
   493  
   494  						Serial: true,
   495  
   496  						SerialGroups: []string{"serial-group"},
   497  
   498  						PlanSequence: []atc.Step{
   499  							{
   500  								Config: &atc.PutStep{
   501  									Name: "some-resource",
   502  									Params: atc.Params{
   503  										"some-param": "some-value",
   504  									},
   505  								},
   506  							},
   507  							{
   508  								Config: &atc.GetStep{
   509  									Name:     "some-input",
   510  									Resource: "some-resource",
   511  									Params: atc.Params{
   512  										"some-param": "some-value",
   513  									},
   514  									Passed:  []string{"job-1", "job-2"},
   515  									Trigger: true,
   516  								},
   517  							},
   518  							{
   519  								Config: &atc.TaskStep{
   520  									Name:       "some-task",
   521  									Privileged: true,
   522  									ConfigPath: "some/config/path.yml",
   523  									Config: &atc.TaskConfig{
   524  										RootfsURI: "some-image",
   525  									},
   526  								},
   527  							},
   528  						},
   529  					},
   530  					{
   531  						Name:   "some-other-job",
   532  						Serial: true,
   533  					},
   534  					{
   535  						Name: "a-job",
   536  					},
   537  					{
   538  						Name: "shared-job",
   539  					},
   540  					{
   541  						Name: "random-job",
   542  					},
   543  					{
   544  						Name:         "other-serial-group-job",
   545  						SerialGroups: []string{"serial-group", "really-different-group"},
   546  					},
   547  					{
   548  						Name:         "different-serial-group-job",
   549  						SerialGroups: []string{"different-serial-group"},
   550  					},
   551  					{
   552  						Name: "job-1",
   553  					},
   554  					{
   555  						Name: "job-2",
   556  					},
   557  				},
   558  			}
   559  
   560  			scenarioPipeline1 = dbtest.Setup(
   561  				builder.WithPipeline(pipelineConfig),
   562  				builder.WithResourceVersions("some-resource"),
   563  				builder.WithResourceVersions("some-really-other-resource"),
   564  			)
   565  
   566  			otherPipelineConfig := atc.Config{
   567  				Groups: atc.GroupConfigs{
   568  					{
   569  						Name:      "some-group",
   570  						Jobs:      []string{"job-1", "job-2"},
   571  						Resources: []string{"some-resource", "some-other-resource"},
   572  					},
   573  				},
   574  
   575  				Resources: atc.ResourceConfigs{
   576  					{
   577  						Name: "some-resource",
   578  						Type: "some-type",
   579  						Source: atc.Source{
   580  							"other-source-config": "some-value",
   581  						},
   582  					},
   583  					{
   584  						Name: "some-other-resource",
   585  						Type: "some-type",
   586  						Source: atc.Source{
   587  							"other-source-config": "some-other-value",
   588  						},
   589  					},
   590  				},
   591  
   592  				Jobs: atc.JobConfigs{
   593  					{
   594  						Name: "some-job",
   595  					},
   596  					{
   597  						Name: "some-other-job",
   598  					},
   599  					{
   600  						Name: "a-job",
   601  					},
   602  					{
   603  						Name: "shared-job",
   604  					},
   605  					{
   606  						Name: "other-serial-group-job",
   607  					},
   608  				},
   609  			}
   610  
   611  			scenarioPipeline2 = dbtest.Setup(
   612  				builder.WithPipeline(otherPipelineConfig),
   613  				builder.WithResourceVersions("some-other-resource"),
   614  			)
   615  
   616  			resource = scenarioPipeline1.Resource(resourceName)
   617  			otherResource = scenarioPipeline1.Resource(otherResourceName)
   618  			reallyOtherResource = scenarioPipeline1.Resource(reallyOtherResourceName)
   619  			otherPipelineResource = scenarioPipeline2.Resource(otherResourceName)
   620  		})
   621  
   622  		It("returns correct resource", func() {
   623  			Expect(resource.Name()).To(Equal("some-resource"))
   624  			Expect(resource.PipelineName()).To(Equal("some-pipeline"))
   625  			Expect(resource.Type()).To(Equal("some-type"))
   626  			Expect(resource.Source()).To(Equal(atc.Source{"source-config": "some-value"}))
   627  		})
   628  
   629  		Context("DebugLoadVersionsDB", func() {
   630  			It("it can load all information about the current state of the db", func() {
   631  				versions, err := scenarioPipeline1.Pipeline.LoadDebugVersionsDB()
   632  				Expect(err).ToNot(HaveOccurred())
   633  				Expect(versions.ResourceVersions).To(BeEmpty())
   634  				Expect(versions.BuildOutputs).To(BeEmpty())
   635  				Expect(versions.Resources).To(ConsistOf([]atc.DebugResource{
   636  					{
   637  						ID:      resource.ID(),
   638  						Name:    resource.Name(),
   639  						ScopeID: intptr(resource.ResourceConfigScopeID()),
   640  					},
   641  					{
   642  						ID:      otherResource.ID(),
   643  						Name:    otherResource.Name(),
   644  						ScopeID: nil,
   645  					},
   646  					{
   647  						ID:      reallyOtherResource.ID(),
   648  						Name:    reallyOtherResource.Name(),
   649  						ScopeID: intptr(reallyOtherResource.ResourceConfigScopeID()),
   650  					},
   651  				}))
   652  
   653  				jobs := []atc.DebugJob{
   654  					{Name: "some-job", ID: scenarioPipeline1.Job("some-job").ID()},
   655  					{Name: "some-other-job", ID: scenarioPipeline1.Job("some-other-job").ID()},
   656  					{Name: "a-job", ID: scenarioPipeline1.Job("a-job").ID()},
   657  					{Name: "shared-job", ID: scenarioPipeline1.Job("shared-job").ID()},
   658  					{Name: "random-job", ID: scenarioPipeline1.Job("random-job").ID()},
   659  					{Name: "other-serial-group-job", ID: scenarioPipeline1.Job("other-serial-group-job").ID()},
   660  					{Name: "different-serial-group-job", ID: scenarioPipeline1.Job("different-serial-group-job").ID()},
   661  					{Name: "job-1", ID: scenarioPipeline1.Job("job-1").ID()},
   662  					{Name: "job-2", ID: scenarioPipeline1.Job("job-2").ID()},
   663  				}
   664  
   665  				Expect(versions.Jobs).To(ConsistOf(jobs))
   666  
   667  				By("initially having no versions")
   668  				resourceVersions, _, _, err := resource.Versions(db.Page{Limit: 10}, nil)
   669  				Expect(err).ToNot(HaveOccurred())
   670  				Expect(resourceVersions).To(HaveLen(0))
   671  
   672  				By("including saved versioned resources of the current pipeline")
   673  				scenarioPipeline1.Run(builder.WithResourceVersions(resourceName, atc.Version{"version": "1"}))
   674  
   675  				savedVR1, found, err := resource.FindVersion(atc.Version{"version": "1"})
   676  				Expect(err).ToNot(HaveOccurred())
   677  				Expect(found).To(BeTrue())
   678  
   679  				scenarioPipeline1.Run(builder.WithResourceVersions(resourceName, atc.Version{"version": "2"}))
   680  
   681  				savedVR2, found, err := resource.FindVersion(atc.Version{"version": "2"})
   682  				Expect(err).ToNot(HaveOccurred())
   683  				Expect(found).To(BeTrue())
   684  
   685  				versions, err = scenarioPipeline1.Pipeline.LoadDebugVersionsDB()
   686  				Expect(err).ToNot(HaveOccurred())
   687  				Expect(versions.ResourceVersions).To(ConsistOf([]atc.DebugResourceVersion{
   688  					{VersionID: savedVR1.ID(), ResourceID: resource.ID(), ScopeID: resource.ResourceConfigScopeID(), CheckOrder: savedVR1.CheckOrder()},
   689  					{VersionID: savedVR2.ID(), ResourceID: resource.ID(), ScopeID: resource.ResourceConfigScopeID(), CheckOrder: savedVR2.CheckOrder()},
   690  				}))
   691  
   692  				Expect(versions.BuildOutputs).To(BeEmpty())
   693  				Expect(versions.Resources).To(ConsistOf([]atc.DebugResource{
   694  					{
   695  						ID:      resource.ID(),
   696  						Name:    resource.Name(),
   697  						ScopeID: intptr(resource.ResourceConfigScopeID()),
   698  					},
   699  					{
   700  						ID:      otherResource.ID(),
   701  						Name:    otherResource.Name(),
   702  						ScopeID: nil,
   703  					},
   704  					{
   705  						ID:      reallyOtherResource.ID(),
   706  						Name:    reallyOtherResource.Name(),
   707  						ScopeID: intptr(reallyOtherResource.ResourceConfigScopeID()),
   708  					},
   709  				}))
   710  				Expect(versions.Jobs).To(ConsistOf(jobs))
   711  
   712  				By("not including saved versioned resources of other pipelines")
   713  				scenarioPipeline2.Run(builder.WithResourceVersions(otherResourceName, atc.Version{"version": "1"}))
   714  
   715  				_, found, err = otherPipelineResource.FindVersion(atc.Version{"version": "1"})
   716  				Expect(err).ToNot(HaveOccurred())
   717  				Expect(found).To(BeTrue())
   718  
   719  				versions, err = scenarioPipeline1.Pipeline.LoadDebugVersionsDB()
   720  				Expect(err).ToNot(HaveOccurred())
   721  				Expect(versions.ResourceVersions).To(ConsistOf([]atc.DebugResourceVersion{
   722  					{
   723  						VersionID:  savedVR1.ID(),
   724  						ResourceID: resource.ID(),
   725  						ScopeID:    resource.ResourceConfigScopeID(),
   726  						CheckOrder: savedVR1.CheckOrder(),
   727  					},
   728  					{
   729  						VersionID:  savedVR2.ID(),
   730  						ResourceID: resource.ID(),
   731  						ScopeID:    resource.ResourceConfigScopeID(),
   732  						CheckOrder: savedVR2.CheckOrder(),
   733  					},
   734  				}))
   735  
   736  				Expect(versions.BuildOutputs).To(BeEmpty())
   737  				Expect(versions.Resources).To(ConsistOf([]atc.DebugResource{
   738  					{
   739  						ID:      resource.ID(),
   740  						Name:    resource.Name(),
   741  						ScopeID: intptr(resource.ResourceConfigScopeID()),
   742  					},
   743  					{
   744  						ID:      otherResource.ID(),
   745  						Name:    otherResource.Name(),
   746  						ScopeID: nil,
   747  					},
   748  					{
   749  						ID:      reallyOtherResource.ID(),
   750  						Name:    reallyOtherResource.Name(),
   751  						ScopeID: intptr(reallyOtherResource.ResourceConfigScopeID()),
   752  					},
   753  				}))
   754  				Expect(versions.Jobs).To(ConsistOf(jobs))
   755  
   756  				By("including outputs of successful builds")
   757  				build1DB, err := scenarioPipeline1.Job("a-job").CreateBuild()
   758  				Expect(err).ToNot(HaveOccurred())
   759  
   760  				err = build1DB.SaveOutput("some-type", atc.Source{"source-config": "some-value"}, atc.VersionedResourceTypes{}, atc.Version{"version": "1"}, nil, "some-output-name", "some-resource")
   761  				Expect(err).ToNot(HaveOccurred())
   762  
   763  				err = build1DB.Finish(db.BuildStatusSucceeded)
   764  				Expect(err).ToNot(HaveOccurred())
   765  
   766  				versions, err = scenarioPipeline1.Pipeline.LoadDebugVersionsDB()
   767  				Expect(err).ToNot(HaveOccurred())
   768  				Expect(versions.ResourceVersions).To(ConsistOf([]atc.DebugResourceVersion{
   769  					{VersionID: savedVR1.ID(), ResourceID: resource.ID(), ScopeID: resource.ResourceConfigScopeID(), CheckOrder: savedVR1.CheckOrder()},
   770  					{VersionID: savedVR2.ID(), ResourceID: resource.ID(), ScopeID: resource.ResourceConfigScopeID(), CheckOrder: savedVR2.CheckOrder()},
   771  				}))
   772  
   773  				explicitOutput := atc.DebugBuildOutput{
   774  					DebugResourceVersion: atc.DebugResourceVersion{
   775  						VersionID:  savedVR1.ID(),
   776  						ResourceID: resource.ID(),
   777  						ScopeID:    resource.ResourceConfigScopeID(),
   778  						CheckOrder: savedVR1.CheckOrder(),
   779  					},
   780  					JobID:   scenarioPipeline1.Job("a-job").ID(),
   781  					BuildID: build1DB.ID(),
   782  				}
   783  
   784  				Expect(versions.BuildOutputs).To(ConsistOf([]atc.DebugBuildOutput{
   785  					explicitOutput,
   786  				}))
   787  
   788  				Expect(versions.Resources).To(ConsistOf([]atc.DebugResource{
   789  					{
   790  						ID:      resource.ID(),
   791  						Name:    resource.Name(),
   792  						ScopeID: intptr(resource.ResourceConfigScopeID()),
   793  					},
   794  					{
   795  						ID:      otherResource.ID(),
   796  						Name:    otherResource.Name(),
   797  						ScopeID: nil,
   798  					},
   799  					{
   800  						ID:      reallyOtherResource.ID(),
   801  						Name:    reallyOtherResource.Name(),
   802  						ScopeID: intptr(reallyOtherResource.ResourceConfigScopeID()),
   803  					},
   804  				}))
   805  				Expect(versions.Jobs).To(ConsistOf(jobs))
   806  
   807  				By("not including outputs of failed builds")
   808  				build2DB, err := scenarioPipeline1.Job("a-job").CreateBuild()
   809  				Expect(err).ToNot(HaveOccurred())
   810  
   811  				err = build2DB.SaveOutput("some-type", atc.Source{"source-config": "some-value"}, atc.VersionedResourceTypes{}, atc.Version{"version": "1"}, nil, "some-output-name", "some-resource")
   812  				Expect(err).ToNot(HaveOccurred())
   813  
   814  				err = build2DB.Finish(db.BuildStatusFailed)
   815  				Expect(err).ToNot(HaveOccurred())
   816  
   817  				versions, err = scenarioPipeline1.Pipeline.LoadDebugVersionsDB()
   818  				Expect(err).ToNot(HaveOccurred())
   819  				Expect(versions.ResourceVersions).To(ConsistOf([]atc.DebugResourceVersion{
   820  					{VersionID: savedVR1.ID(), ResourceID: resource.ID(), ScopeID: resource.ResourceConfigScopeID(), CheckOrder: savedVR1.CheckOrder()},
   821  					{VersionID: savedVR2.ID(), ResourceID: resource.ID(), ScopeID: resource.ResourceConfigScopeID(), CheckOrder: savedVR2.CheckOrder()},
   822  				}))
   823  
   824  				Expect(versions.BuildOutputs).To(ConsistOf([]atc.DebugBuildOutput{
   825  					{
   826  						DebugResourceVersion: atc.DebugResourceVersion{
   827  							VersionID:  savedVR1.ID(),
   828  							ResourceID: resource.ID(),
   829  							ScopeID:    resource.ResourceConfigScopeID(),
   830  							CheckOrder: savedVR1.CheckOrder(),
   831  						},
   832  						JobID:   scenarioPipeline1.Job("a-job").ID(),
   833  						BuildID: build1DB.ID(),
   834  					},
   835  				}))
   836  
   837  				Expect(versions.Resources).To(ConsistOf([]atc.DebugResource{
   838  					{
   839  						ID:      resource.ID(),
   840  						Name:    resource.Name(),
   841  						ScopeID: intptr(resource.ResourceConfigScopeID()),
   842  					},
   843  					{
   844  						ID:      otherResource.ID(),
   845  						Name:    otherResource.Name(),
   846  						ScopeID: nil,
   847  					},
   848  					{
   849  						ID:      reallyOtherResource.ID(),
   850  						Name:    reallyOtherResource.Name(),
   851  						ScopeID: intptr(reallyOtherResource.ResourceConfigScopeID()),
   852  					},
   853  				}))
   854  				Expect(versions.Jobs).To(ConsistOf(jobs))
   855  
   856  				By("not including outputs of builds in other pipelines")
   857  				otherPipelineBuild, err := scenarioPipeline1.Job("a-job").CreateBuild()
   858  				Expect(err).ToNot(HaveOccurred())
   859  
   860  				err = otherPipelineBuild.SaveOutput("some-type", atc.Source{"other-source-config": "some-other-value"}, atc.VersionedResourceTypes{}, atc.Version{"version": "1"}, nil, "some-output-name", "some-other-resource")
   861  				Expect(err).ToNot(HaveOccurred())
   862  
   863  				// After SaveOutput to other resource, we need to reload it because its resourceConfigScopeID
   864  				// is supposed to be updated.
   865  				otherResource = scenarioPipeline1.Resource(otherResourceName)
   866  				Expect(otherResource).ToNot(BeNil())
   867  
   868  				savedVR3, found, err := otherResource.FindVersion(atc.Version{"version": "1"})
   869  				Expect(err).ToNot(HaveOccurred())
   870  				Expect(found).To(BeTrue())
   871  
   872  				err = otherPipelineBuild.Finish(db.BuildStatusSucceeded)
   873  				Expect(err).ToNot(HaveOccurred())
   874  
   875  				versions, err = scenarioPipeline1.Pipeline.LoadDebugVersionsDB()
   876  				Expect(err).ToNot(HaveOccurred())
   877  				Expect(versions.ResourceVersions).To(ConsistOf([]atc.DebugResourceVersion{
   878  					{VersionID: savedVR1.ID(), ResourceID: resource.ID(), ScopeID: resource.ResourceConfigScopeID(), CheckOrder: savedVR1.CheckOrder()},
   879  					{VersionID: savedVR2.ID(), ResourceID: resource.ID(), ScopeID: resource.ResourceConfigScopeID(), CheckOrder: savedVR2.CheckOrder()},
   880  					{VersionID: savedVR3.ID(), ResourceID: otherResource.ID(), ScopeID: otherResource.ResourceConfigScopeID(), CheckOrder: savedVR3.CheckOrder()},
   881  				}))
   882  
   883  				Expect(versions.BuildOutputs).To(ConsistOf([]atc.DebugBuildOutput{
   884  					{
   885  						DebugResourceVersion: atc.DebugResourceVersion{
   886  							VersionID:  savedVR3.ID(),
   887  							ResourceID: otherResource.ID(),
   888  							ScopeID:    otherResource.ResourceConfigScopeID(),
   889  							CheckOrder: savedVR3.CheckOrder(),
   890  						},
   891  						JobID:   scenarioPipeline1.Job("a-job").ID(),
   892  						BuildID: otherPipelineBuild.ID(),
   893  					},
   894  					{
   895  						DebugResourceVersion: atc.DebugResourceVersion{
   896  							VersionID:  savedVR1.ID(),
   897  							ResourceID: resource.ID(),
   898  							ScopeID:    resource.ResourceConfigScopeID(),
   899  							CheckOrder: savedVR1.CheckOrder(),
   900  						},
   901  						JobID:   scenarioPipeline1.Job("a-job").ID(),
   902  						BuildID: build1DB.ID(),
   903  					},
   904  				}))
   905  
   906  				Expect(versions.Resources).To(ConsistOf([]atc.DebugResource{
   907  					{
   908  						ID:      resource.ID(),
   909  						Name:    resource.Name(),
   910  						ScopeID: intptr(resource.ResourceConfigScopeID()),
   911  					},
   912  					{
   913  						ID:      otherResource.ID(),
   914  						Name:    otherResource.Name(),
   915  						ScopeID: intptr(otherResource.ResourceConfigScopeID()),
   916  					},
   917  					{
   918  						ID:      reallyOtherResource.ID(),
   919  						Name:    reallyOtherResource.Name(),
   920  						ScopeID: intptr(reallyOtherResource.ResourceConfigScopeID()),
   921  					},
   922  				}))
   923  				Expect(versions.Jobs).To(ConsistOf(jobs))
   924  
   925  				err = scenarioPipeline1.Job("a-job").SaveNextInputMapping(db.InputMapping{
   926  					"some-input-name": db.InputResult{
   927  						Input: &db.AlgorithmInput{
   928  							AlgorithmVersion: db.AlgorithmVersion{
   929  								Version:    db.ResourceVersion(convertToMD5(atc.Version{"version": "1"})),
   930  								ResourceID: resource.ID(),
   931  							},
   932  							FirstOccurrence: true,
   933  						},
   934  						PassedBuildIDs: []int{},
   935  					}}, true)
   936  				Expect(err).ToNot(HaveOccurred())
   937  
   938  				build1DB, err = scenarioPipeline1.Job("a-job").CreateBuild()
   939  				Expect(err).ToNot(HaveOccurred())
   940  
   941  				_, found, err = build1DB.AdoptInputsAndPipes()
   942  				Expect(err).ToNot(HaveOccurred())
   943  				Expect(found).To(BeTrue())
   944  
   945  				err = build1DB.Finish(db.BuildStatusSucceeded)
   946  				Expect(err).ToNot(HaveOccurred())
   947  
   948  				versions, err = scenarioPipeline1.Pipeline.LoadDebugVersionsDB()
   949  				Expect(err).ToNot(HaveOccurred())
   950  
   951  				Expect(versions.BuildInputs).To(ConsistOf([]atc.DebugBuildInput{
   952  					{
   953  						DebugResourceVersion: atc.DebugResourceVersion{
   954  							VersionID:  savedVR1.ID(),
   955  							ResourceID: resource.ID(),
   956  							ScopeID:    resource.ResourceConfigScopeID(),
   957  							CheckOrder: savedVR1.CheckOrder(),
   958  						},
   959  						JobID:     scenarioPipeline1.Job("a-job").ID(),
   960  						BuildID:   build1DB.ID(),
   961  						InputName: "some-input-name",
   962  					},
   963  				}))
   964  
   965  				By("including implicit outputs of successful builds")
   966  				implicitOutput := atc.DebugBuildOutput{
   967  					DebugResourceVersion: atc.DebugResourceVersion{
   968  						VersionID:  savedVR1.ID(),
   969  						ResourceID: resource.ID(),
   970  						ScopeID:    resource.ResourceConfigScopeID(),
   971  						CheckOrder: savedVR1.CheckOrder(),
   972  					},
   973  					JobID:   scenarioPipeline1.Job("a-job").ID(),
   974  					BuildID: build1DB.ID(),
   975  				}
   976  
   977  				By("including put-only resource's outputs of successful builds")
   978  				otherExplicitOutput := atc.DebugBuildOutput{
   979  					DebugResourceVersion: atc.DebugResourceVersion{
   980  						VersionID:  savedVR3.ID(),
   981  						ResourceID: otherResource.ID(),
   982  						ScopeID:    otherResource.ResourceConfigScopeID(),
   983  						CheckOrder: savedVR3.CheckOrder(),
   984  					},
   985  					JobID:   scenarioPipeline1.Job("a-job").ID(),
   986  					BuildID: otherPipelineBuild.ID(),
   987  				}
   988  
   989  				Expect(versions.BuildOutputs).To(ConsistOf([]atc.DebugBuildOutput{
   990  					otherExplicitOutput,
   991  					explicitOutput,
   992  					implicitOutput,
   993  				}))
   994  
   995  				By("including build rerun mappings for builds")
   996  				build2DB, err = scenarioPipeline1.Job("a-job").RerunBuild(build1DB)
   997  				Expect(err).ToNot(HaveOccurred())
   998  
   999  				versions, err = scenarioPipeline1.Pipeline.LoadDebugVersionsDB()
  1000  				Expect(err).ToNot(HaveOccurred())
  1001  
  1002  				Expect(versions.BuildReruns).To(ConsistOf([]atc.DebugBuildRerun{
  1003  					{
  1004  						JobID:   build1DB.JobID(),
  1005  						BuildID: build2DB.ID(),
  1006  						RerunOf: build1DB.ID(),
  1007  					},
  1008  				}))
  1009  			})
  1010  		})
  1011  
  1012  		It("can load up the latest versioned resource, enabled or not", func() {
  1013  			By("initially having no versions")
  1014  			resourceVersions, _, found, err := resource.Versions(db.Page{Limit: 10}, nil)
  1015  			Expect(err).ToNot(HaveOccurred())
  1016  			Expect(resourceVersions).To(HaveLen(0))
  1017  
  1018  			By("including saved versioned resources of the current pipeline")
  1019  			scenarioPipeline1.Run(builder.WithResourceVersions(resourceName, atc.Version{"version": "1"}))
  1020  
  1021  			savedVR1, found, err := resource.FindVersion(atc.Version{"version": "1"})
  1022  			Expect(err).ToNot(HaveOccurred())
  1023  			Expect(found).To(BeTrue())
  1024  
  1025  			scenarioPipeline1.Run(builder.WithResourceVersions(resourceName, atc.Version{"version": "2"}))
  1026  
  1027  			savedVR2, found, err := resource.FindVersion(atc.Version{"version": "2"})
  1028  			Expect(err).ToNot(HaveOccurred())
  1029  			Expect(found).To(BeTrue())
  1030  
  1031  			Expect(savedVR1.Version()).To(Equal(db.Version{"version": "1"}))
  1032  			Expect(savedVR2.Version()).To(Equal(db.Version{"version": "2"}))
  1033  
  1034  			By("not including saved versioned resources of other pipelines")
  1035  			scenarioPipeline2.Run(builder.WithResourceVersions(otherResourceName, atc.Version{"version": "3"}, atc.Version{"version": "4"}, atc.Version{"version": "5"}))
  1036  
  1037  			_, found, err = scenarioPipeline2.Resource(otherResourceName).FindVersion(atc.Version{"version": "3"})
  1038  			Expect(err).ToNot(HaveOccurred())
  1039  			Expect(found).To(BeTrue())
  1040  
  1041  			_, found, err = scenarioPipeline2.Resource(otherResourceName).FindVersion(atc.Version{"version": "1"})
  1042  			Expect(err).ToNot(HaveOccurred())
  1043  			Expect(found).To(BeFalse())
  1044  
  1045  			By("including disabled versions")
  1046  			err = resource.DisableVersion(savedVR2.ID())
  1047  			Expect(err).ToNot(HaveOccurred())
  1048  
  1049  			latestVR, found, err := resource.FindVersion(atc.Version{"version": "2"})
  1050  			Expect(err).ToNot(HaveOccurred())
  1051  			Expect(found).To(BeTrue())
  1052  
  1053  			Expect(latestVR.Version()).To(Equal(db.Version{"version": "2"}))
  1054  		})
  1055  
  1056  		It("initially has no pending build for a job", func() {
  1057  			pendingBuilds, err := scenarioPipeline1.Job("some-job").GetPendingBuilds()
  1058  			Expect(err).ToNot(HaveOccurred())
  1059  			Expect(pendingBuilds).To(HaveLen(0))
  1060  		})
  1061  	})
  1062  
  1063  	Describe("Destroy", func() {
  1064  		var scenario *dbtest.Scenario
  1065  
  1066  		It("removes the pipeline and all of its data", func() {
  1067  			By("populating resources table")
  1068  			scenario = dbtest.Setup(
  1069  				builder.WithPipeline(atc.Config{
  1070  					Resources: atc.ResourceConfigs{
  1071  						{
  1072  							Name:   "some-resource",
  1073  							Type:   "some-type",
  1074  							Source: atc.Source{"some": "source"},
  1075  						},
  1076  					},
  1077  					Jobs: atc.JobConfigs{
  1078  						{
  1079  							Name: "some-job",
  1080  							PlanSequence: []atc.Step{
  1081  								{
  1082  									Config: &atc.GetStep{
  1083  										Name:     "some-input",
  1084  										Resource: "some-resource",
  1085  									},
  1086  								},
  1087  							},
  1088  						},
  1089  					},
  1090  				}),
  1091  				builder.WithResourceVersions("some-resource", atc.Version{"key": "value"}),
  1092  			)
  1093  
  1094  			By("populating builds")
  1095  			build, err := scenario.Job("some-job").CreateBuild()
  1096  			Expect(err).ToNot(HaveOccurred())
  1097  
  1098  			By("populating build inputs")
  1099  			err = scenario.Job("some-job").SaveNextInputMapping(db.InputMapping{
  1100  				"build-input": db.InputResult{
  1101  					Input: &db.AlgorithmInput{
  1102  						AlgorithmVersion: db.AlgorithmVersion{
  1103  							Version:    db.ResourceVersion(convertToMD5(atc.Version{"key": "value"})),
  1104  							ResourceID: scenario.Resource("some-resource").ID(),
  1105  						},
  1106  						FirstOccurrence: true,
  1107  					},
  1108  					PassedBuildIDs: []int{},
  1109  				}}, true)
  1110  			Expect(err).ToNot(HaveOccurred())
  1111  
  1112  			_, found, err := build.AdoptInputsAndPipes()
  1113  			Expect(err).ToNot(HaveOccurred())
  1114  			Expect(found).To(BeTrue())
  1115  
  1116  			By("populating build outputs")
  1117  			err = build.SaveOutput("some-type", atc.Source{"some": "source"}, atc.VersionedResourceTypes{}, atc.Version{"key": "value"}, nil, "some-output-name", "some-resource")
  1118  			Expect(err).ToNot(HaveOccurred())
  1119  
  1120  			By("populating build events")
  1121  			err = build.SaveEvent(event.StartTask{})
  1122  			Expect(err).ToNot(HaveOccurred())
  1123  
  1124  			err = scenario.Pipeline.Destroy()
  1125  			Expect(err).ToNot(HaveOccurred())
  1126  
  1127  			found, err = scenario.Pipeline.Reload()
  1128  			Expect(err).ToNot(HaveOccurred())
  1129  			Expect(found).To(BeFalse())
  1130  
  1131  			found, err = build.Reload()
  1132  			Expect(err).ToNot(HaveOccurred())
  1133  			Expect(found).To(BeFalse())
  1134  
  1135  			_, found, err = scenario.Team.Pipeline(atc.PipelineRef{Name: pipeline.Name()})
  1136  			Expect(err).ToNot(HaveOccurred())
  1137  			Expect(found).To(BeFalse())
  1138  		})
  1139  
  1140  		It("marks the pipeline ID in the deleted_pipelines table", func() {
  1141  			destroy(pipeline)
  1142  
  1143  			var exists bool
  1144  			err := dbConn.QueryRow(fmt.Sprintf("SELECT EXISTS (SELECT 1 FROM deleted_pipelines WHERE id = %d)", pipeline.ID())).Scan(&exists)
  1145  			Expect(err).ToNot(HaveOccurred())
  1146  			Expect(exists).To(BeTrue(), "did not mark the pipeline id in deleted_pipelines")
  1147  		})
  1148  	})
  1149  
  1150  	Describe("Dashboard", func() {
  1151  		It("returns a Dashboard object with a DashboardJob corresponding to each configured job", func() {
  1152  			job, found, err := pipeline.Job("job-name")
  1153  			Expect(err).ToNot(HaveOccurred())
  1154  			Expect(found).To(BeTrue())
  1155  
  1156  			err = job.UpdateFirstLoggedBuildID(57)
  1157  			Expect(err).ToNot(HaveOccurred())
  1158  
  1159  			otherJob, found, err := pipeline.Job("some-other-job")
  1160  			Expect(err).ToNot(HaveOccurred())
  1161  			Expect(found).To(BeTrue())
  1162  
  1163  			aJob, found, err := pipeline.Job("a-job")
  1164  			Expect(err).ToNot(HaveOccurred())
  1165  			Expect(found).To(BeTrue())
  1166  
  1167  			sharedJob, found, err := pipeline.Job("shared-job")
  1168  			Expect(err).ToNot(HaveOccurred())
  1169  			Expect(found).To(BeTrue())
  1170  
  1171  			randomJob, found, err := pipeline.Job("random-job")
  1172  			Expect(err).ToNot(HaveOccurred())
  1173  			Expect(found).To(BeTrue())
  1174  
  1175  			job1, found, err := pipeline.Job("job-1")
  1176  			Expect(err).ToNot(HaveOccurred())
  1177  			Expect(found).To(BeTrue())
  1178  
  1179  			job2, found, err := pipeline.Job("job-2")
  1180  			Expect(err).ToNot(HaveOccurred())
  1181  			Expect(found).To(BeTrue())
  1182  
  1183  			otherSerialGroupJob, found, err := pipeline.Job("other-serial-group-job")
  1184  			Expect(err).ToNot(HaveOccurred())
  1185  			Expect(found).To(BeTrue())
  1186  
  1187  			differentSerialGroupJob, found, err := pipeline.Job("different-serial-group-job")
  1188  			Expect(err).ToNot(HaveOccurred())
  1189  			Expect(found).To(BeTrue())
  1190  
  1191  			By("returning jobs with no builds")
  1192  			actualDashboard, err := pipeline.Dashboard()
  1193  			Expect(err).ToNot(HaveOccurred())
  1194  
  1195  			Expect(actualDashboard[0].Name).To(Equal(job.Name()))
  1196  			Expect(actualDashboard[1].Name).To(Equal(otherJob.Name()))
  1197  			Expect(actualDashboard[2].Name).To(Equal(aJob.Name()))
  1198  			Expect(actualDashboard[3].Name).To(Equal(sharedJob.Name()))
  1199  			Expect(actualDashboard[4].Name).To(Equal(randomJob.Name()))
  1200  			Expect(actualDashboard[5].Name).To(Equal(job1.Name()))
  1201  			Expect(actualDashboard[6].Name).To(Equal(job2.Name()))
  1202  			Expect(actualDashboard[7].Name).To(Equal(otherSerialGroupJob.Name()))
  1203  			Expect(actualDashboard[8].Name).To(Equal(differentSerialGroupJob.Name()))
  1204  
  1205  			By("returning a job's most recent pending build if there are no running builds")
  1206  			job, found, err = pipeline.Job("job-name")
  1207  			Expect(err).ToNot(HaveOccurred())
  1208  			Expect(found).To(BeTrue())
  1209  
  1210  			firstJobBuild, err := job.CreateBuild()
  1211  			Expect(err).ToNot(HaveOccurred())
  1212  
  1213  			actualDashboard, err = pipeline.Dashboard()
  1214  			Expect(err).ToNot(HaveOccurred())
  1215  
  1216  			Expect(actualDashboard[0].Name).To(Equal(job.Name()))
  1217  			Expect(actualDashboard[0].NextBuild.ID).To(Equal(firstJobBuild.ID()))
  1218  
  1219  			By("returning a job's most recent started build")
  1220  			found, err = firstJobBuild.Start(atc.Plan{ID: "some-id"})
  1221  			Expect(err).ToNot(HaveOccurred())
  1222  			Expect(found).To(BeTrue())
  1223  
  1224  			found, err = firstJobBuild.Reload()
  1225  			Expect(err).ToNot(HaveOccurred())
  1226  			Expect(found).To(BeTrue())
  1227  
  1228  			actualDashboard, err = pipeline.Dashboard()
  1229  			Expect(err).ToNot(HaveOccurred())
  1230  
  1231  			Expect(actualDashboard[0].Name).To(Equal(job.Name()))
  1232  			Expect(actualDashboard[0].NextBuild.ID).To(Equal(firstJobBuild.ID()))
  1233  			Expect(actualDashboard[0].NextBuild.Status).To(Equal(atc.StatusStarted))
  1234  
  1235  			By("returning a job's most recent started build even if there is a newer pending build")
  1236  			job, found, err = pipeline.Job("job-name")
  1237  			Expect(err).ToNot(HaveOccurred())
  1238  			Expect(found).To(BeTrue())
  1239  
  1240  			secondJobBuild, err := job.CreateBuild()
  1241  			Expect(err).ToNot(HaveOccurred())
  1242  
  1243  			actualDashboard, err = pipeline.Dashboard()
  1244  			Expect(err).ToNot(HaveOccurred())
  1245  
  1246  			Expect(actualDashboard[0].Name).To(Equal(job.Name()))
  1247  			Expect(actualDashboard[0].NextBuild.ID).To(Equal(firstJobBuild.ID()))
  1248  
  1249  			By("returning a job's most recent finished build")
  1250  			err = firstJobBuild.Finish(db.BuildStatusSucceeded)
  1251  			Expect(err).ToNot(HaveOccurred())
  1252  
  1253  			err = secondJobBuild.Finish(db.BuildStatusSucceeded)
  1254  			Expect(err).ToNot(HaveOccurred())
  1255  
  1256  			found, err = secondJobBuild.Reload()
  1257  			Expect(err).ToNot(HaveOccurred())
  1258  			Expect(found).To(BeTrue())
  1259  
  1260  			actualDashboard, err = pipeline.Dashboard()
  1261  			Expect(err).ToNot(HaveOccurred())
  1262  
  1263  			Expect(actualDashboard[0].Name).To(Equal(job.Name()))
  1264  			Expect(actualDashboard[0].NextBuild).To(BeNil())
  1265  			Expect(actualDashboard[0].FinishedBuild.ID).To(Equal(secondJobBuild.ID()))
  1266  
  1267  			By("returning the job inputs and outputs")
  1268  			Expect(actualDashboard[0].Outputs).To(ConsistOf(atc.JobOutputSummary{
  1269  				Name:     "some-resource",
  1270  				Resource: "some-resource",
  1271  			}))
  1272  			Expect(actualDashboard[0].Inputs).To(ConsistOf(atc.JobInputSummary{
  1273  				Name:     "some-input",
  1274  				Resource: "some-resource",
  1275  				Passed:   []string{"job-1", "job-2"},
  1276  				Trigger:  true,
  1277  			}))
  1278  		})
  1279  	})
  1280  
  1281  	Describe("DeleteBuildEventsByBuildIDs", func() {
  1282  		It("deletes all build logs corresponding to the given build ids", func() {
  1283  			build1DB, err := team.CreateOneOffBuild()
  1284  			Expect(err).ToNot(HaveOccurred())
  1285  
  1286  			err = build1DB.SaveEvent(event.Log{
  1287  				Payload: "log 1",
  1288  			})
  1289  			Expect(err).ToNot(HaveOccurred())
  1290  
  1291  			build2DB, err := team.CreateOneOffBuild()
  1292  			Expect(err).ToNot(HaveOccurred())
  1293  
  1294  			err = build2DB.SaveEvent(event.Log{
  1295  				Payload: "log 2",
  1296  			})
  1297  			Expect(err).ToNot(HaveOccurred())
  1298  
  1299  			build3DB, err := team.CreateOneOffBuild()
  1300  			Expect(err).ToNot(HaveOccurred())
  1301  
  1302  			err = build3DB.Finish(db.BuildStatusSucceeded)
  1303  			Expect(err).ToNot(HaveOccurred())
  1304  
  1305  			err = build1DB.Finish(db.BuildStatusSucceeded)
  1306  			Expect(err).ToNot(HaveOccurred())
  1307  
  1308  			err = build2DB.Finish(db.BuildStatusSucceeded)
  1309  			Expect(err).ToNot(HaveOccurred())
  1310  
  1311  			build4DB, err := team.CreateOneOffBuild()
  1312  			Expect(err).ToNot(HaveOccurred())
  1313  
  1314  			By("doing nothing if the list is empty")
  1315  			err = pipeline.DeleteBuildEventsByBuildIDs([]int{})
  1316  			Expect(err).ToNot(HaveOccurred())
  1317  
  1318  			By("not returning an error")
  1319  			err = pipeline.DeleteBuildEventsByBuildIDs([]int{build3DB.ID(), build4DB.ID(), build1DB.ID()})
  1320  			Expect(err).ToNot(HaveOccurred())
  1321  
  1322  			err = build4DB.Finish(db.BuildStatusSucceeded)
  1323  			Expect(err).ToNot(HaveOccurred())
  1324  
  1325  			By("deleting events for build 1")
  1326  			events1, err := build1DB.Events(0)
  1327  			Expect(err).ToNot(HaveOccurred())
  1328  			defer db.Close(events1)
  1329  
  1330  			_, err = events1.Next()
  1331  			Expect(err).To(Equal(db.ErrEndOfBuildEventStream))
  1332  
  1333  			By("preserving events for build 2")
  1334  			events2, err := build2DB.Events(0)
  1335  			Expect(err).ToNot(HaveOccurred())
  1336  			defer db.Close(events2)
  1337  
  1338  			build2Event1, err := events2.Next()
  1339  			Expect(err).ToNot(HaveOccurred())
  1340  			Expect(build2Event1).To(Equal(envelope(event.Log{
  1341  				Payload: "log 2",
  1342  			})))
  1343  
  1344  			_, err = events2.Next() // finish event
  1345  			Expect(err).ToNot(HaveOccurred())
  1346  
  1347  			_, err = events2.Next()
  1348  			Expect(err).To(Equal(db.ErrEndOfBuildEventStream))
  1349  
  1350  			By("deleting events for build 3")
  1351  			events3, err := build3DB.Events(0)
  1352  			Expect(err).ToNot(HaveOccurred())
  1353  			defer db.Close(events3)
  1354  
  1355  			_, err = events3.Next()
  1356  			Expect(err).To(Equal(db.ErrEndOfBuildEventStream))
  1357  
  1358  			By("being unflapped by build 4, which had no events at the time")
  1359  			events4, err := build4DB.Events(0)
  1360  			Expect(err).ToNot(HaveOccurred())
  1361  			defer db.Close(events4)
  1362  
  1363  			_, err = events4.Next() // finish event
  1364  			Expect(err).ToNot(HaveOccurred())
  1365  
  1366  			_, err = events4.Next()
  1367  			Expect(err).To(Equal(db.ErrEndOfBuildEventStream))
  1368  
  1369  			By("updating ReapTime for the affected builds")
  1370  			found, err := build1DB.Reload()
  1371  			Expect(err).ToNot(HaveOccurred())
  1372  			Expect(found).To(BeTrue())
  1373  
  1374  			Expect(build1DB.ReapTime()).To(BeTemporally(">", build1DB.EndTime()))
  1375  
  1376  			found, err = build2DB.Reload()
  1377  			Expect(err).ToNot(HaveOccurred())
  1378  			Expect(found).To(BeTrue())
  1379  
  1380  			Expect(build2DB.ReapTime()).To(BeZero())
  1381  
  1382  			found, err = build3DB.Reload()
  1383  			Expect(err).ToNot(HaveOccurred())
  1384  			Expect(found).To(BeTrue())
  1385  
  1386  			Expect(build3DB.ReapTime()).To(Equal(build1DB.ReapTime()))
  1387  
  1388  			found, err = build4DB.Reload()
  1389  			Expect(err).ToNot(HaveOccurred())
  1390  			Expect(found).To(BeTrue())
  1391  
  1392  			// Not required behavior, just a sanity check for what I think will happen
  1393  			Expect(build4DB.ReapTime()).To(Equal(build1DB.ReapTime()))
  1394  		})
  1395  	})
  1396  
  1397  	Describe("Jobs", func() {
  1398  		var jobs []db.Job
  1399  
  1400  		BeforeEach(func() {
  1401  			var err error
  1402  			jobs, err = pipeline.Jobs()
  1403  			Expect(err).ToNot(HaveOccurred())
  1404  		})
  1405  
  1406  		It("returns all the jobs", func() {
  1407  			Expect(jobs[0].Name()).To(Equal("job-name"))
  1408  			Expect(jobs[1].Name()).To(Equal("some-other-job"))
  1409  			Expect(jobs[2].Name()).To(Equal("a-job"))
  1410  			Expect(jobs[3].Name()).To(Equal("shared-job"))
  1411  			Expect(jobs[4].Name()).To(Equal("random-job"))
  1412  			Expect(jobs[5].Name()).To(Equal("job-1"))
  1413  			Expect(jobs[6].Name()).To(Equal("job-2"))
  1414  			Expect(jobs[7].Name()).To(Equal("other-serial-group-job"))
  1415  			Expect(jobs[8].Name()).To(Equal("different-serial-group-job"))
  1416  		})
  1417  	})
  1418  
  1419  	Describe("GetBuildsWithVersionAsInput", func() {
  1420  		var (
  1421  			resourceConfigVersion int
  1422  			expectedBuilds        []int
  1423  			dbSecondBuild         db.Build
  1424  			scenario              *dbtest.Scenario
  1425  		)
  1426  
  1427  		BeforeEach(func() {
  1428  			scenario = dbtest.Setup(
  1429  				builder.WithPipeline(atc.Config{
  1430  					Jobs: atc.JobConfigs{
  1431  						{
  1432  							Name: "job-name",
  1433  							PlanSequence: []atc.Step{
  1434  								{
  1435  									Config: &atc.GetStep{
  1436  										Name:     "some-input",
  1437  										Resource: "some-resource",
  1438  									},
  1439  								},
  1440  							},
  1441  						},
  1442  						{
  1443  							Name:   "some-other-job",
  1444  							Serial: true,
  1445  						},
  1446  					},
  1447  					Resources: atc.ResourceConfigs{
  1448  						{
  1449  							Name:   "some-resource",
  1450  							Type:   "some-type",
  1451  							Source: atc.Source{"some": "source"},
  1452  						},
  1453  					},
  1454  				}),
  1455  				builder.WithResourceVersions("some-resource", atc.Version{"version": "v1"}),
  1456  			)
  1457  
  1458  			build, err := scenario.Job("job-name").CreateBuild()
  1459  			Expect(err).ToNot(HaveOccurred())
  1460  			expectedBuilds = append(expectedBuilds, build.ID())
  1461  
  1462  			secondBuild, err := scenario.Job("job-name").CreateBuild()
  1463  			Expect(err).ToNot(HaveOccurred())
  1464  			expectedBuilds = append(expectedBuilds, secondBuild.ID())
  1465  
  1466  			_, err = scenario.Job("some-other-job").CreateBuild()
  1467  			Expect(err).ToNot(HaveOccurred())
  1468  
  1469  			dbBuild, found, err := buildFactory.Build(build.ID())
  1470  			Expect(err).ToNot(HaveOccurred())
  1471  			Expect(found).To(BeTrue())
  1472  
  1473  			err = scenario.Job("job-name").SaveNextInputMapping(db.InputMapping{
  1474  				"some-input": db.InputResult{
  1475  					Input: &db.AlgorithmInput{
  1476  						AlgorithmVersion: db.AlgorithmVersion{
  1477  							Version:    db.ResourceVersion(convertToMD5(atc.Version{"version": "v1"})),
  1478  							ResourceID: scenario.Resource("some-resource").ID(),
  1479  						},
  1480  						FirstOccurrence: true,
  1481  					},
  1482  					PassedBuildIDs: []int{},
  1483  				}}, true)
  1484  			Expect(err).ToNot(HaveOccurred())
  1485  
  1486  			_, found, err = dbBuild.AdoptInputsAndPipes()
  1487  			Expect(err).ToNot(HaveOccurred())
  1488  			Expect(found).To(BeTrue())
  1489  
  1490  			dbSecondBuild, found, err = buildFactory.Build(secondBuild.ID())
  1491  			Expect(err).ToNot(HaveOccurred())
  1492  			Expect(found).To(BeTrue())
  1493  
  1494  			scenario.Run(builder.WithResourceVersions(
  1495  				"some-resource",
  1496  				atc.Version{"version": "v2"},
  1497  				atc.Version{"version": "v3"},
  1498  				atc.Version{"version": "v4"},
  1499  			))
  1500  
  1501  			err = scenario.Job("job-name").SaveNextInputMapping(db.InputMapping{
  1502  				"some-input": db.InputResult{
  1503  					Input: &db.AlgorithmInput{
  1504  						AlgorithmVersion: db.AlgorithmVersion{
  1505  							Version:    db.ResourceVersion(convertToMD5(atc.Version{"version": "v1"})),
  1506  							ResourceID: scenario.Resource("some-resource").ID(),
  1507  						},
  1508  						FirstOccurrence: true,
  1509  					},
  1510  					PassedBuildIDs: []int{},
  1511  				},
  1512  				"some-other-input": db.InputResult{
  1513  					Input: &db.AlgorithmInput{
  1514  						AlgorithmVersion: db.AlgorithmVersion{
  1515  							Version:    db.ResourceVersion(convertToMD5(atc.Version{"version": "v3"})),
  1516  							ResourceID: scenario.Resource("some-resource").ID(),
  1517  						},
  1518  						FirstOccurrence: true,
  1519  					},
  1520  					PassedBuildIDs: []int{},
  1521  				},
  1522  			}, true)
  1523  			Expect(err).ToNot(HaveOccurred())
  1524  
  1525  			_, found, err = dbSecondBuild.AdoptInputsAndPipes()
  1526  			Expect(err).ToNot(HaveOccurred())
  1527  			Expect(found).To(BeTrue())
  1528  
  1529  			rcv1 := scenario.ResourceVersion("some-resource", atc.Version{"version": "v1"})
  1530  
  1531  			resourceConfigVersion = rcv1.ID()
  1532  		})
  1533  
  1534  		It("returns the two builds for which the provided version id was an input", func() {
  1535  			builds, err := pipeline.GetBuildsWithVersionAsInput(scenario.Resource("some-resource").ID(), resourceConfigVersion)
  1536  			Expect(err).ToNot(HaveOccurred())
  1537  			Expect(builds).To(HaveLen(len(expectedBuilds)))
  1538  
  1539  			buildIDs := []int{}
  1540  			for _, b := range builds {
  1541  				buildIDs = append(buildIDs, b.ID())
  1542  			}
  1543  
  1544  			Expect(buildIDs).To(ConsistOf(expectedBuilds))
  1545  		})
  1546  
  1547  		It("returns the one build that uses the version as an input", func() {
  1548  			rcv3 := scenario.ResourceVersion("some-resource", atc.Version{"version": "v3"})
  1549  
  1550  			builds, err := pipeline.GetBuildsWithVersionAsInput(scenario.Resource("some-resource").ID(), rcv3.ID())
  1551  			Expect(err).ToNot(HaveOccurred())
  1552  			Expect(builds).To(HaveLen(1))
  1553  			Expect(builds[0].ID()).To(Equal(dbSecondBuild.ID()))
  1554  		})
  1555  
  1556  		It("returns an empty slice of builds when the provided version id exists but is not used", func() {
  1557  			rcv4 := scenario.ResourceVersion("some-resource", atc.Version{"version": "v4"})
  1558  
  1559  			builds, err := pipeline.GetBuildsWithVersionAsInput(scenario.Resource("some-resource").ID(), rcv4.ID())
  1560  			Expect(err).ToNot(HaveOccurred())
  1561  			Expect(builds).To(Equal([]db.Build{}))
  1562  		})
  1563  
  1564  		It("returns an empty slice of builds when the provided version id doesn't exist", func() {
  1565  			builds, err := pipeline.GetBuildsWithVersionAsInput(scenario.Resource("some-resource").ID(), resourceConfigVersion+100)
  1566  			Expect(err).ToNot(HaveOccurred())
  1567  			Expect(builds).To(Equal([]db.Build{}))
  1568  		})
  1569  
  1570  		It("returns an empty slice of builds when the provided resource id doesn't exist", func() {
  1571  			builds, err := pipeline.GetBuildsWithVersionAsInput(10293912, resourceConfigVersion)
  1572  			Expect(err).ToNot(HaveOccurred())
  1573  			Expect(builds).To(Equal([]db.Build{}))
  1574  		})
  1575  	})
  1576  
  1577  	Describe("GetBuildsWithVersionAsOutput", func() {
  1578  		var (
  1579  			resourceConfigVersion int
  1580  			expectedBuilds        []db.Build
  1581  			secondBuild           db.Build
  1582  			scenario              *dbtest.Scenario
  1583  		)
  1584  
  1585  		BeforeEach(func() {
  1586  			scenario = dbtest.Setup(
  1587  				builder.WithPipeline(atc.Config{
  1588  					Jobs: atc.JobConfigs{
  1589  						{
  1590  							Name: "job-name",
  1591  							PlanSequence: []atc.Step{
  1592  								{
  1593  									Config: &atc.GetStep{
  1594  										Name:     "some-input",
  1595  										Resource: "some-resource",
  1596  									},
  1597  								},
  1598  							},
  1599  						},
  1600  						{
  1601  							Name:   "some-other-job",
  1602  							Serial: true,
  1603  						},
  1604  					},
  1605  					Resources: atc.ResourceConfigs{
  1606  						{
  1607  							Name:   "some-resource",
  1608  							Type:   "some-type",
  1609  							Source: atc.Source{"some": "source"},
  1610  						},
  1611  					},
  1612  				}),
  1613  				builder.WithResourceVersions("some-resource", atc.Version{"version": "v3"}, atc.Version{"version": "v4"}),
  1614  			)
  1615  
  1616  			build, err := scenario.Job("job-name").CreateBuild()
  1617  			Expect(err).ToNot(HaveOccurred())
  1618  			expectedBuilds = append(expectedBuilds, build)
  1619  
  1620  			secondBuild, err = scenario.Job("job-name").CreateBuild()
  1621  			Expect(err).ToNot(HaveOccurred())
  1622  			expectedBuilds = append(expectedBuilds, secondBuild)
  1623  
  1624  			_, err = scenario.Job("some-other-job").CreateBuild()
  1625  			Expect(err).ToNot(HaveOccurred())
  1626  
  1627  			dbBuild, found, err := buildFactory.Build(build.ID())
  1628  			Expect(err).ToNot(HaveOccurred())
  1629  			Expect(found).To(BeTrue())
  1630  
  1631  			err = dbBuild.SaveOutput("some-type", atc.Source{"some": "source"}, atc.VersionedResourceTypes{}, atc.Version{"version": "v1"}, []db.ResourceConfigMetadataField{
  1632  				{
  1633  					Name:  "some",
  1634  					Value: "value",
  1635  				},
  1636  			}, "some-output-name", "some-resource")
  1637  			Expect(err).ToNot(HaveOccurred())
  1638  
  1639  			dbSecondBuild, found, err := buildFactory.Build(secondBuild.ID())
  1640  			Expect(err).ToNot(HaveOccurred())
  1641  			Expect(found).To(BeTrue())
  1642  
  1643  			err = dbSecondBuild.SaveOutput("some-type", atc.Source{"some": "source"}, atc.VersionedResourceTypes{}, atc.Version{"version": "v1"}, []db.ResourceConfigMetadataField{
  1644  				{
  1645  					Name:  "some",
  1646  					Value: "value",
  1647  				},
  1648  			}, "some-output-name", "some-resource")
  1649  			Expect(err).ToNot(HaveOccurred())
  1650  
  1651  			err = dbSecondBuild.SaveOutput("some-type", atc.Source{"some": "source"}, atc.VersionedResourceTypes{}, atc.Version{"version": "v3"}, nil, "some-output-name", "some-resource")
  1652  			Expect(err).ToNot(HaveOccurred())
  1653  
  1654  			rcv1 := scenario.ResourceVersion("some-resource", atc.Version{"version": "v1"})
  1655  
  1656  			resourceConfigVersion = rcv1.ID()
  1657  		})
  1658  
  1659  		It("returns the two builds for which the provided version id was an output", func() {
  1660  			builds, err := pipeline.GetBuildsWithVersionAsOutput(scenario.Resource("some-resource").ID(), resourceConfigVersion)
  1661  			Expect(err).ToNot(HaveOccurred())
  1662  			Expect(builds).To(ConsistOf(expectedBuilds))
  1663  		})
  1664  
  1665  		It("returns the one build that uses the version as an input", func() {
  1666  			rcv3 := scenario.ResourceVersion("some-resource", atc.Version{"version": "v3"})
  1667  
  1668  			builds, err := pipeline.GetBuildsWithVersionAsOutput(scenario.Resource("some-resource").ID(), rcv3.ID())
  1669  			Expect(err).ToNot(HaveOccurred())
  1670  			Expect(builds).To(HaveLen(1))
  1671  			Expect(builds[0].ID()).To(Equal(secondBuild.ID()))
  1672  		})
  1673  
  1674  		It("returns an empty slice of builds when the provided version id exists but is not used", func() {
  1675  			rcv4 := scenario.ResourceVersion("some-resource", atc.Version{"version": "v4"})
  1676  
  1677  			builds, err := pipeline.GetBuildsWithVersionAsOutput(scenario.Resource("some-resource").ID(), rcv4.ID())
  1678  			Expect(err).ToNot(HaveOccurred())
  1679  			Expect(builds).To(Equal([]db.Build{}))
  1680  		})
  1681  
  1682  		It("returns an empty slice of builds when the provided resource id doesn't exist", func() {
  1683  			builds, err := pipeline.GetBuildsWithVersionAsOutput(10293912, resourceConfigVersion)
  1684  			Expect(err).ToNot(HaveOccurred())
  1685  			Expect(builds).To(Equal([]db.Build{}))
  1686  		})
  1687  
  1688  		It("returns an empty slice of builds when the provided version id doesn't exist", func() {
  1689  			builds, err := pipeline.GetBuildsWithVersionAsOutput(scenario.Resource("some-resource").ID(), resourceConfigVersion+100)
  1690  			Expect(err).ToNot(HaveOccurred())
  1691  			Expect(builds).To(Equal([]db.Build{}))
  1692  		})
  1693  	})
  1694  
  1695  	Describe("Builds", func() {
  1696  		var expectedBuilds []db.Build
  1697  
  1698  		BeforeEach(func() {
  1699  			_, err := team.CreateOneOffBuild()
  1700  			Expect(err).NotTo(HaveOccurred())
  1701  
  1702  			job, found, err := pipeline.Job("job-name")
  1703  			Expect(err).ToNot(HaveOccurred())
  1704  			Expect(found).To(BeTrue())
  1705  
  1706  			build, err := job.CreateBuild()
  1707  			Expect(err).ToNot(HaveOccurred())
  1708  			expectedBuilds = append(expectedBuilds, build)
  1709  
  1710  			secondBuild, err := job.CreateBuild()
  1711  			Expect(err).ToNot(HaveOccurred())
  1712  			expectedBuilds = append(expectedBuilds, secondBuild)
  1713  
  1714  			someOtherJob, found, err := pipeline.Job("some-other-job")
  1715  			Expect(err).ToNot(HaveOccurred())
  1716  			Expect(found).To(BeTrue())
  1717  
  1718  			thirdBuild, err := someOtherJob.CreateBuild()
  1719  			Expect(err).ToNot(HaveOccurred())
  1720  			expectedBuilds = append(expectedBuilds, thirdBuild)
  1721  		})
  1722  
  1723  		It("returns builds for the current pipeline", func() {
  1724  			builds, _, err := pipeline.Builds(db.Page{Limit: 10})
  1725  			Expect(err).NotTo(HaveOccurred())
  1726  			Expect(builds).To(ConsistOf(expectedBuilds))
  1727  		})
  1728  	})
  1729  
  1730  	Describe("CreateStartedBuild", func() {
  1731  		var (
  1732  			plan         atc.Plan
  1733  			startedBuild db.Build
  1734  			err          error
  1735  		)
  1736  
  1737  		BeforeEach(func() {
  1738  			plan = atc.Plan{
  1739  				ID: atc.PlanID("56"),
  1740  				Get: &atc.GetPlan{
  1741  					Type:     "some-type",
  1742  					Name:     "some-name",
  1743  					Resource: "some-resource",
  1744  					Source:   atc.Source{"some": "source"},
  1745  					Params:   atc.Params{"some": "params"},
  1746  					Version:  &atc.Version{"some": "version"},
  1747  					Tags:     atc.Tags{"some-tags"},
  1748  					VersionedResourceTypes: atc.VersionedResourceTypes{
  1749  						{
  1750  							ResourceType: atc.ResourceType{
  1751  								Name:       "some-name",
  1752  								Source:     atc.Source{"some": "source"},
  1753  								Type:       "some-type",
  1754  								Privileged: true,
  1755  								Tags:       atc.Tags{"some-tags"},
  1756  							},
  1757  							Version: atc.Version{"some-resource-type": "version"},
  1758  						},
  1759  					},
  1760  				},
  1761  			}
  1762  
  1763  			startedBuild, err = pipeline.CreateStartedBuild(plan)
  1764  			Expect(err).ToNot(HaveOccurred())
  1765  		})
  1766  
  1767  		It("can create started builds with plans", func() {
  1768  			Expect(startedBuild.ID()).ToNot(BeZero())
  1769  			Expect(startedBuild.JobName()).To(BeZero())
  1770  			Expect(startedBuild.PipelineName()).To(Equal("fake-pipeline"))
  1771  			Expect(startedBuild.Name()).To(Equal(strconv.Itoa(startedBuild.ID())))
  1772  			Expect(startedBuild.TeamName()).To(Equal(team.Name()))
  1773  			Expect(startedBuild.Status()).To(Equal(db.BuildStatusStarted))
  1774  		})
  1775  
  1776  		It("saves the public plan", func() {
  1777  			found, err := startedBuild.Reload()
  1778  			Expect(err).NotTo(HaveOccurred())
  1779  			Expect(found).To(BeTrue())
  1780  			Expect(startedBuild.PublicPlan()).To(Equal(plan.Public()))
  1781  		})
  1782  
  1783  		It("creates Start event", func() {
  1784  			found, err := startedBuild.Reload()
  1785  			Expect(err).NotTo(HaveOccurred())
  1786  			Expect(found).To(BeTrue())
  1787  
  1788  			events, err := startedBuild.Events(0)
  1789  			Expect(err).NotTo(HaveOccurred())
  1790  
  1791  			defer db.Close(events)
  1792  
  1793  			Expect(events.Next()).To(Equal(envelope(event.Status{
  1794  				Status: atc.StatusStarted,
  1795  				Time:   startedBuild.StartTime().Unix(),
  1796  			})))
  1797  		})
  1798  	})
  1799  
  1800  	Describe("Resources", func() {
  1801  		var resourceTypes db.ResourceTypes
  1802  		var scenario *dbtest.Scenario
  1803  
  1804  		BeforeEach(func() {
  1805  			scenario = dbtest.Setup(
  1806  				builder.WithPipeline(atc.Config{
  1807  					ResourceTypes: atc.ResourceTypes{
  1808  						{
  1809  							Name:   "some-other-resource-type",
  1810  							Type:   "global-base-type",
  1811  							Source: atc.Source{"some": "other-type-soure"},
  1812  						},
  1813  						{
  1814  							Name:   "some-resource-type",
  1815  							Type:   "global-base-type",
  1816  							Source: atc.Source{"some": "type-soure"},
  1817  						},
  1818  					},
  1819  				}),
  1820  				builder.WithResourceTypeVersions("some-resource-type",
  1821  					atc.Version{"version": "1"},
  1822  					atc.Version{"version": "2"},
  1823  				),
  1824  				builder.WithResourceTypeVersions("some-other-resource-type",
  1825  					atc.Version{"version": "3"},
  1826  				),
  1827  				builder.WithResourceTypeVersions("some-other-resource-type",
  1828  					atc.Version{"version": "3"},
  1829  					atc.Version{"version": "5"},
  1830  				),
  1831  			)
  1832  		})
  1833  
  1834  		JustBeforeEach(func() {
  1835  			var err error
  1836  			resourceTypes, err = scenario.Pipeline.ResourceTypes()
  1837  			Expect(err).ToNot(HaveOccurred())
  1838  		})
  1839  
  1840  		It("returns the version", func() {
  1841  			resourceTypeVersions := []atc.Version{resourceTypes[0].Version()}
  1842  			resourceTypeVersions = append(resourceTypeVersions, resourceTypes[1].Version())
  1843  			Expect(resourceTypeVersions).To(ConsistOf(atc.Version{"version": "2"}, atc.Version{"version": "5"}))
  1844  		})
  1845  	})
  1846  
  1847  	Describe("ResourceVersion", func() {
  1848  		var (
  1849  			rv                    atc.ResourceVersion
  1850  			resourceConfigVersion db.ResourceConfigVersion
  1851  			scenario              *dbtest.Scenario
  1852  		)
  1853  
  1854  		BeforeEach(func() {
  1855  			scenario = dbtest.Setup(
  1856  				builder.WithPipeline(atc.Config{
  1857  					Resources: atc.ResourceConfigs{
  1858  						{
  1859  							Name:   "some-resource",
  1860  							Type:   "some-type",
  1861  							Source: atc.Source{"some": "source"},
  1862  						},
  1863  					},
  1864  				}),
  1865  				builder.WithResourceVersions("some-resource",
  1866  					atc.Version{"version": "1"},
  1867  				),
  1868  			)
  1869  
  1870  			resourceConfigVersion = scenario.ResourceVersion("some-resource", atc.Version{"version": "1"})
  1871  		})
  1872  
  1873  		JustBeforeEach(func() {
  1874  			var found bool
  1875  			var err error
  1876  
  1877  			rv, found, err = pipeline.ResourceVersion(resourceConfigVersion.ID())
  1878  			Expect(err).ToNot(HaveOccurred())
  1879  			Expect(found).To(BeTrue())
  1880  		})
  1881  
  1882  		Context("when a resource is enabled", func() {
  1883  			It("should return the version with enabled set to true", func() {
  1884  				Expect(rv).To(Equal(atc.ResourceVersion{
  1885  					Version: atc.Version{"version": "1"},
  1886  					ID:      resourceConfigVersion.ID(),
  1887  					Enabled: true,
  1888  				}))
  1889  			})
  1890  		})
  1891  
  1892  		Context("when a resource is not enabled", func() {
  1893  			BeforeEach(func() {
  1894  				err := scenario.Resource("some-resource").DisableVersion(resourceConfigVersion.ID())
  1895  				Expect(err).ToNot(HaveOccurred())
  1896  			})
  1897  
  1898  			It("should return the version with enabled set to false", func() {
  1899  				Expect(rv).To(Equal(atc.ResourceVersion{
  1900  					Version: atc.Version{"version": "1"},
  1901  					ID:      resourceConfigVersion.ID(),
  1902  					Enabled: false,
  1903  				}))
  1904  			})
  1905  		})
  1906  	})
  1907  
  1908  	Describe("BuildsWithTime", func() {
  1909  		var (
  1910  			pipeline db.Pipeline
  1911  			builds   = make([]db.Build, 4)
  1912  			job      db.Job
  1913  		)
  1914  
  1915  		BeforeEach(func() {
  1916  			var (
  1917  				err   error
  1918  				found bool
  1919  			)
  1920  
  1921  			config := atc.Config{
  1922  				Jobs: atc.JobConfigs{
  1923  					{
  1924  						Name: "some-job",
  1925  					},
  1926  					{
  1927  						Name: "some-other-job",
  1928  					},
  1929  				},
  1930  			}
  1931  			pipeline, _, err = team.SavePipeline(atc.PipelineRef{Name: "some-pipeline"}, config, db.ConfigVersion(1), false)
  1932  			Expect(err).ToNot(HaveOccurred())
  1933  
  1934  			job, found, err = pipeline.Job("some-job")
  1935  			Expect(err).ToNot(HaveOccurred())
  1936  			Expect(found).To(BeTrue())
  1937  
  1938  			for i := range builds {
  1939  				builds[i], err = job.CreateBuild()
  1940  				Expect(err).ToNot(HaveOccurred())
  1941  
  1942  				buildStart := time.Date(2020, 11, i+1, 0, 0, 0, 0, time.UTC)
  1943  				_, err = dbConn.Exec("UPDATE builds SET start_time = to_timestamp($1) WHERE id = $2", buildStart.Unix(), builds[i].ID())
  1944  				Expect(err).NotTo(HaveOccurred())
  1945  
  1946  				builds[i], found, err = job.Build(builds[i].Name())
  1947  				Expect(err).ToNot(HaveOccurred())
  1948  				Expect(found).To(BeTrue())
  1949  			}
  1950  
  1951  			otherPipeline, _, err := team.SavePipeline(atc.PipelineRef{Name: "another-pipeline"}, config, db.ConfigVersion(1), false)
  1952  			Expect(err).ToNot(HaveOccurred())
  1953  
  1954  			otherJob, found, err := otherPipeline.Job("some-job")
  1955  			Expect(err).ToNot(HaveOccurred())
  1956  			Expect(found).To(BeTrue())
  1957  
  1958  			_, err = otherJob.CreateBuild()
  1959  		})
  1960  
  1961  		Context("when not providing boundaries", func() {
  1962  			Context("without a limit specified", func() {
  1963  				It("returns no builds", func() {
  1964  					returnedBuilds, _, err := pipeline.BuildsWithTime(db.Page{})
  1965  					Expect(err).NotTo(HaveOccurred())
  1966  
  1967  					Expect(returnedBuilds).To(BeEmpty())
  1968  				})
  1969  			})
  1970  
  1971  			Context("with a limit specified", func() {
  1972  				It("returns a subset of the builds", func() {
  1973  					returnedBuilds, _, err := pipeline.BuildsWithTime(db.Page{
  1974  						Limit: 2,
  1975  					})
  1976  					Expect(err).NotTo(HaveOccurred())
  1977  					Expect(returnedBuilds).To(ConsistOf(builds[3], builds[2]))
  1978  				})
  1979  			})
  1980  		})
  1981  
  1982  		Context("when providing boundaries", func() {
  1983  
  1984  			Context("only to", func() {
  1985  				It("returns only those before and including to", func() {
  1986  					returnedBuilds, _, err := pipeline.BuildsWithTime(db.Page{
  1987  						To:    db.NewIntPtr(int(builds[2].StartTime().Unix())),
  1988  						Limit: 50,
  1989  					})
  1990  
  1991  					Expect(err).NotTo(HaveOccurred())
  1992  					Expect(returnedBuilds).To(ConsistOf(builds[0], builds[1], builds[2]))
  1993  				})
  1994  			})
  1995  
  1996  			Context("only from", func() {
  1997  				It("returns only those after from", func() {
  1998  					returnedBuilds, _, err := pipeline.BuildsWithTime(db.Page{
  1999  						From:  db.NewIntPtr(int(builds[1].StartTime().Unix())),
  2000  						Limit: 50,
  2001  					})
  2002  
  2003  					Expect(err).NotTo(HaveOccurred())
  2004  					Expect(returnedBuilds).To(ConsistOf(builds[1], builds[2], builds[3]))
  2005  				})
  2006  			})
  2007  
  2008  			Context("from and to", func() {
  2009  				It("returns only elements in the range", func() {
  2010  					returnedBuilds, _, err := pipeline.BuildsWithTime(db.Page{
  2011  						From:  db.NewIntPtr(int(builds[1].StartTime().Unix())),
  2012  						To:    db.NewIntPtr(int(builds[2].StartTime().Unix())),
  2013  						Limit: 50,
  2014  					})
  2015  					Expect(err).NotTo(HaveOccurred())
  2016  					Expect(returnedBuilds).To(ConsistOf(builds[1], builds[2]))
  2017  				})
  2018  			})
  2019  		})
  2020  	})
  2021  
  2022  	Describe("Variables", func() {
  2023  		var (
  2024  			fakeGlobalSecrets *credsfakes.FakeSecrets
  2025  			pool              creds.VarSourcePool
  2026  
  2027  			pvars vars.Variables
  2028  			err   error
  2029  		)
  2030  
  2031  		BeforeEach(func() {
  2032  			credentialManagement := creds.CredentialManagementConfig{
  2033  				RetryConfig: creds.SecretRetryConfig{
  2034  					Attempts: 5,
  2035  					Interval: time.Second,
  2036  				},
  2037  				CacheConfig: creds.SecretCacheConfig{
  2038  					Enabled:          true,
  2039  					Duration:         time.Minute,
  2040  					DurationNotFound: time.Minute,
  2041  					PurgeInterval:    time.Minute * 10,
  2042  				},
  2043  			}
  2044  			pool = creds.NewVarSourcePool(logger, credentialManagement, 1*time.Minute, 1*time.Second, clock.NewClock())
  2045  		})
  2046  
  2047  		AfterEach(func() {
  2048  			pool.Close()
  2049  		})
  2050  
  2051  		JustBeforeEach(func() {
  2052  			fakeGlobalSecrets = new(credsfakes.FakeSecrets)
  2053  			fakeGlobalSecrets.GetStub = func(key string) (interface{}, *time.Time, bool, error) {
  2054  				if key == "gk" {
  2055  					return "gv", nil, true, nil
  2056  				}
  2057  				return nil, nil, false, nil
  2058  			}
  2059  
  2060  			pvars, err = pipeline.Variables(logger, fakeGlobalSecrets, pool)
  2061  			Expect(err).NotTo(HaveOccurred())
  2062  		})
  2063  
  2064  		It("should get var from pipeline var source", func() {
  2065  			v, found, err := pvars.Get(vars.Reference{Source: "some-var-source", Path: "pk"})
  2066  			Expect(err).NotTo(HaveOccurred())
  2067  			Expect(found).To(BeTrue())
  2068  			Expect(v.(string)).To(Equal("pv"))
  2069  		})
  2070  
  2071  		It("should not get pipeline var 'pk' without specifying var_source name", func() {
  2072  			_, found, err := pvars.Get(vars.Reference{Path: "pk"})
  2073  			Expect(err).NotTo(HaveOccurred())
  2074  			Expect(found).To(BeFalse())
  2075  		})
  2076  
  2077  		It("should not get from global secrets if found in the pipeline var source", func() {
  2078  			pvars.Get(vars.Reference{Source: "some-var-source", Path: "pk"})
  2079  			Expect(fakeGlobalSecrets.GetCallCount()).To(Equal(0))
  2080  		})
  2081  
  2082  		It("should get var from global var source", func() {
  2083  			v, found, err := pvars.Get(vars.Reference{Path: "gk"})
  2084  			Expect(err).NotTo(HaveOccurred())
  2085  			Expect(found).To(BeTrue())
  2086  			Expect(v.(string)).To(Equal("gv"))
  2087  		})
  2088  
  2089  		It("should not get var 'foo'", func() {
  2090  			_, found, err := pvars.Get(vars.Reference{Path: "foo"})
  2091  			Expect(err).NotTo(HaveOccurred())
  2092  			Expect(found).To(BeFalse())
  2093  		})
  2094  
  2095  		Context("with the second var_source", func() {
  2096  			BeforeEach(func() {
  2097  				pipelineConfig.VarSources = append(pipelineConfig.VarSources, atc.VarSourceConfig{
  2098  					Name: "second-var-source",
  2099  					Type: "dummy",
  2100  					Config: map[string]interface{}{
  2101  						"vars": map[string]interface{}{"pk": "((some-var-source:pk))"},
  2102  					},
  2103  				})
  2104  
  2105  				var created bool
  2106  				pipeline, created, err = team.SavePipeline(atc.PipelineRef{Name: "fake-pipeline"}, pipelineConfig, pipeline.ConfigVersion(), false)
  2107  				Expect(err).ToNot(HaveOccurred())
  2108  				Expect(created).To(BeFalse())
  2109  			})
  2110  
  2111  			// The second var source is configured with vars that needs to be interpolated
  2112  			// from "some-var-source".
  2113  			It("should get pipeline var 'pk' from the second var_source", func() {
  2114  				v, found, err := pvars.Get(vars.Reference{Source: "second-var-source", Path: "pk"})
  2115  				Expect(err).NotTo(HaveOccurred())
  2116  				Expect(found).To(BeTrue())
  2117  				Expect(v.(string)).To(Equal("pv"))
  2118  			})
  2119  		})
  2120  	})
  2121  
  2122  	Describe("SetParentIDs", func() {
  2123  		It("sets the parent_job_id and parent_build_id fields", func() {
  2124  			jobID := 123
  2125  			buildID := 456
  2126  			Expect(pipeline.SetParentIDs(jobID, buildID)).To(Succeed())
  2127  
  2128  			found, err := pipeline.Reload()
  2129  			Expect(err).ToNot(HaveOccurred())
  2130  			Expect(found).To(BeTrue())
  2131  			Expect(pipeline.ParentJobID()).To(Equal(jobID))
  2132  			Expect(pipeline.ParentBuildID()).To(Equal(buildID))
  2133  		})
  2134  
  2135  		It("returns an error if job or build ID are less than or equal to zero", func() {
  2136  			err := pipeline.SetParentIDs(0, 0)
  2137  			Expect(err).To(MatchError("job and build id cannot be negative or zero-value"))
  2138  			err = pipeline.SetParentIDs(-1, -6)
  2139  			Expect(err).To(MatchError("job and build id cannot be negative or zero-value"))
  2140  		})
  2141  
  2142  		Context("pipeline was saved by a newer build", func() {
  2143  			It("returns ErrSetByNewerBuild", func() {
  2144  				By("setting the build ID to a high number")
  2145  				pipeline.SetParentIDs(1, 60)
  2146  
  2147  				By("trying to set the build ID to a lower number")
  2148  				err := pipeline.SetParentIDs(1, 2)
  2149  				Expect(err).To(MatchError(db.ErrSetByNewerBuild))
  2150  			})
  2151  		})
  2152  
  2153  		Context("pipeline was previously saved by team.SavePipeline", func() {
  2154  			It("successfully updates the parent build and job IDs", func() {
  2155  				By("using the defaultPipeline saved by defaultTeam at the suite level")
  2156  				Expect(defaultPipeline.ParentJobID()).To(Equal(0), "should be zero if sql value is null")
  2157  				Expect(defaultPipeline.ParentBuildID()).To(Equal(0), "should be zero if sql value is null")
  2158  
  2159  				err := defaultPipeline.SetParentIDs(1, 6)
  2160  				Expect(err).ToNot(HaveOccurred())
  2161  				defaultPipeline.Reload()
  2162  				Expect(defaultPipeline.ParentJobID()).To(Equal(1), "should be zero if sql value is null")
  2163  				Expect(defaultPipeline.ParentBuildID()).To(Equal(6), "should be zero if sql value is null")
  2164  			})
  2165  		})
  2166  	})
  2167  
  2168  	Context("Config", func() {
  2169  		It("should return config correctly", func() {
  2170  			Expect(pipeline.Config()).To(Equal(pipelineConfig))
  2171  		})
  2172  	})
  2173  })
  2174  
  2175  func intptr(i int) *int {
  2176  	return &i
  2177  }