github.com/cloudfoundry/cli@v7.1.0+incompatible/actor/v2action/buildpack_test.go (about)

     1  package v2action_test
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  	"io/ioutil"
     7  	"os"
     8  	"path/filepath"
     9  	"strings"
    10  
    11  	. "github.com/onsi/ginkgo"
    12  	. "github.com/onsi/gomega"
    13  
    14  	"code.cloudfoundry.org/cli/actor/actionerror"
    15  	. "code.cloudfoundry.org/cli/actor/v2action"
    16  	"code.cloudfoundry.org/cli/actor/v2action/v2actionfakes"
    17  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
    18  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv2"
    19  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv2/constant"
    20  	"code.cloudfoundry.org/cli/types"
    21  )
    22  
    23  var _ = Describe("Buildpack", func() {
    24  	var (
    25  		actor                     *Actor
    26  		fakeCloudControllerClient *v2actionfakes.FakeCloudControllerClient
    27  		fakeConfig                *v2actionfakes.FakeConfig
    28  	)
    29  
    30  	BeforeEach(func() {
    31  		fakeCloudControllerClient = new(v2actionfakes.FakeCloudControllerClient)
    32  		fakeConfig = new(v2actionfakes.FakeConfig)
    33  		actor = NewActor(fakeCloudControllerClient, nil, fakeConfig)
    34  	})
    35  
    36  	Describe("Buildpack", func() {
    37  		Describe("NoStack", func() {
    38  			var buildpack Buildpack
    39  
    40  			When("the stack is empty", func() {
    41  				BeforeEach(func() {
    42  					buildpack.Stack = ""
    43  				})
    44  
    45  				It("returns true", func() {
    46  					Expect(buildpack.NoStack()).To(BeTrue())
    47  				})
    48  			})
    49  
    50  			When("the stack is set", func() {
    51  				BeforeEach(func() {
    52  					buildpack.Stack = "something i guess"
    53  				})
    54  
    55  				It("returns false", func() {
    56  					Expect(buildpack.NoStack()).To(BeFalse())
    57  				})
    58  			})
    59  		})
    60  	})
    61  
    62  	Describe("CreateBuildpack", func() {
    63  		var (
    64  			buildpack  Buildpack
    65  			warnings   Warnings
    66  			executeErr error
    67  		)
    68  
    69  		JustBeforeEach(func() {
    70  			buildpack, warnings, executeErr = actor.CreateBuildpack("some-bp-name", 42, true)
    71  		})
    72  
    73  		When("creating the buildpack is successful", func() {
    74  			BeforeEach(func() {
    75  				fakeCloudControllerClient.CreateBuildpackReturns(ccv2.Buildpack{GUID: "some-guid"}, ccv2.Warnings{"some-create-warning"}, nil)
    76  			})
    77  
    78  			It("returns the buildpack and all warnings", func() {
    79  				Expect(executeErr).ToNot(HaveOccurred())
    80  				Expect(fakeCloudControllerClient.CreateBuildpackCallCount()).To(Equal(1))
    81  				Expect(fakeCloudControllerClient.CreateBuildpackArgsForCall(0)).To(Equal(ccv2.Buildpack{
    82  					Name:     "some-bp-name",
    83  					Position: types.NullInt{IsSet: true, Value: 42},
    84  					Enabled:  types.NullBool{IsSet: true, Value: true},
    85  				}))
    86  
    87  				Expect(buildpack).To(Equal(Buildpack{GUID: "some-guid"}))
    88  				Expect(warnings).To(ConsistOf("some-create-warning"))
    89  			})
    90  		})
    91  
    92  		When("the buildpack already exists with nil stack", func() {
    93  			BeforeEach(func() {
    94  				fakeCloudControllerClient.CreateBuildpackReturns(ccv2.Buildpack{}, ccv2.Warnings{"some-create-warning"}, ccerror.BuildpackInvalidError{Message: "some failure message"})
    95  			})
    96  
    97  			It("returns a BuildpackInvalidError error and all warnings", func() {
    98  				Expect(warnings).To(ConsistOf("some-create-warning"))
    99  				Expect(executeErr).To(MatchError(actionerror.BuildpackInvalidError{Message: "some failure message"}))
   100  			})
   101  		})
   102  
   103  		When("the buildpack name is taken", func() {
   104  			BeforeEach(func() {
   105  				fakeCloudControllerClient.CreateBuildpackReturns(ccv2.Buildpack{}, ccv2.Warnings{"some-create-warning"}, ccerror.BuildpackNameTakenError{Message: ""})
   106  			})
   107  
   108  			It("returns a BuildpackNameTaken error and all warnings", func() {
   109  				Expect(warnings).To(ConsistOf("some-create-warning"))
   110  				Expect(executeErr).To(MatchError(actionerror.BuildpackNameTakenError{Name: "some-bp-name"}))
   111  			})
   112  		})
   113  
   114  		When("a cc create error occurs", func() {
   115  			BeforeEach(func() {
   116  				fakeCloudControllerClient.CreateBuildpackReturns(ccv2.Buildpack{}, ccv2.Warnings{"some-create-warning"}, errors.New("kaboom"))
   117  			})
   118  
   119  			It("returns an error and all warnings", func() {
   120  				Expect(warnings).To(ConsistOf("some-create-warning"))
   121  				Expect(executeErr).To(MatchError("kaboom"))
   122  			})
   123  		})
   124  	})
   125  
   126  	Describe("PrepareBuildpackBits", func() {
   127  		var (
   128  			inPath         string
   129  			outPath        string
   130  			tmpDirPath     string
   131  			fakeDownloader *v2actionfakes.FakeDownloader
   132  
   133  			executeErr error
   134  		)
   135  
   136  		BeforeEach(func() {
   137  			fakeDownloader = new(v2actionfakes.FakeDownloader)
   138  		})
   139  
   140  		JustBeforeEach(func() {
   141  			outPath, executeErr = actor.PrepareBuildpackBits(inPath, tmpDirPath, fakeDownloader)
   142  		})
   143  
   144  		When("the buildpack path is a url", func() {
   145  			BeforeEach(func() {
   146  				inPath = "http://buildpacks.com/a.zip"
   147  				fakeDownloader = new(v2actionfakes.FakeDownloader)
   148  
   149  				var err error
   150  				tmpDirPath, err = ioutil.TempDir("", "buildpackdir-")
   151  				Expect(err).ToNot(HaveOccurred())
   152  			})
   153  
   154  			AfterEach(func() {
   155  				Expect(os.RemoveAll(tmpDirPath)).ToNot(HaveOccurred())
   156  			})
   157  
   158  			When("downloading the file succeeds", func() {
   159  				BeforeEach(func() {
   160  					fakeDownloader.DownloadReturns("/tmp/buildpackdir-100/a.zip", nil)
   161  				})
   162  
   163  				It("downloads the buildpack to a local file", func() {
   164  					Expect(executeErr).ToNot(HaveOccurred())
   165  					Expect(fakeDownloader.DownloadCallCount()).To(Equal(1))
   166  
   167  					inputPath, inputTmpDirPath := fakeDownloader.DownloadArgsForCall(0)
   168  					Expect(inputPath).To(Equal("http://buildpacks.com/a.zip"))
   169  					Expect(inputTmpDirPath).To(Equal(tmpDirPath))
   170  				})
   171  			})
   172  
   173  			When("downloading the file fails", func() {
   174  				BeforeEach(func() {
   175  					fakeDownloader.DownloadReturns("", errors.New("some-download-error"))
   176  				})
   177  
   178  				It("returns the error", func() {
   179  					Expect(executeErr).To(MatchError("some-download-error"))
   180  				})
   181  			})
   182  		})
   183  
   184  		When("the buildpack path points to a directory", func() {
   185  			var tempFile *os.File
   186  			BeforeEach(func() {
   187  				var err error
   188  				inPath, err = ioutil.TempDir("", "buildpackdir-")
   189  				Expect(err).ToNot(HaveOccurred())
   190  
   191  				tempFile, err = ioutil.TempFile(inPath, "foo")
   192  				Expect(err).ToNot(HaveOccurred())
   193  
   194  				tmpDirPath, err = ioutil.TempDir("", "buildpackdir-")
   195  				Expect(err).ToNot(HaveOccurred())
   196  			})
   197  
   198  			AfterEach(func() {
   199  				tempFile.Close()
   200  				Expect(os.RemoveAll(inPath)).ToNot(HaveOccurred())
   201  				Expect(os.RemoveAll(tmpDirPath)).ToNot(HaveOccurred())
   202  			})
   203  
   204  			It("returns a path to the zipped directory", func() {
   205  				Expect(executeErr).ToNot(HaveOccurred())
   206  				Expect(fakeDownloader.DownloadCallCount()).To(Equal(0))
   207  
   208  				Expect(filepath.Base(outPath)).To(Equal(filepath.Base(inPath) + ".zip"))
   209  			})
   210  		})
   211  
   212  		When("the buildpack path points to an empty directory", func() {
   213  			BeforeEach(func() {
   214  				var err error
   215  				inPath, err = ioutil.TempDir("", "some-empty-dir")
   216  				Expect(err).ToNot(HaveOccurred())
   217  
   218  				tmpDirPath, err = ioutil.TempDir("", "buildpackdir-")
   219  				Expect(err).ToNot(HaveOccurred())
   220  			})
   221  
   222  			AfterEach(func() {
   223  				Expect(os.RemoveAll(inPath)).ToNot(HaveOccurred())
   224  				Expect(os.RemoveAll(tmpDirPath)).ToNot(HaveOccurred())
   225  			})
   226  
   227  			It("returns an error", func() {
   228  				Expect(executeErr).To(MatchError(actionerror.EmptyBuildpackDirectoryError{Path: inPath}))
   229  			})
   230  		})
   231  
   232  		When("the buildpack path points to a zip file", func() {
   233  			BeforeEach(func() {
   234  				inPath = "/foo/buildpacks/a.zip"
   235  			})
   236  
   237  			It("returns the local filepath", func() {
   238  				Expect(executeErr).ToNot(HaveOccurred())
   239  				Expect(fakeDownloader.DownloadCallCount()).To(Equal(0))
   240  				Expect(outPath).To(Equal("/foo/buildpacks/a.zip"))
   241  			})
   242  		})
   243  	})
   244  
   245  	Describe("RenameBuildpack", func() {
   246  		var (
   247  			oldName    string
   248  			newName    string
   249  			stackName  string
   250  			warnings   Warnings
   251  			executeErr error
   252  		)
   253  
   254  		BeforeEach(func() {
   255  			oldName = "some-old-name"
   256  			newName = "some-new-name"
   257  			stackName = ""
   258  		})
   259  
   260  		JustBeforeEach(func() {
   261  			warnings, executeErr = actor.RenameBuildpack(oldName, newName, stackName)
   262  		})
   263  
   264  		When("lookup is based on only name of buildpack", func() {
   265  			When("the lookup returns one buildpack", func() {
   266  				BeforeEach(func() {
   267  					fakeCloudControllerClient.GetBuildpacksReturns(
   268  						[]ccv2.Buildpack{{Name: newName}},
   269  						ccv2.Warnings{"warning-1", "warning-2"},
   270  						nil)
   271  				})
   272  
   273  				When("the update succeeds", func() {
   274  					BeforeEach(func() {
   275  						fakeCloudControllerClient.UpdateBuildpackReturns(ccv2.Buildpack{},
   276  							ccv2.Warnings{"warning-3", "warning-4"},
   277  							nil)
   278  					})
   279  
   280  					It("returns warnings", func() {
   281  						Expect(executeErr).ToNot(HaveOccurred())
   282  						Expect(warnings).To(ConsistOf("warning-1", "warning-2", "warning-3", "warning-4"))
   283  					})
   284  				})
   285  
   286  				When("the update errors", func() {
   287  					BeforeEach(func() {
   288  						fakeCloudControllerClient.UpdateBuildpackReturns(ccv2.Buildpack{},
   289  							ccv2.Warnings{"warning-3", "warning-4"},
   290  							errors.New("some-error"))
   291  					})
   292  
   293  					It("returns the error and warnings", func() {
   294  						Expect(executeErr).To(MatchError("some-error"))
   295  						Expect(warnings).To(ConsistOf("warning-1", "warning-2", "warning-3", "warning-4"))
   296  					})
   297  				})
   298  			})
   299  
   300  			When("lookup returns multiple buildpacks", func() {
   301  				When("one buildpack does not have a stack", func() {
   302  					BeforeEach(func() {
   303  						fakeCloudControllerClient.GetBuildpacksReturns(
   304  							[]ccv2.Buildpack{
   305  								{Name: oldName, GUID: "guid-1"},
   306  								{Name: oldName, Stack: "some-stack", GUID: "guid-2"},
   307  							},
   308  							ccv2.Warnings{"warning-1", "warning-2"},
   309  							nil)
   310  					})
   311  
   312  					It("uses that buildpack", func() {
   313  						Expect(executeErr).ToNot(HaveOccurred())
   314  						actualBp := fakeCloudControllerClient.UpdateBuildpackArgsForCall(0)
   315  						Expect(actualBp).To(Equal(ccv2.Buildpack{Name: newName, GUID: "guid-1"}))
   316  						Expect(warnings).To(ConsistOf("warning-1", "warning-2"))
   317  					})
   318  				})
   319  
   320  				When("all the buildpacks have stacks", func() {
   321  					BeforeEach(func() {
   322  						fakeCloudControllerClient.GetBuildpacksReturns(
   323  							[]ccv2.Buildpack{
   324  								{Name: oldName, Stack: "a-stack"},
   325  								{Name: oldName, Stack: "some-stack"},
   326  							},
   327  							ccv2.Warnings{"warning-1", "warning-2"},
   328  							nil)
   329  					})
   330  
   331  					It("returns an error", func() {
   332  						Expect(executeErr).To(MatchError(actionerror.MultipleBuildpacksFoundError{BuildpackName: oldName}))
   333  						Expect(warnings).To(ConsistOf("warning-1", "warning-2"))
   334  					})
   335  				})
   336  			})
   337  		})
   338  
   339  		When("lookup is based on name and stack name of buildpack", func() {
   340  			BeforeEach(func() {
   341  				oldName = "some-old-name"
   342  				stackName = "cflinuxfs2"
   343  			})
   344  
   345  			When("the lookup succeeds", func() {
   346  				BeforeEach(func() {
   347  					fakeCloudControllerClient.GetBuildpacksReturns(
   348  						[]ccv2.Buildpack{{Name: newName}},
   349  						ccv2.Warnings{"warning-1", "warning-2"},
   350  						nil,
   351  					)
   352  				})
   353  
   354  				It("uses the stack filter when looking up buildpacks", func() {
   355  					filters := fakeCloudControllerClient.GetBuildpacksArgsForCall(0)
   356  					Expect(filters).To(ContainElement(ccv2.Filter{Type: constant.StackFilter, Operator: constant.EqualOperator, Values: []string{stackName}}))
   357  					Expect(executeErr).ToNot(HaveOccurred())
   358  					Expect(warnings).To(ConsistOf("warning-1", "warning-2"))
   359  				})
   360  			})
   361  
   362  			When("lookup returns multiple buildpacks", func() {
   363  				BeforeEach(func() {
   364  					fakeCloudControllerClient.GetBuildpacksReturns(
   365  						[]ccv2.Buildpack{
   366  							{Name: oldName, Stack: "a-stack"},
   367  							{Name: oldName, Stack: "some-stack"},
   368  						},
   369  						ccv2.Warnings{"warning-1", "warning-2"},
   370  						nil)
   371  				})
   372  
   373  				It("returns an error", func() {
   374  					Expect(executeErr).To(MatchError(actionerror.MultipleBuildpacksFoundError{BuildpackName: oldName}))
   375  					Expect(warnings).To(ConsistOf("warning-1", "warning-2"))
   376  				})
   377  			})
   378  		})
   379  
   380  		When("the lookup errors", func() {
   381  			BeforeEach(func() {
   382  				fakeCloudControllerClient.GetBuildpacksReturns(nil,
   383  					ccv2.Warnings{"warning-1", "warning-2"},
   384  					errors.New("some-lookup-error"))
   385  			})
   386  			It("returns the error and warnings", func() {
   387  				Expect(executeErr).To(MatchError("some-lookup-error"))
   388  				Expect(warnings).To(ConsistOf("warning-1", "warning-2"))
   389  			})
   390  		})
   391  	})
   392  
   393  	Describe("UpdateBuildpack", func() {
   394  		var (
   395  			buildpack        Buildpack
   396  			updatedBuildpack Buildpack
   397  			warnings         Warnings
   398  			executeErr       error
   399  		)
   400  
   401  		JustBeforeEach(func() {
   402  			buildpack = Buildpack{
   403  				Name:  "some-bp-name",
   404  				GUID:  "some-bp-guid",
   405  				Stack: "some-stack",
   406  			}
   407  			updatedBuildpack, warnings, executeErr = actor.UpdateBuildpack(buildpack)
   408  		})
   409  
   410  		When("there are no errors", func() {
   411  			BeforeEach(func() {
   412  				fakeCloudControllerClient.UpdateBuildpackReturns(ccv2.Buildpack{
   413  					Name:  "some-bp-name",
   414  					GUID:  "some-bp-guid",
   415  					Stack: "some-stack",
   416  				}, ccv2.Warnings{"some-warning"}, nil)
   417  			})
   418  
   419  			It("returns the updated buildpack", func() {
   420  				Expect(executeErr).ToNot(HaveOccurred())
   421  				Expect(warnings).To(ConsistOf(Warnings{"some-warning"}))
   422  				Expect(fakeCloudControllerClient.UpdateBuildpackCallCount()).To(Equal(1))
   423  
   424  				Expect(updatedBuildpack).To(Equal(buildpack))
   425  			})
   426  		})
   427  
   428  		When("the client errors", func() {
   429  			When("the buildpack is not found", func() {
   430  				BeforeEach(func() {
   431  					fakeCloudControllerClient.UpdateBuildpackReturns(ccv2.Buildpack{}, ccv2.Warnings{"some-warning"}, ccerror.ResourceNotFoundError{})
   432  				})
   433  
   434  				It("returns a buildpack not found error", func() {
   435  					Expect(executeErr).To(MatchError(actionerror.BuildpackNotFoundError{BuildpackName: "some-bp-name"}))
   436  					Expect(warnings).To(ConsistOf(Warnings{"some-warning"}))
   437  					Expect(fakeCloudControllerClient.UpdateBuildpackCallCount()).To(Equal(1))
   438  				})
   439  			})
   440  
   441  			When("the buildpack already exists without a stack association", func() {
   442  				BeforeEach(func() {
   443  					fakeCloudControllerClient.UpdateBuildpackReturns(ccv2.Buildpack{}, ccv2.Warnings{"some-warning"}, ccerror.BuildpackInvalidError{Message: "some failure message"})
   444  				})
   445  
   446  				It("returns a buildpack already exists without stack error", func() {
   447  					Expect(executeErr).To(MatchError(actionerror.BuildpackInvalidError{Message: "some failure message"}))
   448  					Expect(warnings).To(ConsistOf(Warnings{"some-warning"}))
   449  					Expect(fakeCloudControllerClient.UpdateBuildpackCallCount()).To(Equal(1))
   450  				})
   451  			})
   452  
   453  			When("the buildpack already exists with a stack association", func() {
   454  				BeforeEach(func() {
   455  					fakeCloudControllerClient.UpdateBuildpackReturns(ccv2.Buildpack{}, ccv2.Warnings{"some-warning"}, ccerror.BuildpackAlreadyExistsForStackError{Message: "some-message"})
   456  				})
   457  
   458  				It("returns a buildpack already exists for stack error", func() {
   459  					Expect(executeErr).To(MatchError(actionerror.BuildpackAlreadyExistsForStackError{Message: "some-message"}))
   460  					Expect(warnings).To(ConsistOf(Warnings{"some-warning"}))
   461  					Expect(fakeCloudControllerClient.UpdateBuildpackCallCount()).To(Equal(1))
   462  				})
   463  			})
   464  
   465  			When("the client returns a generic error", func() {
   466  				BeforeEach(func() {
   467  					fakeCloudControllerClient.UpdateBuildpackReturns(ccv2.Buildpack{}, ccv2.Warnings{"some-warning"}, errors.New("some-error"))
   468  				})
   469  
   470  				It("returns the error", func() {
   471  					Expect(executeErr).To(MatchError("some-error"))
   472  					Expect(warnings).To(ConsistOf(Warnings{"some-warning"}))
   473  					Expect(fakeCloudControllerClient.UpdateBuildpackCallCount()).To(Equal(1))
   474  				})
   475  			})
   476  		})
   477  	})
   478  
   479  	Describe("UpdateBuildpackByNameAndStack", func() {
   480  		var (
   481  			newPosition  types.NullInt
   482  			newLocked    types.NullBool
   483  			newEnabled   types.NullBool
   484  			currentStack string
   485  			newStack     string
   486  
   487  			buildpackGUID string
   488  			warnings      Warnings
   489  			executeErr    error
   490  		)
   491  
   492  		BeforeEach(func() {
   493  			newPosition = types.NullInt{}
   494  			newLocked = types.NullBool{}
   495  			newEnabled = types.NullBool{}
   496  			currentStack = ""
   497  			newStack = ""
   498  		})
   499  
   500  		JustBeforeEach(func() {
   501  			buildpackGUID, warnings, executeErr = actor.UpdateBuildpackByNameAndStack("some-bp-name", currentStack, newPosition, newLocked, newEnabled, newStack)
   502  		})
   503  
   504  		When("current stack is an empty string", func() {
   505  			It("gets the buildpack by name only", func() {
   506  				args := fakeCloudControllerClient.GetBuildpacksArgsForCall(0)
   507  				Expect(len(args)).To(Equal(1))
   508  				Expect(args[0].Values[0]).To(Equal("some-bp-name"))
   509  			})
   510  		})
   511  
   512  		When("a non-empty current stack name is passed", func() {
   513  			BeforeEach(func() {
   514  				currentStack = "some-stack"
   515  			})
   516  
   517  			It("gets the buildpack by name and current stack", func() {
   518  				Expect(fakeCloudControllerClient.GetBuildpacksCallCount()).To(Equal(1))
   519  				args := fakeCloudControllerClient.GetBuildpacksArgsForCall(0)
   520  				Expect(len(args)).To(Equal(2))
   521  				Expect(args[0].Values[0]).To(Equal("some-bp-name"))
   522  				Expect(args[1].Values[0]).To(Equal(currentStack))
   523  			})
   524  
   525  			When("the cc responds successfully", func() {
   526  				BeforeEach(func() {
   527  					fakeCloudControllerClient.GetBuildpacksReturns([]ccv2.Buildpack{{GUID: "some-guid"}}, ccv2.Warnings{"warning-1", "warning-2"}, nil)
   528  				})
   529  
   530  				It("returns the buildpack GUID", func() {
   531  					Expect(fakeCloudControllerClient.GetBuildpacksCallCount()).To(Equal(1))
   532  					Expect(executeErr).ToNot(HaveOccurred())
   533  					Expect(buildpackGUID).To(Equal("some-guid"))
   534  				})
   535  			})
   536  
   537  			When("the cc responds with an error", func() {
   538  				BeforeEach(func() {
   539  					fakeCloudControllerClient.GetBuildpacksReturns([]ccv2.Buildpack{}, ccv2.Warnings{"warning-1", "warning-2"}, errors.New("boom!"))
   540  				})
   541  
   542  				It("returns an error and all warnings", func() {
   543  					Expect(executeErr).To(MatchError("boom!"))
   544  					Expect(warnings).To(ConsistOf("warning-1", "warning-2"))
   545  				})
   546  			})
   547  		})
   548  
   549  		When("getting the buildpack fails", func() {
   550  			var expectedError error
   551  
   552  			BeforeEach(func() {
   553  				expectedError = errors.New("some-error")
   554  				fakeCloudControllerClient.GetBuildpacksReturns(nil, nil, expectedError)
   555  			})
   556  
   557  			It("returns the error", func() {
   558  				Expect(executeErr).To(MatchError(expectedError))
   559  			})
   560  		})
   561  
   562  		When("getting the buildpack returns an empty list", func() {
   563  			BeforeEach(func() {
   564  				fakeCloudControllerClient.GetBuildpacksReturns([]ccv2.Buildpack{}, ccv2.Warnings{"warning-1", "warning-2"}, nil)
   565  			})
   566  
   567  			It("returns a BuildpackNotFoundError and all warnings", func() {
   568  				Expect(executeErr).To(MatchError(actionerror.BuildpackNotFoundError{BuildpackName: "some-bp-name"}))
   569  				Expect(warnings).To(ConsistOf("warning-1", "warning-2"))
   570  			})
   571  		})
   572  
   573  		When("getting the buildpack returns multiple buildpacks with stacks", func() {
   574  			BeforeEach(func() {
   575  				fakeCloudControllerClient.GetBuildpacksReturns(
   576  					[]ccv2.Buildpack{
   577  						{GUID: "some guid", Name: "some-bp-name", Stack: "some-stack"},
   578  						{GUID: "some other guid", Name: "some-bp-name", Stack: "some-stack-2"},
   579  					},
   580  					ccv2.Warnings{"warning-1", "warning-2"},
   581  					nil)
   582  			})
   583  
   584  			It("returns an error that multiple buildpacks were found and returns all warnings", func() {
   585  				Expect(executeErr).To(MatchError(actionerror.MultipleBuildpacksFoundError{BuildpackName: "some-bp-name"}))
   586  				Expect(warnings).To(ConsistOf("warning-1", "warning-2"))
   587  			})
   588  		})
   589  
   590  		When("getting the buildpack successfully returns a single buildpack", func() {
   591  			BeforeEach(func() {
   592  				fakeCloudControllerClient.GetBuildpacksReturns([]ccv2.Buildpack{
   593  					ccv2.Buildpack{GUID: "some guid", Name: "some-bp-name"}}, ccv2.Warnings{"get warning"}, nil)
   594  			})
   595  
   596  			It("does not return an error", func() {
   597  				Expect(executeErr).ToNot(HaveOccurred())
   598  			})
   599  
   600  			It("returns any warnings", func() {
   601  				Expect(warnings).To(ConsistOf("get warning"))
   602  			})
   603  
   604  			When("no changes to the buildpack record are specified", func() {
   605  				It("doesn't call the CC API", func() {
   606  					Expect(fakeCloudControllerClient.UpdateBuildpackCallCount()).To(Equal(0))
   607  				})
   608  			})
   609  
   610  			When("a new position is specified", func() {
   611  				BeforeEach(func() {
   612  					newPosition = types.NullInt{IsSet: true, Value: 3}
   613  				})
   614  
   615  				It("makes an API call to update the position", func() {
   616  					Expect(fakeCloudControllerClient.UpdateBuildpackCallCount()).To(Equal(1))
   617  					passedBuildpack := fakeCloudControllerClient.UpdateBuildpackArgsForCall(0)
   618  					Expect(passedBuildpack.Position).To(Equal(newPosition))
   619  				})
   620  			})
   621  
   622  			When("a new locked state is specified", func() {
   623  				BeforeEach(func() {
   624  					newLocked = types.NullBool{IsSet: true, Value: true}
   625  				})
   626  
   627  				It("makes an API call to update the locked state", func() {
   628  					Expect(fakeCloudControllerClient.UpdateBuildpackCallCount()).To(Equal(1))
   629  					passedBuildpack := fakeCloudControllerClient.UpdateBuildpackArgsForCall(0)
   630  					Expect(passedBuildpack.Locked).To(Equal(newLocked))
   631  				})
   632  			})
   633  
   634  			When("a new enabled state is specified", func() {
   635  				BeforeEach(func() {
   636  					newEnabled = types.NullBool{IsSet: true, Value: true}
   637  				})
   638  
   639  				It("makes an API call to update the enabled state", func() {
   640  					Expect(fakeCloudControllerClient.UpdateBuildpackCallCount()).To(Equal(1))
   641  					passedBuildpack := fakeCloudControllerClient.UpdateBuildpackArgsForCall(0)
   642  					Expect(passedBuildpack.Enabled).To(Equal(newEnabled))
   643  				})
   644  			})
   645  
   646  			When("a new stack is specified", func() {
   647  				BeforeEach(func() {
   648  					newStack = "some-new-stack"
   649  					fakeCloudControllerClient.GetStacksReturns(
   650  						[]ccv2.Stack{{Name: newStack}},
   651  						nil,
   652  						nil,
   653  					)
   654  				})
   655  
   656  				When("new stack exists", func() {
   657  					It("makes an API call to update the stack", func() {
   658  						Expect(fakeCloudControllerClient.UpdateBuildpackCallCount()).To(Equal(1))
   659  						passedBuildpack := fakeCloudControllerClient.UpdateBuildpackArgsForCall(0)
   660  						Expect(passedBuildpack.Stack).To(Equal(newStack))
   661  					})
   662  
   663  					When("the buildpack already has a stack association", func() {
   664  						BeforeEach(func() {
   665  							fakeConfig.BinaryNameReturns("faceman")
   666  							fakeCloudControllerClient.GetBuildpacksReturns([]ccv2.Buildpack{
   667  								ccv2.Buildpack{Stack: "some-old-stack-name", Name: "some-bp-name"}}, ccv2.Warnings{"get warning"}, nil)
   668  						})
   669  
   670  						It("return the error and warnings", func() {
   671  							Expect(executeErr).To(MatchError(actionerror.BuildpackStackChangeError{BuildpackName: "some-bp-name", BinaryName: "faceman"}))
   672  							Expect(warnings).To(ConsistOf("get warning"))
   673  						})
   674  
   675  						When("there are multiple buildpacks with the same name with stack associations", func() {
   676  							BeforeEach(func() {
   677  								fakeCloudControllerClient.GetBuildpacksReturns([]ccv2.Buildpack{
   678  									ccv2.Buildpack{
   679  										Stack: "some-stack-name",
   680  										Name:  "some-bp-name",
   681  									},
   682  									ccv2.Buildpack{
   683  										Stack: "some-other-stack-name",
   684  										Name:  "some-bp-name",
   685  									}}, ccv2.Warnings{"warning-1", "warning-2"}, nil)
   686  							})
   687  
   688  							It("returns a BuildpackStackChangeError and warnings", func() {
   689  								Expect(executeErr).To(MatchError(actionerror.BuildpackStackChangeError{BuildpackName: "some-bp-name", BinaryName: "faceman"}))
   690  								Expect(warnings).To(ConsistOf("warning-1", "warning-2"))
   691  							})
   692  						})
   693  
   694  						When("there are multiple buildpacks with the same name and one has no stack association", func() {
   695  							BeforeEach(func() {
   696  								currentStack = ""
   697  								newStack = "some-new-stack"
   698  								fakeCloudControllerClient.GetBuildpacksReturns([]ccv2.Buildpack{
   699  									ccv2.Buildpack{
   700  										Stack: "some-stack-name",
   701  										Name:  "some-bp-name",
   702  										GUID:  "wrong-id",
   703  									},
   704  									ccv2.Buildpack{
   705  										Name: "some-bp-name",
   706  										GUID: "right-id",
   707  									}}, ccv2.Warnings{"warning-1", "warning-2"}, nil)
   708  							})
   709  
   710  							It("succeeds and returns all warnings", func() {
   711  								Expect(executeErr).NotTo(HaveOccurred())
   712  								Expect(warnings).To(ConsistOf("warning-1", "warning-2"))
   713  							})
   714  
   715  							It("updates the buildpack that had no stack association", func() {
   716  								Expect(fakeCloudControllerClient.UpdateBuildpackCallCount()).To(Equal(1))
   717  								ccv2Buildpack := fakeCloudControllerClient.UpdateBuildpackArgsForCall(0)
   718  								Expect(ccv2Buildpack.GUID).To(Equal("right-id"))
   719  							})
   720  						})
   721  					})
   722  				})
   723  
   724  				When("new stack does not exists", func() {
   725  					BeforeEach(func() {
   726  						newStack = "some-non-existing-stack"
   727  						fakeCloudControllerClient.GetStacksReturns(
   728  							[]ccv2.Stack{},
   729  							ccv2.Warnings{"get-stack-warning"},
   730  							nil,
   731  						)
   732  					})
   733  
   734  					It("returns the error and warnings", func() {
   735  						Expect(executeErr).To(MatchError(actionerror.StackNotFoundError{Name: newStack}))
   736  						Expect(warnings).To(ConsistOf("get-stack-warning"))
   737  					})
   738  				})
   739  			})
   740  
   741  			When("some arguments are specified and buildpack record update is needed", func() {
   742  				BeforeEach(func() {
   743  					newPosition = types.NullInt{IsSet: true, Value: 3}
   744  					newLocked = types.NullBool{IsSet: true, Value: true}
   745  					newEnabled = types.NullBool{IsSet: true, Value: true}
   746  				})
   747  
   748  				It("updates the buildpack using the correct buildpack values", func() {
   749  					Expect(fakeCloudControllerClient.UpdateBuildpackCallCount()).To(Equal(1))
   750  					Expect(fakeCloudControllerClient.UpdateBuildpackArgsForCall(0)).To(Equal(ccv2.Buildpack{
   751  						GUID:     "some guid",
   752  						Name:     "some-bp-name",
   753  						Position: types.NullInt{IsSet: true, Value: 3},
   754  						Locked:   types.NullBool{IsSet: true, Value: true},
   755  						Enabled:  types.NullBool{IsSet: true, Value: true},
   756  					}))
   757  				})
   758  
   759  				When("updating the buildpack record returns an error", func() {
   760  					BeforeEach(func() {
   761  						fakeCloudControllerClient.UpdateBuildpackReturns(ccv2.Buildpack{}, nil, errors.New("failed"))
   762  					})
   763  
   764  					It("returns the error", func() {
   765  						Expect(executeErr).To(MatchError("failed"))
   766  					})
   767  				})
   768  
   769  				When("updating the buildpack record succeeds", func() {
   770  					BeforeEach(func() {
   771  						fakeCloudControllerClient.UpdateBuildpackReturns(ccv2.Buildpack{GUID: "some guid"}, ccv2.Warnings{"update warning"}, nil)
   772  					})
   773  
   774  					It("does not return an error", func() {
   775  						Expect(executeErr).ToNot(HaveOccurred())
   776  					})
   777  
   778  					It("returns any warnings", func() {
   779  						Expect(warnings).To(ConsistOf("get warning", "update warning"))
   780  					})
   781  				})
   782  			})
   783  		})
   784  	})
   785  
   786  	Describe("UploadBuildpack", func() {
   787  		var (
   788  			bpFile     io.Reader
   789  			bpFilePath string
   790  			fakePb     *v2actionfakes.FakeSimpleProgressBar
   791  
   792  			warnings   Warnings
   793  			executeErr error
   794  		)
   795  
   796  		BeforeEach(func() {
   797  			bpFile = strings.NewReader("")
   798  		})
   799  
   800  		JustBeforeEach(func() {
   801  			fakePb = new(v2actionfakes.FakeSimpleProgressBar)
   802  			fakePb.InitializeReturns(bpFile, 0, nil)
   803  			bpFilePath = "tmp/buildpack.zip"
   804  			warnings, executeErr = actor.UploadBuildpack("some-bp-guid", bpFilePath, fakePb)
   805  		})
   806  
   807  		It("tracks the progress of the upload", func() {
   808  			Expect(executeErr).ToNot(HaveOccurred())
   809  			Expect(fakePb.InitializeCallCount()).To(Equal(1))
   810  			Expect(fakePb.InitializeArgsForCall(0)).To(Equal(bpFilePath))
   811  			Expect(fakePb.TerminateCallCount()).To(Equal(1))
   812  		})
   813  
   814  		When("the upload errors", func() {
   815  			BeforeEach(func() {
   816  				fakeCloudControllerClient.UploadBuildpackReturns(ccv2.Warnings{"some-upload-warning"}, errors.New("some-upload-error"))
   817  			})
   818  
   819  			It("returns warnings and errors", func() {
   820  				Expect(warnings).To(ConsistOf("some-upload-warning"))
   821  				Expect(executeErr).To(MatchError("some-upload-error"))
   822  			})
   823  		})
   824  
   825  		When("the cc returns an error because the buildpack and stack combo already exists", func() {
   826  			BeforeEach(func() {
   827  				fakeCloudControllerClient.UploadBuildpackReturns(ccv2.Warnings{"some-upload-warning"}, ccerror.BuildpackAlreadyExistsForStackError{Message: "ya blew it"})
   828  			})
   829  
   830  			It("returns warnings and a BuildpackAlreadyExistsForStackError", func() {
   831  				Expect(warnings).To(ConsistOf("some-upload-warning"))
   832  				Expect(executeErr).To(MatchError(actionerror.BuildpackAlreadyExistsForStackError{Message: "ya blew it"}))
   833  			})
   834  		})
   835  
   836  		When("the upload is successful", func() {
   837  			BeforeEach(func() {
   838  				fakeCloudControllerClient.UploadBuildpackReturns(ccv2.Warnings{"some-create-warning"}, nil)
   839  			})
   840  
   841  			It("uploads the buildpack and returns any warnings", func() {
   842  				Expect(executeErr).ToNot(HaveOccurred())
   843  				Expect(fakeCloudControllerClient.UploadBuildpackCallCount()).To(Equal(1))
   844  				guid, path, pbReader, size := fakeCloudControllerClient.UploadBuildpackArgsForCall(0)
   845  				Expect(guid).To(Equal("some-bp-guid"))
   846  				Expect(size).To(Equal(int64(0)))
   847  				Expect(path).To(Equal(bpFilePath))
   848  				Expect(pbReader).To(Equal(bpFile))
   849  				Expect(warnings).To(ConsistOf("some-create-warning"))
   850  			})
   851  		})
   852  	})
   853  
   854  	Describe("Zipit", func() {
   855  		//tested in buildpack_linux_test.go and buildpack_windows_test.go
   856  		var (
   857  			source string
   858  			target string
   859  
   860  			executeErr error
   861  		)
   862  
   863  		JustBeforeEach(func() {
   864  			executeErr = Zipit(source, target, "testzip-")
   865  		})
   866  
   867  		When("the source directory does not exist", func() {
   868  			BeforeEach(func() {
   869  				source = ""
   870  				target = ""
   871  			})
   872  
   873  			It("returns an error", func() {
   874  				Expect(os.IsNotExist(executeErr)).To(BeTrue())
   875  			})
   876  		})
   877  	})
   878  })