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