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

     1  package db_test
     2  
     3  import (
     4  	"context"
     5  	"strconv"
     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"
    15  	"go.opentelemetry.io/otel/api/trace/tracetest"
    16  )
    17  
    18  var _ = Describe("Resource", func() {
    19  	var pipeline db.Pipeline
    20  
    21  	BeforeEach(func() {
    22  		var (
    23  			created bool
    24  			err     error
    25  		)
    26  
    27  		pipeline, created, err = defaultTeam.SavePipeline(
    28  			atc.PipelineRef{Name: "pipeline-with-resources"},
    29  			atc.Config{
    30  				Resources: atc.ResourceConfigs{
    31  					{
    32  						Name:         "some-resource",
    33  						Type:         "registry-image",
    34  						WebhookToken: "some-token",
    35  						Source:       atc.Source{"some": "repository"},
    36  						Version:      atc.Version{"ref": "abcdef"},
    37  						CheckTimeout: "999m",
    38  					},
    39  					{
    40  						Name:   "some-other-resource",
    41  						Public: true,
    42  						Type:   "git",
    43  						Source: atc.Source{"some": "other-repository"},
    44  					},
    45  					{
    46  						Name:   "some-secret-resource",
    47  						Public: false,
    48  						Type:   "git",
    49  						Source: atc.Source{"some": "((secret-repository))"},
    50  					},
    51  					{
    52  						Name:         "some-resource-custom-check",
    53  						Type:         "git",
    54  						Source:       atc.Source{"some": "some-repository"},
    55  						CheckEvery:   "10ms",
    56  						CheckTimeout: "1m",
    57  					},
    58  				},
    59  				Jobs: atc.JobConfigs{
    60  					{
    61  						Name: "job-using-resource",
    62  						PlanSequence: []atc.Step{
    63  							{
    64  								Config: &atc.GetStep{
    65  									Name: "some-other-resource",
    66  								},
    67  							},
    68  						},
    69  					},
    70  					{
    71  						Name: "not-using-resource",
    72  					},
    73  				},
    74  			},
    75  			0,
    76  			false,
    77  		)
    78  		Expect(err).ToNot(HaveOccurred())
    79  		Expect(created).To(BeTrue())
    80  	})
    81  
    82  	Describe("(Pipeline).Resources", func() {
    83  		var resources []db.Resource
    84  
    85  		JustBeforeEach(func() {
    86  			var err error
    87  			resources, err = pipeline.Resources()
    88  			Expect(err).ToNot(HaveOccurred())
    89  		})
    90  
    91  		It("returns the resources", func() {
    92  			Expect(resources).To(HaveLen(4))
    93  
    94  			ids := map[int]struct{}{}
    95  
    96  			for _, r := range resources {
    97  				ids[r.ID()] = struct{}{}
    98  
    99  				switch r.Name() {
   100  				case "some-resource":
   101  					Expect(r.Type()).To(Equal("registry-image"))
   102  					Expect(r.Source()).To(Equal(atc.Source{"some": "repository"}))
   103  					Expect(r.ConfigPinnedVersion()).To(Equal(atc.Version{"ref": "abcdef"}))
   104  					Expect(r.CurrentPinnedVersion()).To(Equal(r.ConfigPinnedVersion()))
   105  					Expect(r.HasWebhook()).To(BeTrue())
   106  				case "some-other-resource":
   107  					Expect(r.Type()).To(Equal("git"))
   108  					Expect(r.Source()).To(Equal(atc.Source{"some": "other-repository"}))
   109  					Expect(r.HasWebhook()).To(BeFalse())
   110  				case "some-secret-resource":
   111  					Expect(r.Type()).To(Equal("git"))
   112  					Expect(r.Source()).To(Equal(atc.Source{"some": "((secret-repository))"}))
   113  					Expect(r.HasWebhook()).To(BeFalse())
   114  				case "some-resource-custom-check":
   115  					Expect(r.Type()).To(Equal("git"))
   116  					Expect(r.Source()).To(Equal(atc.Source{"some": "some-repository"}))
   117  					Expect(r.CheckEvery()).To(Equal("10ms"))
   118  					Expect(r.CheckTimeout()).To(Equal("1m"))
   119  					Expect(r.HasWebhook()).To(BeFalse())
   120  				}
   121  			}
   122  		})
   123  	})
   124  
   125  	Describe("(Pipeline).Resource", func() {
   126  		var (
   127  			scenario *dbtest.Scenario
   128  		)
   129  
   130  		Context("when the resource exists", func() {
   131  			BeforeEach(func() {
   132  				scenario = dbtest.Setup(
   133  					builder.WithPipeline(atc.Config{
   134  						Resources: atc.ResourceConfigs{
   135  							{
   136  								Name:   "some-resource",
   137  								Type:   "some-base-resource-type",
   138  								Source: atc.Source{"some": "repository"},
   139  							},
   140  						},
   141  					}),
   142  				)
   143  			})
   144  
   145  			It("returns the resource", func() {
   146  				Expect(scenario.Resource("some-resource").Name()).To(Equal("some-resource"))
   147  				Expect(scenario.Resource("some-resource").Type()).To(Equal("some-base-resource-type"))
   148  				Expect(scenario.Resource("some-resource").Source()).To(Equal(atc.Source{"some": "repository"}))
   149  			})
   150  		})
   151  
   152  		Context("when the resource does not exist", func() {
   153  			var resource db.Resource
   154  			var found bool
   155  			var err error
   156  
   157  			BeforeEach(func() {
   158  				scenario = dbtest.Setup(
   159  					builder.WithPipeline(atc.Config{
   160  						Resources: atc.ResourceConfigs{
   161  							{
   162  								Name:   "some-resource",
   163  								Type:   "some-base-resource-type",
   164  								Source: atc.Source{"some": "repository"},
   165  							},
   166  						},
   167  					}),
   168  				)
   169  
   170  				resource, found, err = scenario.Pipeline.Resource("bonkers")
   171  				Expect(err).ToNot(HaveOccurred())
   172  			})
   173  
   174  			It("returns nil", func() {
   175  				Expect(found).To(BeFalse())
   176  				Expect(resource).To(BeNil())
   177  			})
   178  		})
   179  	})
   180  
   181  	Describe("Enable/Disable Version", func() {
   182  		var scenario *dbtest.Scenario
   183  
   184  		BeforeEach(func() {
   185  			scenario = dbtest.Setup(
   186  				builder.WithPipeline(atc.Config{
   187  					Jobs: atc.JobConfigs{
   188  						{
   189  							Name: "job-using-resource",
   190  							PlanSequence: []atc.Step{
   191  								{
   192  									Config: &atc.GetStep{
   193  										Name: "some-other-resource",
   194  									},
   195  								},
   196  							},
   197  						},
   198  						{
   199  							Name: "not-using-resource",
   200  						},
   201  					},
   202  					Resources: atc.ResourceConfigs{
   203  						{
   204  							Name:   "some-other-resource",
   205  							Type:   "some-base-resource-type",
   206  							Source: atc.Source{"some": "other-repository"},
   207  						},
   208  					},
   209  				}),
   210  				builder.WithResourceVersions("some-other-resource", atc.Version{"disabled": "version"}),
   211  			)
   212  		})
   213  
   214  		Context("when disabling a version that exists", func() {
   215  			var disableErr error
   216  			var requestedSchedule1 time.Time
   217  			var requestedSchedule2 time.Time
   218  
   219  			BeforeEach(func() {
   220  				requestedSchedule1 = scenario.Job("job-using-resource").ScheduleRequestedTime()
   221  				requestedSchedule2 = scenario.Job("not-using-resource").ScheduleRequestedTime()
   222  
   223  				scenario.Run(builder.WithDisabledVersion("some-other-resource", atc.Version{"disabled": "version"}))
   224  			})
   225  
   226  			It("successfully disables the version", func() {
   227  				Expect(disableErr).ToNot(HaveOccurred())
   228  
   229  				versions, _, found, err := scenario.Resource("some-other-resource").Versions(db.Page{Limit: 3}, nil)
   230  				Expect(err).ToNot(HaveOccurred())
   231  				Expect(found).To(BeTrue())
   232  				Expect(versions).To(HaveLen(1))
   233  				Expect(versions[0].Version).To(Equal(atc.Version{"disabled": "version"}))
   234  				Expect(versions[0].Enabled).To(BeFalse())
   235  			})
   236  
   237  			It("requests schedule on the jobs using that resource", func() {
   238  				found, err := scenario.Job("job-using-resource").Reload()
   239  				Expect(err).NotTo(HaveOccurred())
   240  				Expect(found).To(BeTrue())
   241  
   242  				Expect(scenario.Job("job-using-resource").ScheduleRequestedTime()).Should(BeTemporally(">", requestedSchedule1))
   243  			})
   244  
   245  			It("does not request schedule on jobs that do not use the resource", func() {
   246  				found, err := scenario.Job("not-using-resource").Reload()
   247  				Expect(err).NotTo(HaveOccurred())
   248  				Expect(found).To(BeTrue())
   249  
   250  				Expect(scenario.Job("not-using-resource").ScheduleRequestedTime()).Should(BeTemporally("==", requestedSchedule2))
   251  			})
   252  
   253  			Context("when enabling that version", func() {
   254  				BeforeEach(func() {
   255  					scenario.Run(builder.WithEnabledVersion("some-other-resource", atc.Version{"disabled": "version"}))
   256  				})
   257  
   258  				It("successfully enables the version", func() {
   259  					versions, _, found, err := scenario.Resource("some-other-resource").Versions(db.Page{Limit: 3}, nil)
   260  					Expect(err).ToNot(HaveOccurred())
   261  					Expect(found).To(BeTrue())
   262  					Expect(versions).To(HaveLen(1))
   263  					Expect(versions[0].Version).To(Equal(atc.Version{"disabled": "version"}))
   264  					Expect(versions[0].Enabled).To(BeTrue())
   265  				})
   266  
   267  				It("request schedule on the jobs using that resource", func() {
   268  					found, err := scenario.Job("job-using-resource").Reload()
   269  					Expect(err).NotTo(HaveOccurred())
   270  					Expect(found).To(BeTrue())
   271  
   272  					Expect(scenario.Job("job-using-resource").ScheduleRequestedTime()).Should(BeTemporally(">", requestedSchedule1))
   273  				})
   274  
   275  				It("does not request schedule on jobs that do not use the resource", func() {
   276  					found, err := scenario.Job("not-using-resource").Reload()
   277  					Expect(err).NotTo(HaveOccurred())
   278  					Expect(found).To(BeTrue())
   279  
   280  					Expect(scenario.Job("not-using-resource").ScheduleRequestedTime()).Should(BeTemporally("==", requestedSchedule2))
   281  				})
   282  			})
   283  		})
   284  
   285  		Context("when disabling version that does not exist", func() {
   286  			var disableErr error
   287  			BeforeEach(func() {
   288  				resource, found, err := scenario.Pipeline.Resource("some-other-resource")
   289  				Expect(err).ToNot(HaveOccurred())
   290  				Expect(found).To(BeTrue())
   291  
   292  				disableErr = resource.DisableVersion(123456)
   293  			})
   294  
   295  			It("returns an error", func() {
   296  				Expect(disableErr).To(HaveOccurred())
   297  			})
   298  		})
   299  
   300  		Context("when enabling a version that is already enabled", func() {
   301  			var enableError error
   302  			BeforeEach(func() {
   303  				resource, found, err := scenario.Pipeline.Resource("some-other-resource")
   304  				Expect(err).ToNot(HaveOccurred())
   305  				Expect(found).To(BeTrue())
   306  
   307  				enableError = resource.EnableVersion(scenario.ResourceVersion("some-other-resource", atc.Version{"disabled": "version"}).ID())
   308  			})
   309  
   310  			It("returns a non one row affected error", func() {
   311  				Expect(enableError).To(Equal(db.NonOneRowAffectedError{0}))
   312  			})
   313  		})
   314  	})
   315  
   316  	Describe("SetResourceConfigScope", func() {
   317  		var pipeline db.Pipeline
   318  		var resource db.Resource
   319  		var scope db.ResourceConfigScope
   320  
   321  		BeforeEach(func() {
   322  			config := atc.Config{
   323  				Resources: atc.ResourceConfigs{
   324  					{
   325  						Name:   "some-resource",
   326  						Type:   defaultWorkerResourceType.Type,
   327  						Source: atc.Source{"some": "repository"},
   328  					},
   329  					{
   330  						Name:   "some-other-resource",
   331  						Type:   defaultWorkerResourceType.Type,
   332  						Source: atc.Source{"some": "other-repository"},
   333  					},
   334  				},
   335  				Jobs: atc.JobConfigs{
   336  					{
   337  						Name: "using-resource",
   338  						PlanSequence: []atc.Step{
   339  							{
   340  								Config: &atc.GetStep{
   341  									Name: "some-resource",
   342  								},
   343  							},
   344  						},
   345  					},
   346  					{
   347  						Name: "not-using-resource",
   348  						PlanSequence: []atc.Step{
   349  							{
   350  								Config: &atc.GetStep{
   351  									Name: "some-other-resource",
   352  								},
   353  							},
   354  						},
   355  					},
   356  				},
   357  			}
   358  
   359  			var err error
   360  
   361  			var created bool
   362  			pipeline, created, err = defaultTeam.SavePipeline(
   363  				atc.PipelineRef{Name: "some-pipeline-with-two-jobs"},
   364  				config,
   365  				0,
   366  				false,
   367  			)
   368  			Expect(err).ToNot(HaveOccurred())
   369  			Expect(created).To(BeTrue())
   370  
   371  			var found bool
   372  			resource, found, err = pipeline.Resource("some-resource")
   373  			Expect(err).ToNot(HaveOccurred())
   374  			Expect(found).To(BeTrue())
   375  
   376  			resourceConfig, err := resourceConfigFactory.FindOrCreateResourceConfig(resource.Type(), resource.Source(), atc.VersionedResourceTypes{})
   377  			Expect(err).ToNot(HaveOccurred())
   378  
   379  			scope, err = resourceConfig.FindOrCreateScope(resource)
   380  			Expect(err).ToNot(HaveOccurred())
   381  		})
   382  
   383  		It("associates the resource to the config and scope", func() {
   384  			Expect(resource.ResourceConfigID()).To(BeZero())
   385  			Expect(resource.ResourceConfigScopeID()).To(BeZero())
   386  
   387  			Expect(resource.SetResourceConfigScope(scope)).To(Succeed())
   388  
   389  			_, err := resource.Reload()
   390  			Expect(err).ToNot(HaveOccurred())
   391  
   392  			Expect(resource.ResourceConfigID()).To(Equal(scope.ResourceConfig().ID()))
   393  			Expect(resource.ResourceConfigScopeID()).To(Equal(scope.ID()))
   394  		})
   395  
   396  		It("requests scheduling for downstream jobs", func() {
   397  			job, found, err := pipeline.Job("using-resource")
   398  			Expect(err).NotTo(HaveOccurred())
   399  			Expect(found).To(BeTrue())
   400  
   401  			otherJob, found, err := pipeline.Job("not-using-resource")
   402  			Expect(err).NotTo(HaveOccurred())
   403  			Expect(found).To(BeTrue())
   404  
   405  			requestedSchedule := job.ScheduleRequestedTime()
   406  			otherRequestedSchedule := otherJob.ScheduleRequestedTime()
   407  
   408  			Expect(resource.SetResourceConfigScope(scope)).To(Succeed())
   409  
   410  			found, err = job.Reload()
   411  			Expect(err).NotTo(HaveOccurred())
   412  			Expect(found).To(BeTrue())
   413  
   414  			found, err = otherJob.Reload()
   415  			Expect(err).NotTo(HaveOccurred())
   416  			Expect(found).To(BeTrue())
   417  
   418  			Expect(job.ScheduleRequestedTime()).Should(BeTemporally(">", requestedSchedule))
   419  			Expect(otherJob.ScheduleRequestedTime()).Should(Equal(otherRequestedSchedule))
   420  		})
   421  	})
   422  
   423  	Describe("CheckPlan", func() {
   424  		var resource db.Resource
   425  		var resourceTypes db.ResourceTypes
   426  
   427  		BeforeEach(func() {
   428  			var err error
   429  			var found bool
   430  			resource, found, err = pipeline.Resource("some-resource")
   431  			Expect(err).ToNot(HaveOccurred())
   432  			Expect(found).To(BeTrue())
   433  
   434  			resourceTypes, err = pipeline.ResourceTypes()
   435  			Expect(err).ToNot(HaveOccurred())
   436  		})
   437  
   438  		It("returns a plan which will update the resource", func() {
   439  			defaults := atc.Source{"sdk": "sdv"}
   440  			Expect(resource.CheckPlan(atc.Version{"some": "version"}, time.Minute, resourceTypes, defaults)).To(Equal(atc.CheckPlan{
   441  				Name:    resource.Name(),
   442  				Type:    resource.Type(),
   443  				Source:  defaults.Merge(resource.Source()),
   444  				Tags:    resource.Tags(),
   445  				Timeout: "999m",
   446  
   447  				FromVersion: atc.Version{"some": "version"},
   448  
   449  				Interval: "1m0s",
   450  
   451  				VersionedResourceTypes: resourceTypes.Deserialize(),
   452  
   453  				Resource: resource.Name(),
   454  			}))
   455  		})
   456  	})
   457  
   458  	Describe("CreateBuild", func() {
   459  		var ctx context.Context
   460  		var manuallyTriggered bool
   461  		var plan atc.Plan
   462  		var build db.Build
   463  		var created bool
   464  
   465  		BeforeEach(func() {
   466  			ctx = context.TODO()
   467  			manuallyTriggered = false
   468  			plan = atc.Plan{
   469  				ID: "some-plan",
   470  				Check: &atc.CheckPlan{
   471  					Name: "wreck",
   472  				},
   473  			}
   474  		})
   475  
   476  		JustBeforeEach(func() {
   477  			var err error
   478  			build, created, err = defaultResource.CreateBuild(ctx, manuallyTriggered, plan)
   479  			Expect(err).ToNot(HaveOccurred())
   480  		})
   481  
   482  		It("creates a started build for a resource", func() {
   483  			Expect(created).To(BeTrue())
   484  			Expect(build).ToNot(BeNil())
   485  			Expect(build.Name()).To(Equal(db.CheckBuildName))
   486  			Expect(build.ResourceID()).To(Equal(defaultResource.ID()))
   487  			Expect(build.PipelineID()).To(Equal(defaultResource.PipelineID()))
   488  			Expect(build.TeamID()).To(Equal(defaultResource.TeamID()))
   489  			Expect(build.IsManuallyTriggered()).To(BeFalse())
   490  			Expect(build.Status()).To(Equal(db.BuildStatusStarted))
   491  			Expect(build.PrivatePlan()).To(Equal(plan))
   492  		})
   493  
   494  		It("associates the resource to the build", func() {
   495  			exists, err := defaultResource.Reload()
   496  			Expect(err).ToNot(HaveOccurred())
   497  			Expect(exists).To(BeTrue())
   498  
   499  			Expect(defaultResource.BuildSummary()).To(Equal(&atc.BuildSummary{
   500  				ID:                   build.ID(),
   501  				Name:                 db.CheckBuildName,
   502  				Status:               atc.StatusStarted,
   503  				StartTime:            build.StartTime().Unix(),
   504  				TeamName:             defaultTeam.Name(),
   505  				PipelineID:           defaultPipeline.ID(),
   506  				PipelineName:         defaultPipeline.Name(),
   507  				PipelineInstanceVars: defaultPipeline.InstanceVars(),
   508  			}))
   509  		})
   510  
   511  		Context("when tracing is configured", func() {
   512  			var span trace.Span
   513  
   514  			BeforeEach(func() {
   515  				tracing.ConfigureTraceProvider(tracetest.NewProvider())
   516  
   517  				ctx, span = tracing.StartSpan(context.Background(), "fake-operation", nil)
   518  			})
   519  
   520  			AfterEach(func() {
   521  				tracing.Configured = false
   522  			})
   523  
   524  			It("propagates span context", func() {
   525  				traceID := span.SpanContext().TraceID.String()
   526  				buildContext := build.SpanContext()
   527  				traceParent := buildContext.Get("traceparent")
   528  				Expect(traceParent).To(ContainSubstring(traceID))
   529  			})
   530  		})
   531  
   532  		Context("when another build already exists", func() {
   533  			var prevBuild db.Build
   534  
   535  			BeforeEach(func() {
   536  				var err error
   537  				var prevCreated bool
   538  				prevBuild, prevCreated, err = defaultResource.CreateBuild(ctx, false, plan)
   539  				Expect(err).ToNot(HaveOccurred())
   540  				Expect(prevCreated).To(BeTrue())
   541  			})
   542  
   543  			It("does not create the second build", func() {
   544  				Expect(created).To(BeFalse())
   545  			})
   546  
   547  			Context("when manually triggered", func() {
   548  				BeforeEach(func() {
   549  					manuallyTriggered = true
   550  				})
   551  
   552  				It("creates a manually triggered resource build", func() {
   553  					Expect(created).To(BeTrue())
   554  					Expect(build.IsManuallyTriggered()).To(BeTrue())
   555  					Expect(build.ResourceID()).To(Equal(defaultResource.ID()))
   556  				})
   557  
   558  				It("associates the resource to the build", func() {
   559  					exists, err := defaultResource.Reload()
   560  					Expect(err).ToNot(HaveOccurred())
   561  					Expect(exists).To(BeTrue())
   562  
   563  					Expect(defaultResource.BuildSummary()).To(Equal(&atc.BuildSummary{
   564  						ID:                   build.ID(),
   565  						Name:                 db.CheckBuildName,
   566  						Status:               atc.StatusStarted,
   567  						StartTime:            build.StartTime().Unix(),
   568  						TeamName:             defaultTeam.Name(),
   569  						PipelineID:           defaultPipeline.ID(),
   570  						PipelineName:         defaultPipeline.Name(),
   571  						PipelineInstanceVars: defaultPipeline.InstanceVars(),
   572  					}))
   573  				})
   574  			})
   575  
   576  			Context("when the previous build is finished", func() {
   577  				BeforeEach(func() {
   578  					Expect(prevBuild.Finish(db.BuildStatusSucceeded)).To(Succeed())
   579  				})
   580  
   581  				It("creates the build", func() {
   582  					Expect(created).To(BeTrue())
   583  					Expect(build.ResourceID()).To(Equal(defaultResource.ID()))
   584  				})
   585  
   586  				Context("when there are multiple completed previous builds", func() {
   587  					var prevBuilds []db.Build
   588  
   589  					BeforeEach(func() {
   590  						prevBuilds = []db.Build{prevBuild}
   591  
   592  						for i := 0; i < 10; i++ {
   593  							prev, created, err := defaultResource.CreateBuild(ctx, true, plan)
   594  							Expect(err).ToNot(HaveOccurred())
   595  							Expect(created).To(BeTrue())
   596  
   597  							Expect(prev.Finish(db.BuildStatusSucceeded)).To(Succeed())
   598  
   599  							prevBuilds = append(prevBuilds, prev)
   600  						}
   601  					})
   602  
   603  					It("deletes all previous builds", func() {
   604  						for _, prev := range prevBuilds {
   605  							found, err := prev.Reload()
   606  							Expect(err).ToNot(HaveOccurred())
   607  							Expect(found).To(BeFalse())
   608  						}
   609  					})
   610  
   611  					It("deletes the previous builds' events", func() {
   612  						for _, prev := range prevBuilds {
   613  							var exists bool
   614  							err := dbConn.QueryRow(`SELECT EXISTS (
   615  								SELECT 1
   616  								FROM build_events
   617  								WHERE build_id = $1
   618  							)`, prev.ID()).Scan(&exists)
   619  							Expect(err).ToNot(HaveOccurred())
   620  							Expect(exists).To(BeFalse())
   621  						}
   622  					})
   623  				})
   624  			})
   625  		})
   626  	})
   627  
   628  	Context("Versions", func() {
   629  		var (
   630  			scenario *dbtest.Scenario
   631  		)
   632  
   633  		Context("with version filters", func() {
   634  			var filter atc.Version
   635  			var resourceVersions []atc.ResourceVersion
   636  
   637  			BeforeEach(func() {
   638  				scenario = dbtest.Setup(
   639  					builder.WithPipeline(atc.Config{
   640  						Resources: atc.ResourceConfigs{
   641  							{
   642  								Name:   "some-resource",
   643  								Type:   "some-base-resource-type",
   644  								Source: atc.Source{"some": "repository"},
   645  							},
   646  						},
   647  					}),
   648  					builder.WithResourceVersions(
   649  						"some-resource",
   650  						atc.Version{"ref": "v0", "commit": "v0"},
   651  						atc.Version{"ref": "v1", "commit": "v1"},
   652  						atc.Version{"ref": "v2", "commit": "v2"},
   653  					),
   654  				)
   655  
   656  				resourceVersions = make([]atc.ResourceVersion, 0)
   657  
   658  				for i := 0; i < 3; i++ {
   659  					rcv := scenario.ResourceVersion("some-resource", atc.Version{
   660  						"ref":    "v" + strconv.Itoa(i),
   661  						"commit": "v" + strconv.Itoa(i),
   662  					})
   663  
   664  					var metadata []atc.MetadataField
   665  
   666  					for _, v := range rcv.Metadata() {
   667  						metadata = append(metadata, atc.MetadataField(v))
   668  					}
   669  
   670  					resourceVersion := atc.ResourceVersion{
   671  						ID:       rcv.ID(),
   672  						Version:  atc.Version(rcv.Version()),
   673  						Metadata: metadata,
   674  						Enabled:  true,
   675  					}
   676  
   677  					resourceVersions = append(resourceVersions, resourceVersion)
   678  				}
   679  			})
   680  
   681  			Context("when filter include one field that matches", func() {
   682  				BeforeEach(func() {
   683  					filter = atc.Version{"ref": "v2"}
   684  				})
   685  
   686  				It("return version that matches field filter", func() {
   687  					result, _, found, err := scenario.Resource("some-resource").Versions(db.Page{Limit: 10}, filter)
   688  					Expect(err).ToNot(HaveOccurred())
   689  					Expect(found).To(BeTrue())
   690  					Expect(len(result)).To(Equal(1))
   691  					Expect(result[0].Version).To(Equal(resourceVersions[2].Version))
   692  				})
   693  			})
   694  
   695  			Context("when filter include one field that doesn't match", func() {
   696  				BeforeEach(func() {
   697  					filter = atc.Version{"ref": "v20"}
   698  				})
   699  
   700  				It("return no version", func() {
   701  					result, _, found, err := scenario.Resource("some-resource").Versions(db.Page{Limit: 10}, filter)
   702  					Expect(err).ToNot(HaveOccurred())
   703  					Expect(found).To(BeTrue())
   704  					Expect(len(result)).To(Equal(0))
   705  				})
   706  			})
   707  
   708  			Context("when filter include two fields that match", func() {
   709  				BeforeEach(func() {
   710  					filter = atc.Version{"ref": "v1", "commit": "v1"}
   711  				})
   712  
   713  				It("return version", func() {
   714  					result, _, found, err := scenario.Resource("some-resource").Versions(db.Page{Limit: 10}, filter)
   715  					Expect(err).ToNot(HaveOccurred())
   716  					Expect(found).To(BeTrue())
   717  					Expect(len(result)).To(Equal(1))
   718  					Expect(result[0].Version).To(Equal(resourceVersions[1].Version))
   719  				})
   720  			})
   721  
   722  			Context("when filter include two fields and one of them does not match", func() {
   723  				BeforeEach(func() {
   724  					filter = atc.Version{"ref": "v1", "commit": "v2"}
   725  				})
   726  
   727  				It("return no version", func() {
   728  					result, _, found, err := scenario.Resource("some-resource").Versions(db.Page{Limit: 10}, filter)
   729  					Expect(err).ToNot(HaveOccurred())
   730  					Expect(found).To(BeTrue())
   731  					Expect(len(result)).To(Equal(0))
   732  				})
   733  			})
   734  		})
   735  
   736  		Context("when resource has versions created in order of check order", func() {
   737  			var resourceVersions []atc.ResourceVersion
   738  
   739  			BeforeEach(func() {
   740  				scenario = dbtest.Setup(
   741  					builder.WithPipeline(atc.Config{
   742  						Resources: atc.ResourceConfigs{
   743  							{
   744  								Name:   "some-resource",
   745  								Type:   "some-base-resource-type",
   746  								Source: atc.Source{"some": "repository"},
   747  							},
   748  						},
   749  					}),
   750  					builder.WithResourceVersions(
   751  						"some-resource",
   752  						atc.Version{"ref": "v0"},
   753  						atc.Version{"ref": "v1"},
   754  						atc.Version{"ref": "v2"},
   755  						atc.Version{"ref": "v3"},
   756  						atc.Version{"ref": "v4"},
   757  						atc.Version{"ref": "v5"},
   758  						atc.Version{"ref": "v6"},
   759  						atc.Version{"ref": "v7"},
   760  						atc.Version{"ref": "v8"},
   761  						atc.Version{"ref": "v9"},
   762  					),
   763  				)
   764  
   765  				resourceVersions = make([]atc.ResourceVersion, 0)
   766  
   767  				for i := 0; i < 10; i++ {
   768  					rcv := scenario.ResourceVersion("some-resource", atc.Version{"ref": "v" + strconv.Itoa(i)})
   769  
   770  					var metadata []atc.MetadataField
   771  
   772  					for _, v := range rcv.Metadata() {
   773  						metadata = append(metadata, atc.MetadataField(v))
   774  					}
   775  
   776  					resourceVersion := atc.ResourceVersion{
   777  						ID:       rcv.ID(),
   778  						Version:  atc.Version(rcv.Version()),
   779  						Metadata: metadata,
   780  						Enabled:  true,
   781  					}
   782  
   783  					resourceVersions = append(resourceVersions, resourceVersion)
   784  				}
   785  			})
   786  
   787  			Context("with no from/to", func() {
   788  				It("returns the first page, with the given limit, and a next page", func() {
   789  					historyPage, pagination, found, err := scenario.Resource("some-resource").Versions(db.Page{Limit: 2}, nil)
   790  					Expect(err).ToNot(HaveOccurred())
   791  					Expect(found).To(BeTrue())
   792  					Expect(len(historyPage)).To(Equal(2))
   793  					Expect(historyPage[0].Version).To(Equal(resourceVersions[9].Version))
   794  					Expect(historyPage[1].Version).To(Equal(resourceVersions[8].Version))
   795  					Expect(pagination.Newer).To(BeNil())
   796  					Expect(pagination.Older).To(Equal(&db.Page{To: db.NewIntPtr(resourceVersions[7].ID), Limit: 2}))
   797  				})
   798  			})
   799  
   800  			Context("with a to that places it in the middle of the versions", func() {
   801  				It("returns the versions, with previous/next pages", func() {
   802  					historyPage, pagination, found, err := scenario.Resource("some-resource").Versions(db.Page{To: db.NewIntPtr(resourceVersions[6].ID), Limit: 2}, nil)
   803  					Expect(err).ToNot(HaveOccurred())
   804  					Expect(found).To(BeTrue())
   805  					Expect(len(historyPage)).To(Equal(2))
   806  					Expect(historyPage[0].Version).To(Equal(resourceVersions[6].Version))
   807  					Expect(historyPage[1].Version).To(Equal(resourceVersions[5].Version))
   808  					Expect(pagination.Newer).To(Equal(&db.Page{From: db.NewIntPtr(resourceVersions[7].ID), Limit: 2}))
   809  					Expect(pagination.Older).To(Equal(&db.Page{To: db.NewIntPtr(resourceVersions[4].ID), Limit: 2}))
   810  				})
   811  			})
   812  
   813  			Context("with a to that places it to the oldest version", func() {
   814  				It("returns the versions, with no next page", func() {
   815  					historyPage, pagination, found, err := scenario.Resource("some-resource").Versions(db.Page{To: db.NewIntPtr(resourceVersions[1].ID), Limit: 2}, nil)
   816  					Expect(err).ToNot(HaveOccurred())
   817  					Expect(found).To(BeTrue())
   818  					Expect(len(historyPage)).To(Equal(2))
   819  					Expect(historyPage[0].Version).To(Equal(resourceVersions[1].Version))
   820  					Expect(historyPage[1].Version).To(Equal(resourceVersions[0].Version))
   821  					Expect(pagination.Newer).To(Equal(&db.Page{From: db.NewIntPtr(resourceVersions[2].ID), Limit: 2}))
   822  					Expect(pagination.Older).To(BeNil())
   823  				})
   824  			})
   825  
   826  			Context("with a from that places it in the middle of the versions", func() {
   827  				It("returns the versions, with previous/next pages", func() {
   828  					historyPage, pagination, found, err := scenario.Resource("some-resource").Versions(db.Page{From: db.NewIntPtr(resourceVersions[6].ID), Limit: 2}, nil)
   829  					Expect(err).ToNot(HaveOccurred())
   830  					Expect(found).To(BeTrue())
   831  					Expect(len(historyPage)).To(Equal(2))
   832  					Expect(historyPage[0].Version).To(Equal(resourceVersions[7].Version))
   833  					Expect(historyPage[1].Version).To(Equal(resourceVersions[6].Version))
   834  					Expect(pagination.Newer).To(Equal(&db.Page{From: db.NewIntPtr(resourceVersions[8].ID), Limit: 2}))
   835  					Expect(pagination.Older).To(Equal(&db.Page{To: db.NewIntPtr(resourceVersions[5].ID), Limit: 2}))
   836  				})
   837  			})
   838  
   839  			Context("with a from that places it at the beginning of the most recent versions", func() {
   840  				It("returns the versions, with no previous page", func() {
   841  					historyPage, pagination, found, err := scenario.Resource("some-resource").Versions(db.Page{From: db.NewIntPtr(resourceVersions[8].ID), Limit: 2}, nil)
   842  					Expect(err).ToNot(HaveOccurred())
   843  					Expect(found).To(BeTrue())
   844  					Expect(len(historyPage)).To(Equal(2))
   845  					Expect(historyPage[0].Version).To(Equal(resourceVersions[9].Version))
   846  					Expect(historyPage[1].Version).To(Equal(resourceVersions[8].Version))
   847  					Expect(pagination.Newer).To(BeNil())
   848  					Expect(pagination.Older).To(Equal(&db.Page{To: db.NewIntPtr(resourceVersions[7].ID), Limit: 2}))
   849  				})
   850  			})
   851  
   852  			Context("when the version has metadata", func() {
   853  				BeforeEach(func() {
   854  					metadata := []db.ResourceConfigMetadataField{{Name: "name1", Value: "value1"}}
   855  
   856  					scenario.Run(builder.WithVersionMetadata("some-resource", atc.Version(resourceVersions[9].Version), metadata))
   857  				})
   858  
   859  				It("returns the metadata in the version history", func() {
   860  					historyPage, _, found, err := scenario.Resource("some-resource").Versions(db.Page{Limit: 1}, nil)
   861  					Expect(err).ToNot(HaveOccurred())
   862  					Expect(found).To(BeTrue())
   863  					Expect(len(historyPage)).To(Equal(1))
   864  					Expect(historyPage[0].Version).To(Equal(resourceVersions[9].Version))
   865  					Expect(historyPage[0].Metadata).To(Equal([]atc.MetadataField{{Name: "name1", Value: "value1"}}))
   866  				})
   867  
   868  				It("maintains existing metadata after same version is saved with no metadata", func() {
   869  					scenario.Run(builder.WithResourceVersions("some-resource", atc.Version(resourceVersions[9].Version)))
   870  
   871  					historyPage, _, found, err := scenario.Resource("some-resource").Versions(db.Page{Limit: 1}, atc.Version{})
   872  					Expect(err).ToNot(HaveOccurred())
   873  					Expect(found).To(BeTrue())
   874  					Expect(len(historyPage)).To(Equal(1))
   875  					Expect(historyPage[0].Version).To(Equal(resourceVersions[9].Version))
   876  					Expect(historyPage[0].Metadata).To(Equal([]atc.MetadataField{{Name: "name1", Value: "value1"}}))
   877  				})
   878  
   879  				It("updates metadata after same version is saved with different metadata", func() {
   880  					newMetadata := []db.ResourceConfigMetadataField{{Name: "name-new", Value: "value-new"}}
   881  					scenario.Run(builder.WithVersionMetadata("some-resource", atc.Version(resourceVersions[9].Version), newMetadata))
   882  
   883  					historyPage, _, found, err := scenario.Resource("some-resource").Versions(db.Page{Limit: 1}, atc.Version{})
   884  					Expect(err).ToNot(HaveOccurred())
   885  					Expect(found).To(BeTrue())
   886  					Expect(len(historyPage)).To(Equal(1))
   887  					Expect(historyPage[0].Version).To(Equal(resourceVersions[9].Version))
   888  					Expect(historyPage[0].Metadata).To(Equal([]atc.MetadataField{{Name: "name-new", Value: "value-new"}}))
   889  				})
   890  			})
   891  
   892  			Context("when a version is disabled", func() {
   893  				BeforeEach(func() {
   894  					scenario.Run(builder.WithDisabledVersion("some-resource", resourceVersions[9].Version))
   895  
   896  					resourceVersions[9].Enabled = false
   897  				})
   898  
   899  				It("returns a disabled version", func() {
   900  					historyPage, _, found, err := scenario.Resource("some-resource").Versions(db.Page{Limit: 1}, nil)
   901  					Expect(err).ToNot(HaveOccurred())
   902  					Expect(found).To(BeTrue())
   903  					Expect(historyPage).To(ConsistOf([]atc.ResourceVersion{resourceVersions[9]}))
   904  				})
   905  			})
   906  
   907  			Context("when the version metadata is updated", func() {
   908  				var metadata db.ResourceConfigMetadataFields
   909  
   910  				BeforeEach(func() {
   911  					metadata = []db.ResourceConfigMetadataField{{Name: "name1", Value: "value1"}}
   912  
   913  					scenario.Run(builder.WithVersionMetadata("some-resource", atc.Version(resourceVersions[9].Version), metadata))
   914  				})
   915  
   916  				It("returns a version with metadata updated", func() {
   917  					historyPage, _, found, err := scenario.Resource("some-resource").Versions(db.Page{Limit: 1}, nil)
   918  					Expect(err).ToNot(HaveOccurred())
   919  					Expect(found).To(BeTrue())
   920  					Expect(len(historyPage)).To(Equal(1))
   921  					Expect(historyPage[0].Version).To(Equal(resourceVersions[9].Version))
   922  					Expect(historyPage[0].Metadata).To(Equal([]atc.MetadataField{{Name: "name1", Value: "value1"}}))
   923  				})
   924  			})
   925  		})
   926  
   927  		Context("when check orders are different than versions ids", func() {
   928  			var resourceVersions []atc.ResourceVersion
   929  
   930  			BeforeEach(func() {
   931  				scenario = dbtest.Setup(
   932  					builder.WithPipeline(atc.Config{
   933  						Resources: atc.ResourceConfigs{
   934  							{
   935  								Name:   "some-resource",
   936  								Type:   "some-base-resource-type",
   937  								Source: atc.Source{"some": "repository"},
   938  							},
   939  						},
   940  					}),
   941  					builder.WithResourceVersions(
   942  						"some-resource",
   943  						atc.Version{"ref": "v1"}, // id: 1, check_order: 1
   944  						atc.Version{"ref": "v3"}, // id: 2, check_order: 2
   945  						atc.Version{"ref": "v4"}, // id: 3, check_order: 3
   946  					),
   947  					builder.WithResourceVersions(
   948  						"some-resource",
   949  						atc.Version{"ref": "v2"}, // id: 4, check_order: 4
   950  						atc.Version{"ref": "v3"}, // id: 2, check_order: 5
   951  						atc.Version{"ref": "v4"}, // id: 3, check_order: 6
   952  					),
   953  				)
   954  
   955  				for i := 1; i < 5; i++ {
   956  					rcv := scenario.ResourceVersion("some-resource", atc.Version{"ref": "v" + strconv.Itoa(i)})
   957  
   958  					var metadata []atc.MetadataField
   959  
   960  					for _, v := range rcv.Metadata() {
   961  						metadata = append(metadata, atc.MetadataField(v))
   962  					}
   963  
   964  					resourceVersion := atc.ResourceVersion{
   965  						ID:       rcv.ID(),
   966  						Version:  atc.Version(rcv.Version()),
   967  						Metadata: metadata,
   968  						Enabled:  true,
   969  					}
   970  
   971  					resourceVersions = append(resourceVersions, resourceVersion)
   972  				}
   973  
   974  				// ids ordered by check order now: [3, 2, 4, 1]
   975  			})
   976  
   977  			Context("with no from/to", func() {
   978  				It("returns versions ordered by check order", func() {
   979  					historyPage, pagination, found, err := scenario.Resource("some-resource").Versions(db.Page{Limit: 4}, nil)
   980  					Expect(err).ToNot(HaveOccurred())
   981  					Expect(found).To(BeTrue())
   982  					Expect(historyPage).To(HaveLen(4))
   983  					Expect(historyPage[0].Version).To(Equal(resourceVersions[3].Version))
   984  					Expect(historyPage[1].Version).To(Equal(resourceVersions[2].Version))
   985  					Expect(historyPage[2].Version).To(Equal(resourceVersions[1].Version))
   986  					Expect(historyPage[3].Version).To(Equal(resourceVersions[0].Version))
   987  					Expect(pagination.Newer).To(BeNil())
   988  					Expect(pagination.Older).To(BeNil())
   989  				})
   990  			})
   991  
   992  			Context("with from", func() {
   993  				It("returns the versions, with previous/next pages including from", func() {
   994  					historyPage, pagination, found, err := scenario.Resource("some-resource").Versions(db.Page{From: db.NewIntPtr(resourceVersions[1].ID), Limit: 2}, nil)
   995  					Expect(err).ToNot(HaveOccurred())
   996  					Expect(found).To(BeTrue())
   997  					Expect(historyPage).To(HaveLen(2))
   998  					Expect(historyPage[0].Version).To(Equal(resourceVersions[2].Version))
   999  					Expect(historyPage[1].Version).To(Equal(resourceVersions[1].Version))
  1000  					Expect(pagination.Newer).To(Equal(&db.Page{From: db.NewIntPtr(resourceVersions[3].ID), Limit: 2}))
  1001  					Expect(pagination.Older).To(Equal(&db.Page{To: db.NewIntPtr(resourceVersions[0].ID), Limit: 2}))
  1002  				})
  1003  			})
  1004  
  1005  			Context("with to", func() {
  1006  				It("returns the builds, with previous/next pages including to", func() {
  1007  					historyPage, pagination, found, err := scenario.Resource("some-resource").Versions(db.Page{To: db.NewIntPtr(resourceVersions[2].ID), Limit: 2}, nil)
  1008  					Expect(err).ToNot(HaveOccurred())
  1009  					Expect(found).To(BeTrue())
  1010  					Expect(historyPage).To(HaveLen(2))
  1011  					Expect(historyPage[0].Version).To(Equal(resourceVersions[2].Version))
  1012  					Expect(historyPage[1].Version).To(Equal(resourceVersions[1].Version))
  1013  					Expect(pagination.Newer).To(Equal(&db.Page{From: db.NewIntPtr(resourceVersions[3].ID), Limit: 2}))
  1014  					Expect(pagination.Older).To(Equal(&db.Page{To: db.NewIntPtr(resourceVersions[0].ID), Limit: 2}))
  1015  				})
  1016  			})
  1017  		})
  1018  	})
  1019  
  1020  	Describe("PinVersion/UnpinVersion", func() {
  1021  		var (
  1022  			scenario *dbtest.Scenario
  1023  		)
  1024  
  1025  		BeforeEach(func() {
  1026  			scenario = dbtest.Setup(
  1027  				builder.WithPipeline(atc.Config{
  1028  					Resources: atc.ResourceConfigs{
  1029  						{
  1030  							Name:   "some-resource",
  1031  							Type:   "some-base-resource-type",
  1032  							Source: atc.Source{"some": "repository"},
  1033  						},
  1034  					},
  1035  					Jobs: atc.JobConfigs{
  1036  						{
  1037  							Name: "job-using-resource",
  1038  							PlanSequence: []atc.Step{
  1039  								{
  1040  									Config: &atc.GetStep{
  1041  										Name: "some-resource",
  1042  									},
  1043  								},
  1044  							},
  1045  						},
  1046  						{
  1047  							Name: "not-using-resource",
  1048  						},
  1049  					},
  1050  				}),
  1051  				builder.WithResourceVersions(
  1052  					"some-resource",
  1053  					atc.Version{"version": "v1"},
  1054  					atc.Version{"version": "v2"},
  1055  					atc.Version{"version": "v3"},
  1056  				),
  1057  			)
  1058  		})
  1059  
  1060  		Context("when we use an invalid version id (does not exist)", func() {
  1061  			var (
  1062  				pinnedVersion atc.Version
  1063  			)
  1064  
  1065  			BeforeEach(func() {
  1066  				found, err := scenario.Resource("some-resource").PinVersion(scenario.ResourceVersion("some-resource", atc.Version{"version": "v1"}).ID())
  1067  				Expect(found).To(BeTrue())
  1068  				Expect(err).ToNot(HaveOccurred())
  1069  
  1070  				Expect(scenario.Resource("some-resource").CurrentPinnedVersion()).To(Equal(scenario.Resource("some-resource").APIPinnedVersion()))
  1071  				pinnedVersion = scenario.Resource("some-resource").APIPinnedVersion()
  1072  			})
  1073  
  1074  			It("returns not found and does not update anything", func() {
  1075  				found, err := scenario.Resource("some-resource").PinVersion(-1)
  1076  				Expect(found).To(BeFalse())
  1077  				Expect(err).To(HaveOccurred())
  1078  
  1079  				Expect(scenario.Resource("some-resource").APIPinnedVersion()).To(Equal(pinnedVersion))
  1080  			})
  1081  		})
  1082  
  1083  		Context("when requesting schedule for version pinning", func() {
  1084  			It("requests schedule on all jobs using the resource", func() {
  1085  				requestedSchedule := scenario.Job("job-using-resource").ScheduleRequestedTime()
  1086  
  1087  				found, err := scenario.Resource("some-resource").PinVersion(scenario.ResourceVersion("some-resource", atc.Version{"version": "v1"}).ID())
  1088  				Expect(found).To(BeTrue())
  1089  				Expect(err).ToNot(HaveOccurred())
  1090  
  1091  				Expect(scenario.Job("job-using-resource").ScheduleRequestedTime()).Should(BeTemporally(">", requestedSchedule))
  1092  			})
  1093  
  1094  			It("does not request schedule on jobs that do not use the resource", func() {
  1095  				requestedSchedule := scenario.Job("not-using-resource").ScheduleRequestedTime()
  1096  
  1097  				found, err := scenario.Resource("some-resource").PinVersion(scenario.ResourceVersion("some-resource", atc.Version{"version": "v1"}).ID())
  1098  				Expect(found).To(BeTrue())
  1099  				Expect(err).ToNot(HaveOccurred())
  1100  
  1101  				Expect(scenario.Job("not-using-resource").ScheduleRequestedTime()).Should(BeTemporally("==", requestedSchedule))
  1102  			})
  1103  		})
  1104  
  1105  		Context("when we pin a resource to a version", func() {
  1106  			BeforeEach(func() {
  1107  				found, err := scenario.Resource("some-resource").PinVersion(scenario.ResourceVersion("some-resource", atc.Version{"version": "v1"}).ID())
  1108  				Expect(found).To(BeTrue())
  1109  				Expect(err).ToNot(HaveOccurred())
  1110  			})
  1111  
  1112  			Context("when the resource is not pinned", func() {
  1113  				It("sets the api pinned version", func() {
  1114  					Expect(scenario.Resource("some-resource").APIPinnedVersion()).To(Equal(atc.Version{"version": "v1"}))
  1115  					Expect(scenario.Resource("some-resource").CurrentPinnedVersion()).To(Equal(scenario.Resource("some-resource").APIPinnedVersion()))
  1116  				})
  1117  			})
  1118  
  1119  			Context("when the resource is pinned by another version already", func() {
  1120  				BeforeEach(func() {
  1121  					found, err := scenario.Resource("some-resource").PinVersion(scenario.ResourceVersion("some-resource", atc.Version{"version": "v3"}).ID())
  1122  					Expect(found).To(BeTrue())
  1123  					Expect(err).ToNot(HaveOccurred())
  1124  				})
  1125  
  1126  				It("switch the pin to given version", func() {
  1127  					Expect(scenario.Resource("some-resource").APIPinnedVersion()).To(Equal(atc.Version{"version": "v3"}))
  1128  					Expect(scenario.Resource("some-resource").CurrentPinnedVersion()).To(Equal(scenario.Resource("some-resource").APIPinnedVersion()))
  1129  				})
  1130  			})
  1131  
  1132  			Context("when we set the pin comment on a resource", func() {
  1133  				BeforeEach(func() {
  1134  					err := scenario.Resource("some-resource").SetPinComment("foo")
  1135  					Expect(err).ToNot(HaveOccurred())
  1136  				})
  1137  
  1138  				It("should set the pin comment", func() {
  1139  					Expect(scenario.Resource("some-resource").PinComment()).To(Equal("foo"))
  1140  				})
  1141  			})
  1142  
  1143  			Context("when requesting schedule for version unpinning", func() {
  1144  				It("requests schedule on all jobs using the resource", func() {
  1145  					requestedSchedule := scenario.Job("job-using-resource").ScheduleRequestedTime()
  1146  
  1147  					err := scenario.Resource("some-resource").UnpinVersion()
  1148  					Expect(err).ToNot(HaveOccurred())
  1149  
  1150  					Expect(scenario.Job("job-using-resource").ScheduleRequestedTime()).Should(BeTemporally(">", requestedSchedule))
  1151  				})
  1152  
  1153  				It("does not request schedule on jobs that do not use the resource", func() {
  1154  					requestedSchedule := scenario.Job("not-using-resource").ScheduleRequestedTime()
  1155  
  1156  					err := scenario.Resource("some-resource").UnpinVersion()
  1157  					Expect(err).ToNot(HaveOccurred())
  1158  
  1159  					Expect(scenario.Job("not-using-resource").ScheduleRequestedTime()).Should(BeTemporally("==", requestedSchedule))
  1160  				})
  1161  			})
  1162  
  1163  			Context("when we unpin a resource to a version", func() {
  1164  				BeforeEach(func() {
  1165  					err := scenario.Resource("some-resource").UnpinVersion()
  1166  					Expect(err).ToNot(HaveOccurred())
  1167  				})
  1168  
  1169  				It("sets the api pinned version to nil", func() {
  1170  					Expect(scenario.Resource("some-resource").APIPinnedVersion()).To(BeNil())
  1171  					Expect(scenario.Resource("some-resource").CurrentPinnedVersion()).To(BeNil())
  1172  				})
  1173  
  1174  				It("unsets the pin comment", func() {
  1175  					Expect(scenario.Resource("some-resource").PinComment()).To(BeEmpty())
  1176  				})
  1177  			})
  1178  		})
  1179  
  1180  		Context("when we pin a resource that is already pinned to a version (through the config)", func() {
  1181  			BeforeEach(func() {
  1182  				scenario.Run(
  1183  					builder.WithPipeline(atc.Config{
  1184  						Resources: atc.ResourceConfigs{
  1185  							{
  1186  								Name:    "some-resource",
  1187  								Type:    "some-base-resource-type",
  1188  								Source:  atc.Source{"some": "repository"},
  1189  								Version: atc.Version{"pinned": "version"},
  1190  							},
  1191  						},
  1192  					}),
  1193  					builder.WithResourceVersions(
  1194  						"some-resource",
  1195  						atc.Version{"version": "v1"},
  1196  						atc.Version{"version": "v2"},
  1197  						atc.Version{"version": "v3"},
  1198  					))
  1199  			})
  1200  
  1201  			It("should fail to update the pinned version", func() {
  1202  				found, err := scenario.Resource("some-resource").PinVersion(scenario.ResourceVersion("some-resource", atc.Version{"version": "v1"}).ID())
  1203  				Expect(found).To(BeFalse())
  1204  				Expect(err).To(Equal(db.ErrPinnedThroughConfig))
  1205  			})
  1206  		})
  1207  	})
  1208  
  1209  	Describe("Public", func() {
  1210  		var (
  1211  			resource db.Resource
  1212  			found    bool
  1213  			err      error
  1214  		)
  1215  
  1216  		Context("when public is not set in the config", func() {
  1217  			BeforeEach(func() {
  1218  				resource, found, err = pipeline.Resource("some-resource")
  1219  				Expect(err).ToNot(HaveOccurred())
  1220  				Expect(found).To(BeTrue())
  1221  			})
  1222  
  1223  			It("returns false", func() {
  1224  				Expect(resource.Public()).To(BeFalse())
  1225  			})
  1226  		})
  1227  
  1228  		Context("when public is set to true in the config", func() {
  1229  			BeforeEach(func() {
  1230  				resource, found, err = pipeline.Resource("some-other-resource")
  1231  				Expect(err).ToNot(HaveOccurred())
  1232  				Expect(found).To(BeTrue())
  1233  			})
  1234  
  1235  			It("returns true", func() {
  1236  				Expect(resource.Public()).To(BeTrue())
  1237  			})
  1238  		})
  1239  
  1240  		Context("when public is set to false in the config", func() {
  1241  			BeforeEach(func() {
  1242  				resource, found, err = pipeline.Resource("some-secret-resource")
  1243  				Expect(err).ToNot(HaveOccurred())
  1244  				Expect(found).To(BeTrue())
  1245  			})
  1246  
  1247  			It("returns false", func() {
  1248  				Expect(resource.Public()).To(BeFalse())
  1249  			})
  1250  		})
  1251  	})
  1252  })