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

     1  package v3action_test
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  	"io/ioutil"
     7  	"os"
     8  	"strings"
     9  
    10  	"code.cloudfoundry.org/cli/actor/actionerror"
    11  	"code.cloudfoundry.org/cli/actor/sharedaction"
    12  	. "code.cloudfoundry.org/cli/actor/v3action"
    13  	"code.cloudfoundry.org/cli/actor/v3action/v3actionfakes"
    14  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3"
    15  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
    16  	"code.cloudfoundry.org/cli/resources"
    17  
    18  	. "github.com/onsi/ginkgo"
    19  	. "github.com/onsi/ginkgo/extensions/table"
    20  	. "github.com/onsi/gomega"
    21  	. "github.com/onsi/gomega/gstruct"
    22  )
    23  
    24  var _ = Describe("Package Actions", func() {
    25  	var (
    26  		actor                     *Actor
    27  		fakeCloudControllerClient *v3actionfakes.FakeCloudControllerClient
    28  		fakeSharedActor           *v3actionfakes.FakeSharedActor
    29  		fakeConfig                *v3actionfakes.FakeConfig
    30  	)
    31  
    32  	BeforeEach(func() {
    33  		fakeCloudControllerClient = new(v3actionfakes.FakeCloudControllerClient)
    34  		fakeConfig = new(v3actionfakes.FakeConfig)
    35  		fakeSharedActor = new(v3actionfakes.FakeSharedActor)
    36  		actor = NewActor(fakeCloudControllerClient, fakeConfig, fakeSharedActor, nil)
    37  	})
    38  
    39  	Describe("GetApplicationPackages", func() {
    40  		When("there are no client errors", func() {
    41  			BeforeEach(func() {
    42  				fakeCloudControllerClient.GetApplicationsReturns(
    43  					[]resources.Application{
    44  						{GUID: "some-app-guid"},
    45  					},
    46  					ccv3.Warnings{"get-applications-warning"},
    47  					nil,
    48  				)
    49  
    50  				fakeCloudControllerClient.GetPackagesReturns(
    51  					[]ccv3.Package{
    52  						{
    53  							GUID:      "some-package-guid-1",
    54  							State:     constant.PackageReady,
    55  							CreatedAt: "2017-08-14T21:16:42Z",
    56  						},
    57  						{
    58  							GUID:      "some-package-guid-2",
    59  							State:     constant.PackageFailed,
    60  							CreatedAt: "2017-08-16T00:18:24Z",
    61  						},
    62  					},
    63  					ccv3.Warnings{"get-application-packages-warning"},
    64  					nil,
    65  				)
    66  			})
    67  
    68  			It("gets the app's packages", func() {
    69  				packages, warnings, err := actor.GetApplicationPackages("some-app-name", "some-space-guid")
    70  
    71  				Expect(err).ToNot(HaveOccurred())
    72  				Expect(warnings).To(ConsistOf("get-applications-warning", "get-application-packages-warning"))
    73  				Expect(packages).To(Equal([]Package{
    74  					{
    75  						GUID:      "some-package-guid-1",
    76  						State:     constant.PackageReady,
    77  						CreatedAt: "2017-08-14T21:16:42Z",
    78  					},
    79  					{
    80  						GUID:      "some-package-guid-2",
    81  						State:     constant.PackageFailed,
    82  						CreatedAt: "2017-08-16T00:18:24Z",
    83  					},
    84  				}))
    85  
    86  				Expect(fakeCloudControllerClient.GetApplicationsCallCount()).To(Equal(1))
    87  				Expect(fakeCloudControllerClient.GetApplicationsArgsForCall(0)).To(ConsistOf(
    88  					ccv3.Query{Key: ccv3.NameFilter, Values: []string{"some-app-name"}},
    89  					ccv3.Query{Key: ccv3.SpaceGUIDFilter, Values: []string{"some-space-guid"}},
    90  				))
    91  
    92  				Expect(fakeCloudControllerClient.GetPackagesCallCount()).To(Equal(1))
    93  				Expect(fakeCloudControllerClient.GetPackagesArgsForCall(0)).To(ConsistOf(
    94  					ccv3.Query{Key: ccv3.AppGUIDFilter, Values: []string{"some-app-guid"}},
    95  				))
    96  			})
    97  		})
    98  
    99  		When("getting the application fails", func() {
   100  			var expectedErr error
   101  
   102  			BeforeEach(func() {
   103  				expectedErr = errors.New("some get application error")
   104  
   105  				fakeCloudControllerClient.GetApplicationsReturns(
   106  					[]resources.Application{},
   107  					ccv3.Warnings{"get-applications-warning"},
   108  					expectedErr,
   109  				)
   110  			})
   111  
   112  			It("returns the error", func() {
   113  				_, warnings, err := actor.GetApplicationPackages("some-app-name", "some-space-guid")
   114  
   115  				Expect(err).To(Equal(expectedErr))
   116  				Expect(warnings).To(ConsistOf("get-applications-warning"))
   117  			})
   118  		})
   119  
   120  		When("getting the application packages fails", func() {
   121  			var expectedErr error
   122  
   123  			BeforeEach(func() {
   124  				expectedErr = errors.New("some get application error")
   125  
   126  				fakeCloudControllerClient.GetApplicationsReturns(
   127  					[]resources.Application{
   128  						{GUID: "some-app-guid"},
   129  					},
   130  					ccv3.Warnings{"get-applications-warning"},
   131  					nil,
   132  				)
   133  
   134  				fakeCloudControllerClient.GetPackagesReturns(
   135  					[]ccv3.Package{},
   136  					ccv3.Warnings{"get-application-packages-warning"},
   137  					expectedErr,
   138  				)
   139  			})
   140  
   141  			It("returns the error", func() {
   142  				_, warnings, err := actor.GetApplicationPackages("some-app-name", "some-space-guid")
   143  
   144  				Expect(err).To(Equal(expectedErr))
   145  				Expect(warnings).To(ConsistOf("get-applications-warning", "get-application-packages-warning"))
   146  			})
   147  		})
   148  	})
   149  
   150  	Describe("CreateDockerPackageByApplicationNameAndSpace", func() {
   151  		var (
   152  			dockerPackage Package
   153  			warnings      Warnings
   154  			executeErr    error
   155  		)
   156  
   157  		JustBeforeEach(func() {
   158  			dockerPackage, warnings, executeErr = actor.CreateDockerPackageByApplicationNameAndSpace("some-app-name", "some-space-guid", DockerImageCredentials{Path: "some-docker-image", Password: "some-password", Username: "some-username"})
   159  		})
   160  
   161  		When("the application can't be retrieved", func() {
   162  			BeforeEach(func() {
   163  				fakeCloudControllerClient.GetApplicationsReturns(
   164  					[]resources.Application{},
   165  					ccv3.Warnings{"some-app-warning"},
   166  					errors.New("some-app-error"),
   167  				)
   168  			})
   169  
   170  			It("returns the error and all warnings", func() {
   171  				Expect(executeErr).To(MatchError("some-app-error"))
   172  				Expect(warnings).To(ConsistOf("some-app-warning"))
   173  			})
   174  		})
   175  
   176  		When("the application can be retrieved", func() {
   177  			BeforeEach(func() {
   178  				fakeCloudControllerClient.GetApplicationsReturns(
   179  					[]resources.Application{
   180  						{
   181  							Name: "some-app-name",
   182  							GUID: "some-app-guid",
   183  						},
   184  					},
   185  					ccv3.Warnings{"some-app-warning"},
   186  					nil,
   187  				)
   188  			})
   189  
   190  			When("creating the package fails", func() {
   191  				BeforeEach(func() {
   192  					fakeCloudControllerClient.CreatePackageReturns(
   193  						ccv3.Package{},
   194  						ccv3.Warnings{"some-create-package-warning"},
   195  						errors.New("some-create-package-error"),
   196  					)
   197  				})
   198  				It("fails to create the package", func() {
   199  					Expect(executeErr).To(MatchError("some-create-package-error"))
   200  					Expect(warnings).To(ConsistOf("some-app-warning", "some-create-package-warning"))
   201  				})
   202  			})
   203  
   204  			When("creating the package succeeds", func() {
   205  				BeforeEach(func() {
   206  					createdPackage := ccv3.Package{
   207  						DockerImage:    "some-docker-image",
   208  						DockerUsername: "some-username",
   209  						DockerPassword: "some-password",
   210  						GUID:           "some-pkg-guid",
   211  						State:          constant.PackageReady,
   212  						Relationships: resources.Relationships{
   213  							constant.RelationshipTypeApplication: resources.Relationship{
   214  								GUID: "some-app-guid",
   215  							},
   216  						},
   217  					}
   218  
   219  					fakeCloudControllerClient.CreatePackageReturns(
   220  						createdPackage,
   221  						ccv3.Warnings{"some-create-package-warning"},
   222  						nil,
   223  					)
   224  				})
   225  
   226  				It("calls CC to create the package and returns the package", func() {
   227  					Expect(executeErr).ToNot(HaveOccurred())
   228  					Expect(warnings).To(ConsistOf("some-app-warning", "some-create-package-warning"))
   229  
   230  					expectedPackage := ccv3.Package{
   231  						DockerImage:    "some-docker-image",
   232  						DockerUsername: "some-username",
   233  						DockerPassword: "some-password",
   234  						GUID:           "some-pkg-guid",
   235  						State:          constant.PackageReady,
   236  						Relationships: resources.Relationships{
   237  							constant.RelationshipTypeApplication: resources.Relationship{
   238  								GUID: "some-app-guid",
   239  							},
   240  						},
   241  					}
   242  					Expect(dockerPackage).To(Equal(Package(expectedPackage)))
   243  
   244  					Expect(fakeCloudControllerClient.GetApplicationsCallCount()).To(Equal(1))
   245  					Expect(fakeCloudControllerClient.GetApplicationsArgsForCall(0)).To(ConsistOf(
   246  						ccv3.Query{Key: ccv3.NameFilter, Values: []string{"some-app-name"}},
   247  						ccv3.Query{Key: ccv3.SpaceGUIDFilter, Values: []string{"some-space-guid"}},
   248  					))
   249  
   250  					Expect(fakeCloudControllerClient.CreatePackageCallCount()).To(Equal(1))
   251  					Expect(fakeCloudControllerClient.CreatePackageArgsForCall(0)).To(Equal(ccv3.Package{
   252  						Type:           constant.PackageTypeDocker,
   253  						DockerImage:    "some-docker-image",
   254  						DockerUsername: "some-username",
   255  						DockerPassword: "some-password",
   256  						Relationships: resources.Relationships{
   257  							constant.RelationshipTypeApplication: resources.Relationship{GUID: "some-app-guid"},
   258  						},
   259  					}))
   260  				})
   261  			})
   262  		})
   263  	})
   264  
   265  	Describe("CreateAndUploadBitsPackageByApplicationNameAndSpace", func() {
   266  		var (
   267  			bitsPath   string
   268  			pkg        Package
   269  			warnings   Warnings
   270  			executeErr error
   271  		)
   272  
   273  		BeforeEach(func() {
   274  			bitsPath = ""
   275  			pkg = Package{}
   276  			warnings = nil
   277  			executeErr = nil
   278  
   279  			// putting this here so the tests don't hang on polling
   280  			fakeCloudControllerClient.GetPackageReturns(
   281  				ccv3.Package{GUID: "some-pkg-guid", State: constant.PackageReady},
   282  				ccv3.Warnings{},
   283  				nil,
   284  			)
   285  		})
   286  
   287  		JustBeforeEach(func() {
   288  			pkg, warnings, executeErr = actor.CreateAndUploadBitsPackageByApplicationNameAndSpace("some-app-name", "some-space-guid", bitsPath)
   289  		})
   290  
   291  		When("retrieving the application errors", func() {
   292  			BeforeEach(func() {
   293  				fakeCloudControllerClient.GetApplicationsReturns(
   294  					[]resources.Application{},
   295  					ccv3.Warnings{"some-app-warning"},
   296  					errors.New("some-get-error"),
   297  				)
   298  			})
   299  
   300  			It("returns the warnings and the error", func() {
   301  				Expect(executeErr).To(MatchError("some-get-error"))
   302  				Expect(warnings).To(ConsistOf("some-app-warning"))
   303  			})
   304  		})
   305  
   306  		When("the application can be retrieved", func() {
   307  			BeforeEach(func() {
   308  				fakeCloudControllerClient.GetApplicationsReturns(
   309  					[]resources.Application{
   310  						{
   311  							Name: "some-app-name",
   312  							GUID: "some-app-guid",
   313  						},
   314  					},
   315  					ccv3.Warnings{"some-app-warning"},
   316  					nil,
   317  				)
   318  			})
   319  
   320  			When("bits path is a directory", func() {
   321  				BeforeEach(func() {
   322  					var err error
   323  					bitsPath, err = ioutil.TempDir("", "example")
   324  					Expect(err).ToNot(HaveOccurred())
   325  				})
   326  
   327  				AfterEach(func() {
   328  					if bitsPath != "" {
   329  						err := os.RemoveAll(bitsPath)
   330  						Expect(err).ToNot(HaveOccurred())
   331  					}
   332  				})
   333  
   334  				It("calls GatherDirectoryResources and ZipDirectoryResources", func() {
   335  					Expect(fakeSharedActor.GatherDirectoryResourcesCallCount()).To(Equal(1))
   336  					Expect(fakeSharedActor.ZipDirectoryResourcesCallCount()).To(Equal(1))
   337  				})
   338  
   339  				When("gathering resources fails", func() {
   340  					BeforeEach(func() {
   341  						fakeSharedActor.GatherDirectoryResourcesReturns(nil, errors.New("some-gather-error"))
   342  					})
   343  
   344  					It("returns the error", func() {
   345  						Expect(executeErr).To(MatchError("some-gather-error"))
   346  						Expect(warnings).To(ConsistOf("some-app-warning"))
   347  					})
   348  				})
   349  
   350  				When("gathering resources succeeds", func() {
   351  					BeforeEach(func() {
   352  						fakeSharedActor.GatherDirectoryResourcesReturns([]sharedaction.Resource{{Filename: "file-1"}, {Filename: "file-2"}}, nil)
   353  					})
   354  
   355  					When("zipping gathered resources fails", func() {
   356  						BeforeEach(func() {
   357  							fakeSharedActor.ZipDirectoryResourcesReturns("", errors.New("some-archive-error"))
   358  						})
   359  
   360  						It("returns the error", func() {
   361  							Expect(executeErr).To(MatchError("some-archive-error"))
   362  							Expect(warnings).To(ConsistOf("some-app-warning"))
   363  						})
   364  					})
   365  
   366  					When("zipping gathered resources succeeds", func() {
   367  						BeforeEach(func() {
   368  							fakeSharedActor.ZipDirectoryResourcesReturns("zipped-archive", nil)
   369  						})
   370  
   371  						When("creating the package fails", func() {
   372  							BeforeEach(func() {
   373  								fakeCloudControllerClient.CreatePackageReturns(
   374  									ccv3.Package{},
   375  									ccv3.Warnings{"create-package-warning"},
   376  									errors.New("some-create-error"),
   377  								)
   378  							})
   379  
   380  							It("returns the error", func() {
   381  								Expect(executeErr).To(MatchError("some-create-error"))
   382  								Expect(warnings).To(ConsistOf("some-app-warning", "create-package-warning"))
   383  							})
   384  						})
   385  
   386  						When("creating the package succeeds", func() {
   387  							var createdPackage ccv3.Package
   388  
   389  							BeforeEach(func() {
   390  								createdPackage = ccv3.Package{
   391  									GUID:  "some-pkg-guid",
   392  									State: constant.PackageAwaitingUpload,
   393  									Relationships: resources.Relationships{
   394  										constant.RelationshipTypeApplication: resources.Relationship{
   395  											GUID: "some-app-guid",
   396  										},
   397  									},
   398  								}
   399  
   400  								fakeCloudControllerClient.CreatePackageReturns(
   401  									createdPackage,
   402  									ccv3.Warnings{"some-package-warning"},
   403  									nil,
   404  								)
   405  							})
   406  
   407  							It("uploads the package with the path to the zip", func() {
   408  								Expect(fakeCloudControllerClient.UploadPackageCallCount()).To(Equal(1))
   409  								_, zippedArchive := fakeCloudControllerClient.UploadPackageArgsForCall(0)
   410  								Expect(zippedArchive).To(Equal("zipped-archive"))
   411  							})
   412  
   413  							When("uploading fails", func() {
   414  								BeforeEach(func() {
   415  									fakeCloudControllerClient.UploadPackageReturns(
   416  										ccv3.Package{},
   417  										ccv3.Warnings{"upload-package-warning"},
   418  										errors.New("some-error"),
   419  									)
   420  								})
   421  
   422  								It("returns the error", func() {
   423  									Expect(executeErr).To(MatchError("some-error"))
   424  									Expect(warnings).To(ConsistOf("some-app-warning", "some-package-warning", "upload-package-warning"))
   425  								})
   426  							})
   427  
   428  							When("uploading succeeds", func() {
   429  								BeforeEach(func() {
   430  									fakeCloudControllerClient.UploadPackageReturns(
   431  										ccv3.Package{},
   432  										ccv3.Warnings{"upload-package-warning"},
   433  										nil,
   434  									)
   435  								})
   436  
   437  								When("the polling errors", func() {
   438  									var expectedErr error
   439  
   440  									BeforeEach(func() {
   441  										expectedErr = errors.New("Fake error during polling")
   442  										fakeCloudControllerClient.GetPackageReturns(
   443  											ccv3.Package{},
   444  											ccv3.Warnings{"some-get-pkg-warning"},
   445  											expectedErr,
   446  										)
   447  									})
   448  
   449  									It("returns the error and warnings", func() {
   450  										Expect(executeErr).To(MatchError(expectedErr))
   451  										Expect(warnings).To(ConsistOf("some-app-warning", "some-package-warning", "upload-package-warning", "some-get-pkg-warning"))
   452  									})
   453  								})
   454  
   455  								When("the polling is successful", func() {
   456  									It("collects all warnings", func() {
   457  										Expect(executeErr).NotTo(HaveOccurred())
   458  										Expect(warnings).To(ConsistOf("some-app-warning", "some-package-warning", "upload-package-warning"))
   459  									})
   460  
   461  									It("successfully resolves the app name", func() {
   462  										Expect(executeErr).ToNot(HaveOccurred())
   463  
   464  										Expect(fakeCloudControllerClient.GetApplicationsCallCount()).To(Equal(1))
   465  										Expect(fakeCloudControllerClient.GetApplicationsArgsForCall(0)).To(ConsistOf(
   466  											ccv3.Query{Key: ccv3.NameFilter, Values: []string{"some-app-name"}},
   467  											ccv3.Query{Key: ccv3.SpaceGUIDFilter, Values: []string{"some-space-guid"}},
   468  										))
   469  									})
   470  
   471  									It("successfully creates the Package", func() {
   472  										Expect(executeErr).ToNot(HaveOccurred())
   473  
   474  										Expect(fakeCloudControllerClient.CreatePackageCallCount()).To(Equal(1))
   475  										inputPackage := fakeCloudControllerClient.CreatePackageArgsForCall(0)
   476  										Expect(inputPackage).To(Equal(ccv3.Package{
   477  											Type: constant.PackageTypeBits,
   478  											Relationships: resources.Relationships{
   479  												constant.RelationshipTypeApplication: resources.Relationship{GUID: "some-app-guid"},
   480  											},
   481  										}))
   482  									})
   483  
   484  									It("returns the package", func() {
   485  										Expect(executeErr).ToNot(HaveOccurred())
   486  
   487  										expectedPackage := ccv3.Package{
   488  											GUID:  "some-pkg-guid",
   489  											State: constant.PackageReady,
   490  										}
   491  										Expect(pkg).To(Equal(Package(expectedPackage)))
   492  
   493  										Expect(fakeCloudControllerClient.GetPackageCallCount()).To(Equal(1))
   494  										Expect(fakeCloudControllerClient.GetPackageArgsForCall(0)).To(Equal("some-pkg-guid"))
   495  									})
   496  
   497  									DescribeTable("polls until terminal state is reached",
   498  										func(finalState constant.PackageState, expectedErr error) {
   499  											fakeCloudControllerClient.GetPackageReturns(
   500  												ccv3.Package{GUID: "some-pkg-guid", State: constant.PackageAwaitingUpload},
   501  												ccv3.Warnings{"poll-package-warning"},
   502  												nil,
   503  											)
   504  											fakeCloudControllerClient.GetPackageReturnsOnCall(
   505  												2,
   506  												ccv3.Package{State: finalState},
   507  												ccv3.Warnings{"poll-package-warning"},
   508  												nil,
   509  											)
   510  
   511  											_, tableWarnings, err := actor.CreateAndUploadBitsPackageByApplicationNameAndSpace("some-app-name", "some-space-guid", bitsPath)
   512  
   513  											if expectedErr == nil {
   514  												Expect(err).ToNot(HaveOccurred())
   515  											} else {
   516  												Expect(err).To(MatchError(expectedErr))
   517  											}
   518  
   519  											Expect(tableWarnings).To(ConsistOf("some-app-warning", "some-package-warning", "upload-package-warning", "poll-package-warning", "poll-package-warning"))
   520  
   521  											// hacky, get packages is called an extry time cause the
   522  											// JustBeforeEach executes everything once as well
   523  											Expect(fakeCloudControllerClient.GetPackageCallCount()).To(Equal(3))
   524  											Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(3))
   525  										},
   526  
   527  										Entry("READY", constant.PackageReady, nil),
   528  										Entry("FAILED", constant.PackageFailed, actionerror.PackageProcessingFailedError{}),
   529  										Entry("EXPIRED", constant.PackageExpired, actionerror.PackageProcessingExpiredError{}),
   530  									)
   531  								})
   532  							})
   533  						})
   534  					})
   535  				})
   536  			})
   537  
   538  			When("bitsPath is blank", func() {
   539  				var oldCurrentDir, appDir string
   540  				BeforeEach(func() {
   541  					var err error
   542  					oldCurrentDir, err = os.Getwd()
   543  					Expect(err).NotTo(HaveOccurred())
   544  
   545  					appDir, err = ioutil.TempDir("", "example")
   546  					Expect(err).ToNot(HaveOccurred())
   547  
   548  					Expect(os.Chdir(appDir)).NotTo(HaveOccurred())
   549  					appDir, err = os.Getwd()
   550  					Expect(err).ToNot(HaveOccurred())
   551  				})
   552  
   553  				AfterEach(func() {
   554  					Expect(os.Chdir(oldCurrentDir)).NotTo(HaveOccurred())
   555  					err := os.RemoveAll(appDir)
   556  					Expect(err).ToNot(HaveOccurred())
   557  				})
   558  
   559  				It("uses the current working directory", func() {
   560  					Expect(executeErr).NotTo(HaveOccurred())
   561  
   562  					Expect(fakeSharedActor.GatherDirectoryResourcesCallCount()).To(Equal(1))
   563  					Expect(fakeSharedActor.GatherDirectoryResourcesArgsForCall(0)).To(Equal(appDir))
   564  
   565  					Expect(fakeSharedActor.ZipDirectoryResourcesCallCount()).To(Equal(1))
   566  					pathArg, _ := fakeSharedActor.ZipDirectoryResourcesArgsForCall(0)
   567  					Expect(pathArg).To(Equal(appDir))
   568  				})
   569  			})
   570  
   571  			When("bits path is an archive", func() {
   572  				BeforeEach(func() {
   573  					var err error
   574  					tempFile, err := ioutil.TempFile("", "bits-zip-test")
   575  					Expect(err).ToNot(HaveOccurred())
   576  					Expect(tempFile.Close()).To(Succeed())
   577  					tempFilePath := tempFile.Name()
   578  
   579  					bitsPathFile, err := ioutil.TempFile("", "example")
   580  					Expect(err).ToNot(HaveOccurred())
   581  					Expect(bitsPathFile.Close()).To(Succeed())
   582  					bitsPath = bitsPathFile.Name()
   583  
   584  					err = zipit(tempFilePath, bitsPath, "")
   585  					Expect(err).ToNot(HaveOccurred())
   586  					Expect(os.Remove(tempFilePath)).To(Succeed())
   587  				})
   588  
   589  				AfterEach(func() {
   590  					err := os.RemoveAll(bitsPath)
   591  					Expect(err).ToNot(HaveOccurred())
   592  				})
   593  
   594  				It("calls GatherArchiveResources and ZipArchiveResources", func() {
   595  					Expect(fakeSharedActor.GatherArchiveResourcesCallCount()).To(Equal(1))
   596  					Expect(fakeSharedActor.ZipArchiveResourcesCallCount()).To(Equal(1))
   597  				})
   598  
   599  				When("gathering archive resources fails", func() {
   600  					BeforeEach(func() {
   601  						fakeSharedActor.GatherArchiveResourcesReturns(nil, errors.New("some-archive-resource-error"))
   602  					})
   603  					It("should return an error", func() {
   604  						Expect(executeErr).To(MatchError("some-archive-resource-error"))
   605  						Expect(warnings).To(ConsistOf("some-app-warning"))
   606  					})
   607  
   608  				})
   609  
   610  				When("gathering resources succeeds", func() {
   611  					BeforeEach(func() {
   612  						fakeSharedActor.GatherArchiveResourcesReturns([]sharedaction.Resource{{Filename: "file-1"}, {Filename: "file-2"}}, nil)
   613  					})
   614  
   615  					When("zipping gathered resources fails", func() {
   616  						BeforeEach(func() {
   617  							fakeSharedActor.ZipArchiveResourcesReturns("", errors.New("some-archive-error"))
   618  						})
   619  
   620  						It("returns the error", func() {
   621  							Expect(executeErr).To(MatchError("some-archive-error"))
   622  							Expect(warnings).To(ConsistOf("some-app-warning"))
   623  						})
   624  					})
   625  
   626  					When("zipping gathered resources succeeds", func() {
   627  						BeforeEach(func() {
   628  							fakeSharedActor.ZipArchiveResourcesReturns("zipped-archive", nil)
   629  						})
   630  
   631  						It("uploads the package", func() {
   632  							Expect(executeErr).ToNot(HaveOccurred())
   633  							Expect(warnings).To(ConsistOf("some-app-warning"))
   634  
   635  							Expect(fakeCloudControllerClient.UploadPackageCallCount()).To(Equal(1))
   636  							_, archivePathArg := fakeCloudControllerClient.UploadPackageArgsForCall(0)
   637  							Expect(archivePathArg).To(Equal("zipped-archive"))
   638  						})
   639  					})
   640  				})
   641  			})
   642  
   643  			When("bits path is a symlink to a directory", func() {
   644  				var tempDir string
   645  
   646  				BeforeEach(func() {
   647  					var err error
   648  					tempDir, err = ioutil.TempDir("", "example")
   649  					Expect(err).ToNot(HaveOccurred())
   650  
   651  					tempFile, err := ioutil.TempFile("", "example-file-")
   652  					Expect(err).ToNot(HaveOccurred())
   653  					Expect(tempFile.Close()).To(Succeed())
   654  
   655  					bitsPath = tempFile.Name()
   656  					Expect(os.Remove(bitsPath)).To(Succeed())
   657  					Expect(os.Symlink(tempDir, bitsPath)).To(Succeed())
   658  				})
   659  
   660  				AfterEach(func() {
   661  					Expect(os.RemoveAll(tempDir)).To(Succeed())
   662  					Expect(os.Remove(bitsPath)).To(Succeed())
   663  				})
   664  
   665  				It("calls GatherDirectoryResources and returns without an error", func() {
   666  					Expect(fakeSharedActor.GatherDirectoryResourcesCallCount()).To(Equal(1))
   667  					Expect(fakeSharedActor.GatherDirectoryResourcesArgsForCall(0)).To(Equal(bitsPath))
   668  					Expect(executeErr).ToNot(HaveOccurred())
   669  				})
   670  			})
   671  
   672  			When("bits path is symlink to an archive", func() {
   673  				var archivePath string
   674  
   675  				BeforeEach(func() {
   676  					var err error
   677  					tempArchiveFile, err := ioutil.TempFile("", "bits-zip-test")
   678  					Expect(err).ToNot(HaveOccurred())
   679  					Expect(tempArchiveFile.Close()).To(Succeed())
   680  					tempArchiveFilePath := tempArchiveFile.Name()
   681  
   682  					archivePathFile, err := ioutil.TempFile("", "example")
   683  					Expect(err).ToNot(HaveOccurred())
   684  					Expect(archivePathFile.Close()).To(Succeed())
   685  					archivePath = archivePathFile.Name()
   686  
   687  					err = zipit(tempArchiveFilePath, archivePath, "")
   688  					Expect(err).ToNot(HaveOccurred())
   689  					Expect(os.Remove(tempArchiveFilePath)).To(Succeed())
   690  
   691  					tempFile, err := ioutil.TempFile("", "example-file-")
   692  					Expect(err).ToNot(HaveOccurred())
   693  					Expect(tempFile.Close()).To(Succeed())
   694  
   695  					bitsPath = tempFile.Name()
   696  					Expect(os.Remove(bitsPath)).To(Succeed())
   697  					Expect(os.Symlink(archivePath, bitsPath)).To(Succeed())
   698  				})
   699  
   700  				AfterEach(func() {
   701  					Expect(os.Remove(archivePath)).To(Succeed())
   702  					Expect(os.Remove(bitsPath)).To(Succeed())
   703  				})
   704  
   705  				It("calls GatherArchiveResources and returns without an error", func() {
   706  					Expect(fakeSharedActor.GatherArchiveResourcesCallCount()).To(Equal(1))
   707  					Expect(fakeSharedActor.GatherArchiveResourcesArgsForCall(0)).To(Equal(bitsPath))
   708  					Expect(executeErr).ToNot(HaveOccurred())
   709  				})
   710  			})
   711  		})
   712  	})
   713  
   714  	Describe("CreateBitsPackageByApplication", func() {
   715  		var (
   716  			appGUID string
   717  
   718  			pkg        Package
   719  			executeErr error
   720  			warnings   Warnings
   721  		)
   722  
   723  		JustBeforeEach(func() {
   724  			pkg, warnings, executeErr = actor.CreateBitsPackageByApplication(appGUID)
   725  		})
   726  
   727  		When("creating the package fails", func() {
   728  			BeforeEach(func() {
   729  				fakeCloudControllerClient.CreatePackageReturns(
   730  					ccv3.Package{},
   731  					ccv3.Warnings{"create-package-warning"},
   732  					errors.New("some-create-error"),
   733  				)
   734  			})
   735  
   736  			It("returns the error", func() {
   737  				Expect(executeErr).To(MatchError("some-create-error"))
   738  				Expect(warnings).To(ConsistOf("create-package-warning"))
   739  			})
   740  		})
   741  
   742  		When("creating the package succeeds", func() {
   743  			var createdPackage ccv3.Package
   744  
   745  			BeforeEach(func() {
   746  				createdPackage = ccv3.Package{GUID: "some-pkg-guid"}
   747  				fakeCloudControllerClient.CreatePackageReturns(
   748  					createdPackage,
   749  					ccv3.Warnings{"create-package-warning"},
   750  					nil,
   751  				)
   752  			})
   753  
   754  			It("returns all warnings and the package", func() {
   755  				Expect(executeErr).ToNot(HaveOccurred())
   756  
   757  				Expect(fakeCloudControllerClient.CreatePackageCallCount()).To(Equal(1))
   758  				Expect(fakeCloudControllerClient.CreatePackageArgsForCall(0)).To(Equal(ccv3.Package{
   759  					Type: constant.PackageTypeBits,
   760  					Relationships: resources.Relationships{
   761  						constant.RelationshipTypeApplication: resources.Relationship{GUID: appGUID},
   762  					},
   763  				}))
   764  
   765  				Expect(warnings).To(ConsistOf("create-package-warning"))
   766  				Expect(pkg).To(MatchFields(IgnoreExtras, Fields{
   767  					"GUID": Equal("some-pkg-guid"),
   768  				}))
   769  			})
   770  		})
   771  	})
   772  
   773  	Describe("UploadBitsPackage", func() {
   774  		var (
   775  			pkg              Package
   776  			matchedResources []sharedaction.Resource
   777  			reader           io.Reader
   778  			readerLength     int64
   779  
   780  			appPkg     Package
   781  			warnings   Warnings
   782  			executeErr error
   783  		)
   784  
   785  		BeforeEach(func() {
   786  			pkg = Package{GUID: "some-package-guid"}
   787  
   788  			matchedResources = []sharedaction.Resource{{Filename: "some-resource"}, {Filename: "another-resource"}}
   789  			someString := "who reads these days"
   790  			reader = strings.NewReader(someString)
   791  			readerLength = int64(len([]byte(someString)))
   792  		})
   793  
   794  		JustBeforeEach(func() {
   795  			appPkg, warnings, executeErr = actor.UploadBitsPackage(pkg, matchedResources, reader, readerLength)
   796  		})
   797  
   798  		When("the upload is successful", func() {
   799  			BeforeEach(func() {
   800  				fakeCloudControllerClient.UploadBitsPackageReturns(ccv3.Package{GUID: "some-package-guid"}, ccv3.Warnings{"upload-warning-1", "upload-warning-2"}, nil)
   801  			})
   802  
   803  			It("passes a ccv3 Resource to the client", func() {
   804  				passedPackage, passedMatchedResources, passedReader, passedReaderLength := fakeCloudControllerClient.UploadBitsPackageArgsForCall(0)
   805  				Expect(passedPackage).To(Equal(ccv3.Package(appPkg)))
   806  				Expect(passedMatchedResources).To(ConsistOf(ccv3.Resource{FilePath: "some-resource"}, ccv3.Resource{FilePath: "another-resource"}))
   807  				Expect(passedReader).To(Equal(reader))
   808  				Expect(passedReaderLength).To(Equal(readerLength))
   809  			})
   810  
   811  			It("returns all warnings", func() {
   812  				Expect(executeErr).ToNot(HaveOccurred())
   813  				Expect(warnings).To(ConsistOf("upload-warning-1", "upload-warning-2"))
   814  				Expect(appPkg).To(Equal(Package{GUID: "some-package-guid"}))
   815  
   816  				Expect(fakeCloudControllerClient.UploadBitsPackageCallCount()).To(Equal(1))
   817  
   818  			})
   819  		})
   820  
   821  		When("the upload returns an error", func() {
   822  			var err error
   823  
   824  			BeforeEach(func() {
   825  				err = errors.New("some-error")
   826  				fakeCloudControllerClient.UploadBitsPackageReturns(ccv3.Package{}, ccv3.Warnings{"upload-warning-1", "upload-warning-2"}, err)
   827  			})
   828  
   829  			It("returns the error", func() {
   830  				Expect(executeErr).To(MatchError(err))
   831  				Expect(warnings).To(ConsistOf("upload-warning-1", "upload-warning-2"))
   832  			})
   833  		})
   834  	})
   835  
   836  	Describe("PollPackage", func() {
   837  		Context("Polling Behavior", func() {
   838  			var (
   839  				pkg Package
   840  
   841  				appPkg     Package
   842  				warnings   Warnings
   843  				executeErr error
   844  			)
   845  
   846  			BeforeEach(func() {
   847  				pkg = Package{
   848  					GUID: "some-pkg-guid",
   849  				}
   850  
   851  				warnings = nil
   852  				executeErr = nil
   853  
   854  				// putting this here so the tests don't hang on polling
   855  				fakeCloudControllerClient.GetPackageReturns(
   856  					ccv3.Package{
   857  						GUID:  "some-pkg-guid",
   858  						State: constant.PackageReady,
   859  					},
   860  					ccv3.Warnings{},
   861  					nil,
   862  				)
   863  			})
   864  
   865  			JustBeforeEach(func() {
   866  				appPkg, warnings, executeErr = actor.PollPackage(pkg)
   867  			})
   868  
   869  			When("the polling errors", func() {
   870  				var expectedErr error
   871  
   872  				BeforeEach(func() {
   873  					expectedErr = errors.New("Fake error during polling")
   874  					fakeCloudControllerClient.GetPackageReturns(
   875  						ccv3.Package{},
   876  						ccv3.Warnings{"some-get-pkg-warning"},
   877  						expectedErr,
   878  					)
   879  				})
   880  
   881  				It("returns the error and warnings", func() {
   882  					Expect(executeErr).To(MatchError(expectedErr))
   883  					Expect(warnings).To(ConsistOf("some-get-pkg-warning"))
   884  				})
   885  			})
   886  
   887  			When("the polling is successful", func() {
   888  				It("returns the package", func() {
   889  					Expect(executeErr).ToNot(HaveOccurred())
   890  
   891  					expectedPackage := ccv3.Package{
   892  						GUID:  "some-pkg-guid",
   893  						State: constant.PackageReady,
   894  					}
   895  
   896  					Expect(appPkg).To(Equal(Package(expectedPackage)))
   897  					Expect(fakeCloudControllerClient.GetPackageCallCount()).To(Equal(1))
   898  					Expect(fakeCloudControllerClient.GetPackageArgsForCall(0)).To(Equal("some-pkg-guid"))
   899  				})
   900  			})
   901  		})
   902  
   903  		DescribeTable("Polling states",
   904  			func(finalState constant.PackageState, expectedErr error) {
   905  				fakeCloudControllerClient.GetPackageReturns(
   906  					ccv3.Package{GUID: "some-pkg-guid", State: constant.PackageAwaitingUpload},
   907  					ccv3.Warnings{"poll-package-warning"},
   908  					nil,
   909  				)
   910  
   911  				fakeCloudControllerClient.GetPackageReturnsOnCall(
   912  					1,
   913  					ccv3.Package{State: finalState},
   914  					ccv3.Warnings{"poll-package-warning"},
   915  					nil,
   916  				)
   917  
   918  				_, tableWarnings, err := actor.PollPackage(Package{
   919  					GUID: "some-pkg-guid",
   920  				})
   921  
   922  				if expectedErr == nil {
   923  					Expect(err).ToNot(HaveOccurred())
   924  				} else {
   925  					Expect(err).To(MatchError(expectedErr))
   926  				}
   927  
   928  				Expect(tableWarnings).To(ConsistOf("poll-package-warning", "poll-package-warning"))
   929  
   930  				Expect(fakeCloudControllerClient.GetPackageCallCount()).To(Equal(2))
   931  				Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(2))
   932  			},
   933  
   934  			Entry("READY", constant.PackageReady, nil),
   935  			Entry("FAILED", constant.PackageFailed, actionerror.PackageProcessingFailedError{}),
   936  			Entry("EXPIRED", constant.PackageExpired, actionerror.PackageProcessingExpiredError{}),
   937  		)
   938  	})
   939  })