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