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

     1  package v7action_test
     2  
     3  import (
     4  	"errors"
     5  	"time"
     6  
     7  	"code.cloudfoundry.org/cli/actor/actionerror"
     8  	. "code.cloudfoundry.org/cli/actor/v7action"
     9  	"code.cloudfoundry.org/cli/actor/v7action/v7actionfakes"
    10  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3"
    11  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
    12  	"code.cloudfoundry.org/cli/resources"
    13  	"code.cloudfoundry.org/clock"
    14  	. "github.com/onsi/ginkgo"
    15  	. "github.com/onsi/gomega"
    16  )
    17  
    18  var _ = Describe("Build Actions", func() {
    19  	var (
    20  		actor                     *Actor
    21  		fakeCloudControllerClient *v7actionfakes.FakeCloudControllerClient
    22  		fakeConfig                *v7actionfakes.FakeConfig
    23  	)
    24  
    25  	BeforeEach(func() {
    26  		fakeCloudControllerClient = new(v7actionfakes.FakeCloudControllerClient)
    27  		fakeConfig = new(v7actionfakes.FakeConfig)
    28  		actor = NewActor(fakeCloudControllerClient, fakeConfig, nil, nil, nil, clock.NewClock())
    29  	})
    30  
    31  	Describe("StagePackage", func() {
    32  		var (
    33  			dropletStream  <-chan resources.Droplet
    34  			warningsStream <-chan Warnings
    35  			errorStream    <-chan error
    36  
    37  			appName     string
    38  			appGUID     string
    39  			buildGUID   string
    40  			dropletGUID string
    41  			spaceGUID   string
    42  			packageGUID string
    43  		)
    44  
    45  		BeforeEach(func() {
    46  			appName = "some-app"
    47  			appGUID = "app-guid"
    48  			spaceGUID = "space-guid"
    49  			packageGUID = "some-package-guid"
    50  		})
    51  
    52  		AfterEach(func() {
    53  			Eventually(errorStream).Should(BeClosed())
    54  			Eventually(warningsStream).Should(BeClosed())
    55  			Eventually(dropletStream).Should(BeClosed())
    56  		})
    57  
    58  		JustBeforeEach(func() {
    59  			dropletStream, warningsStream, errorStream = actor.StagePackage(packageGUID, appName, spaceGUID)
    60  		})
    61  
    62  		When("finding the app fails", func() {
    63  			var expectedErr error
    64  
    65  			BeforeEach(func() {
    66  				expectedErr = errors.New("I am a tomato")
    67  				fakeCloudControllerClient.GetApplicationsReturns(nil, ccv3.Warnings{"get-apps-warning"}, expectedErr)
    68  			})
    69  
    70  			It("returns the error and warnings", func() {
    71  				Eventually(warningsStream).Should(Receive(ConsistOf("get-apps-warning")))
    72  				Eventually(errorStream).Should(Receive(MatchError(expectedErr)))
    73  			})
    74  		})
    75  
    76  		When("app is not found", func() {
    77  			BeforeEach(func() {
    78  				fakeCloudControllerClient.GetApplicationsReturns([]resources.Application{}, ccv3.Warnings{"get-apps-warning"}, nil)
    79  			})
    80  
    81  			It("returns the error and warnings", func() {
    82  				Eventually(warningsStream).Should(Receive(ConsistOf("get-apps-warning")))
    83  				Eventually(errorStream).Should(Receive(MatchError(actionerror.ApplicationNotFoundError{Name: appName})))
    84  			})
    85  		})
    86  
    87  		When("finding the package fails", func() {
    88  			var expectedErr error
    89  
    90  			BeforeEach(func() {
    91  				expectedErr = errors.New("I am a passion fruit")
    92  				fakeCloudControllerClient.GetApplicationsReturns([]resources.Application{{GUID: appGUID}}, ccv3.Warnings{"get-apps-warning"}, nil)
    93  				fakeCloudControllerClient.GetPackagesReturns([]ccv3.Package{}, ccv3.Warnings{"get-packages-warning"}, expectedErr)
    94  			})
    95  
    96  			It("returns the error and warnings", func() {
    97  				Eventually(warningsStream).Should(Receive(ConsistOf("get-apps-warning")))
    98  				Eventually(warningsStream).Should(Receive(ConsistOf("get-packages-warning")))
    99  				Eventually(errorStream).Should(Receive(MatchError(expectedErr)))
   100  			})
   101  		})
   102  
   103  		When("the package does not belong to the app", func() {
   104  			BeforeEach(func() {
   105  				fakeCloudControllerClient.GetApplicationsReturns([]resources.Application{{GUID: appGUID}}, ccv3.Warnings{"get-apps-warning"}, nil)
   106  				fakeCloudControllerClient.GetPackagesReturns(
   107  					[]ccv3.Package{{GUID: "some-other-package-guid"}},
   108  					ccv3.Warnings{"get-packages-warning"},
   109  					nil,
   110  				)
   111  			})
   112  
   113  			It("returns a not found error and warnings", func() {
   114  				Eventually(warningsStream).Should(Receive(ConsistOf("get-apps-warning")))
   115  				Eventually(warningsStream).Should(Receive(ConsistOf("get-packages-warning")))
   116  				Eventually(errorStream).Should(Receive(MatchError(actionerror.PackageNotFoundInAppError{GUID: packageGUID, AppName: appName})))
   117  			})
   118  		})
   119  
   120  		When("the creation is successful", func() {
   121  			BeforeEach(func() {
   122  				fakeCloudControllerClient.GetApplicationsReturns([]resources.Application{{GUID: appGUID}}, ccv3.Warnings{"get-apps-warning"}, nil)
   123  				fakeCloudControllerClient.GetPackagesReturns(
   124  					[]ccv3.Package{{GUID: packageGUID}},
   125  					ccv3.Warnings{"get-packages-warning"},
   126  					nil,
   127  				)
   128  
   129  				buildGUID = "some-build-guid"
   130  				dropletGUID = "some-droplet-guid"
   131  				fakeCloudControllerClient.CreateBuildReturns(ccv3.Build{GUID: buildGUID, State: constant.BuildStaging}, ccv3.Warnings{"create-warnings-1", "create-warnings-2"}, nil)
   132  				fakeConfig.StagingTimeoutReturns(time.Minute)
   133  			})
   134  
   135  			When("the polling is successful", func() {
   136  				BeforeEach(func() {
   137  					fakeCloudControllerClient.GetBuildReturnsOnCall(0, ccv3.Build{GUID: buildGUID, State: constant.BuildStaging}, ccv3.Warnings{"get-warnings-1", "get-warnings-2"}, nil)
   138  					fakeCloudControllerClient.GetBuildReturnsOnCall(1, ccv3.Build{CreatedAt: "some-time", GUID: buildGUID, State: constant.BuildStaged, DropletGUID: "some-droplet-guid"}, ccv3.Warnings{"get-warnings-3", "get-warnings-4"}, nil)
   139  				})
   140  
   141  				//TODO: uncommend after #150569020
   142  				// FWhen("looking up the droplet fails", func() {
   143  				// 	BeforeEach(func() {
   144  				// 		fakeCloudControllerClient.GetDropletReturns(resources.Droplet{}, ccv3.Warnings{"droplet-warnings-1", "droplet-warnings-2"}, errors.New("some-droplet-error"))
   145  				// 	})
   146  
   147  				// 	It("returns the warnings and the droplet error", func() {
   148  				//    Eventually(warningsStream).Should(Receive(ConsistOf("get-apps-warning")))
   149  				//    Eventually(warningsStream).Should(Receive(ConsistOf("get-packages-warning")))
   150  				// 		Eventually(warningsStream).Should(Receive(ConsistOf("create-warnings-1", "create-warnings-2")))
   151  				// 		Eventually(warningsStream).Should(Receive(ConsistOf("get-warnings-1", "get-warnings-2")))
   152  				// 		Eventually(warningsStream).Should(Receive(ConsistOf("get-warnings-3", "get-warnings-4")))
   153  				// 		Eventually(warningsStream).Should(Receive(ConsistOf("droplet-warnings-1", "droplet-warnings-2")))
   154  
   155  				// 		Eventually(errorStream).Should(Receive(MatchError("some-droplet-error")))
   156  				// 	})
   157  				// })
   158  
   159  				// When("looking up the droplet succeeds", func() {
   160  				// 	BeforeEach(func() {
   161  				// 		fakeCloudControllerClient.GetDropletReturns(resources.Droplet{GUID: dropletGUID, State: ccv3.DropletStateStaged}, ccv3.Warnings{"droplet-warnings-1", "droplet-warnings-2"}, nil)
   162  				// 	})
   163  
   164  				It("polls until build is finished and returns the final droplet", func() {
   165  					Eventually(warningsStream).Should(Receive(ConsistOf("get-apps-warning")))
   166  					Eventually(warningsStream).Should(Receive(ConsistOf("get-packages-warning")))
   167  					Eventually(warningsStream).Should(Receive(ConsistOf("create-warnings-1", "create-warnings-2")))
   168  					Eventually(warningsStream).Should(Receive(ConsistOf("get-warnings-1", "get-warnings-2")))
   169  					Eventually(warningsStream).Should(Receive(ConsistOf("get-warnings-3", "get-warnings-4")))
   170  					// Eventually(warningsStream).Should(Receive(ConsistOf("droplet-warnings-1", "droplet-warnings-2")))
   171  
   172  					Eventually(dropletStream).Should(Receive(Equal(resources.Droplet{GUID: dropletGUID, State: constant.DropletStaged, CreatedAt: "some-time"})))
   173  					Consistently(errorStream).ShouldNot(Receive())
   174  
   175  					Expect(fakeCloudControllerClient.GetApplicationsCallCount()).To(Equal(1))
   176  					Expect(fakeCloudControllerClient.GetApplicationsArgsForCall(0)).To(Equal([]ccv3.Query{
   177  						{Key: ccv3.NameFilter, Values: []string{appName}},
   178  						{Key: ccv3.SpaceGUIDFilter, Values: []string{spaceGUID}},
   179  					}))
   180  
   181  					Expect(fakeCloudControllerClient.GetPackagesCallCount()).To(Equal(1))
   182  					Expect(fakeCloudControllerClient.GetPackagesArgsForCall(0)).To(Equal([]ccv3.Query{
   183  						{Key: ccv3.AppGUIDFilter, Values: []string{appGUID}},
   184  					}))
   185  
   186  					Expect(fakeCloudControllerClient.CreateBuildCallCount()).To(Equal(1))
   187  					Expect(fakeCloudControllerClient.CreateBuildArgsForCall(0)).To(Equal(ccv3.Build{
   188  						PackageGUID: "some-package-guid",
   189  					}))
   190  
   191  					Expect(fakeCloudControllerClient.GetBuildCallCount()).To(Equal(2))
   192  					Expect(fakeCloudControllerClient.GetBuildArgsForCall(0)).To(Equal(buildGUID))
   193  					Expect(fakeCloudControllerClient.GetBuildArgsForCall(1)).To(Equal(buildGUID))
   194  
   195  					Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(1))
   196  				})
   197  				// })
   198  
   199  				When("polling returns a failed build", func() {
   200  					BeforeEach(func() {
   201  						fakeCloudControllerClient.GetBuildReturnsOnCall(
   202  							1,
   203  							ccv3.Build{
   204  								GUID:  buildGUID,
   205  								State: constant.BuildFailed,
   206  								Error: "some staging error",
   207  							},
   208  							ccv3.Warnings{"get-warnings-3", "get-warnings-4"}, nil)
   209  					})
   210  
   211  					It("returns an error and all warnings", func() {
   212  						Eventually(warningsStream).Should(Receive(ConsistOf("get-apps-warning")))
   213  						Eventually(warningsStream).Should(Receive(ConsistOf("get-packages-warning")))
   214  						Eventually(warningsStream).Should(Receive(ConsistOf("create-warnings-1", "create-warnings-2")))
   215  						Eventually(warningsStream).Should(Receive(ConsistOf("get-warnings-1", "get-warnings-2")))
   216  						Eventually(warningsStream).Should(Receive(ConsistOf("get-warnings-3", "get-warnings-4")))
   217  						stagingErr := errors.New("some staging error")
   218  						Eventually(errorStream).Should(Receive(&stagingErr))
   219  						Eventually(dropletStream).ShouldNot(Receive())
   220  
   221  						Expect(fakeCloudControllerClient.GetBuildCallCount()).To(Equal(2))
   222  						Expect(fakeCloudControllerClient.GetBuildArgsForCall(0)).To(Equal(buildGUID))
   223  						Expect(fakeCloudControllerClient.GetBuildArgsForCall(1)).To(Equal(buildGUID))
   224  
   225  						Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(1))
   226  					})
   227  				})
   228  			})
   229  
   230  			When("polling times out", func() {
   231  				var expectedErr error
   232  
   233  				BeforeEach(func() {
   234  					expectedErr = actionerror.StagingTimeoutError{AppName: "some-app", Timeout: 0}
   235  					fakeConfig.StagingTimeoutReturns(0)
   236  				})
   237  
   238  				It("returns the error and warnings", func() {
   239  					Eventually(warningsStream).Should(Receive(ConsistOf("create-warnings-1", "create-warnings-2")))
   240  					Eventually(errorStream).Should(Receive(MatchError(expectedErr)))
   241  				})
   242  			})
   243  
   244  			When("the polling errors", func() {
   245  				var expectedErr error
   246  
   247  				BeforeEach(func() {
   248  					expectedErr = errors.New("I am a banana")
   249  					fakeCloudControllerClient.GetBuildReturnsOnCall(0, ccv3.Build{GUID: buildGUID, State: constant.BuildStaging}, ccv3.Warnings{"get-warnings-1", "get-warnings-2"}, nil)
   250  					fakeCloudControllerClient.GetBuildReturnsOnCall(1, ccv3.Build{}, ccv3.Warnings{"get-warnings-3", "get-warnings-4"}, expectedErr)
   251  				})
   252  
   253  				It("returns the error and warnings", func() {
   254  					Eventually(warningsStream).Should(Receive(ConsistOf("get-apps-warning")))
   255  					Eventually(warningsStream).Should(Receive(ConsistOf("get-packages-warning")))
   256  					Eventually(warningsStream).Should(Receive(ConsistOf("create-warnings-1", "create-warnings-2")))
   257  					Eventually(warningsStream).Should(Receive(ConsistOf("get-warnings-1", "get-warnings-2")))
   258  					Eventually(warningsStream).Should(Receive(ConsistOf("get-warnings-3", "get-warnings-4")))
   259  					Eventually(errorStream).Should(Receive(MatchError(expectedErr)))
   260  				})
   261  			})
   262  		})
   263  
   264  		When("creation errors", func() {
   265  			var expectedErr error
   266  
   267  			BeforeEach(func() {
   268  				fakeCloudControllerClient.GetApplicationsReturns([]resources.Application{{GUID: appGUID}}, ccv3.Warnings{"get-apps-warning"}, nil)
   269  				fakeCloudControllerClient.GetPackagesReturns(
   270  					[]ccv3.Package{{GUID: packageGUID}},
   271  					ccv3.Warnings{"get-packages-warning"},
   272  					nil,
   273  				)
   274  
   275  				expectedErr = errors.New("I am a banana")
   276  				fakeCloudControllerClient.CreateBuildReturns(ccv3.Build{}, ccv3.Warnings{"create-warnings-1", "create-warnings-2"}, expectedErr)
   277  			})
   278  
   279  			It("returns the error and warnings", func() {
   280  				Eventually(warningsStream).Should(Receive(ConsistOf("get-apps-warning")))
   281  				Eventually(warningsStream).Should(Receive(ConsistOf("get-packages-warning")))
   282  				Eventually(warningsStream).Should(Receive(ConsistOf("create-warnings-1", "create-warnings-2")))
   283  				Eventually(errorStream).Should(Receive(MatchError(expectedErr)))
   284  			})
   285  		})
   286  	})
   287  
   288  	Describe("StageApplicationPackage", func() {
   289  		var (
   290  			build      Build
   291  			warnings   Warnings
   292  			executeErr error
   293  		)
   294  
   295  		JustBeforeEach(func() {
   296  			build, warnings, executeErr = actor.StageApplicationPackage("some-package-guid")
   297  		})
   298  
   299  		When("the creation is successful", func() {
   300  			BeforeEach(func() {
   301  				fakeCloudControllerClient.CreateBuildReturns(ccv3.Build{GUID: "some-build-guid"}, ccv3.Warnings{"create-warnings-1", "create-warnings-2"}, nil)
   302  			})
   303  
   304  			It("returns the build and warnings", func() {
   305  				Expect(executeErr).ToNot(HaveOccurred())
   306  				Expect(build).To(Equal(Build{GUID: "some-build-guid"}))
   307  				Expect(warnings).To(ConsistOf("create-warnings-1", "create-warnings-2"))
   308  			})
   309  		})
   310  
   311  		When("the creation fails", func() {
   312  			BeforeEach(func() {
   313  				fakeCloudControllerClient.CreateBuildReturns(ccv3.Build{}, ccv3.Warnings{"create-warnings-1", "create-warnings-2"}, errors.New("blurp"))
   314  			})
   315  
   316  			It("returns the error and warnings", func() {
   317  				Expect(executeErr).To(MatchError("blurp"))
   318  				Expect(warnings).To(ConsistOf("create-warnings-1", "create-warnings-2"))
   319  			})
   320  		})
   321  	})
   322  
   323  	Describe("PollBuild", func() {
   324  		var (
   325  			droplet    resources.Droplet
   326  			warnings   Warnings
   327  			executeErr error
   328  		)
   329  
   330  		JustBeforeEach(func() {
   331  			droplet, warnings, executeErr = actor.PollBuild("some-build-guid", "some-app")
   332  		})
   333  
   334  		When("getting the build yields a 'Staged' build", func() {
   335  			BeforeEach(func() {
   336  				fakeCloudControllerClient.GetBuildReturnsOnCall(0, ccv3.Build{State: constant.BuildStaging}, ccv3.Warnings{"some-get-build-warnings"}, nil)
   337  				fakeCloudControllerClient.GetBuildReturnsOnCall(1, ccv3.Build{GUID: "some-build-guid", DropletGUID: "some-droplet-guid", State: constant.BuildStaged}, ccv3.Warnings{"some-get-build-warnings"}, nil)
   338  				fakeConfig.StagingTimeoutReturns(500 * time.Millisecond)
   339  			})
   340  
   341  			It("gets the droplet", func() {
   342  				Expect(fakeCloudControllerClient.GetBuildCallCount()).To(Equal(2))
   343  
   344  				Expect(fakeCloudControllerClient.GetDropletCallCount()).To(Equal(1))
   345  				Expect(fakeCloudControllerClient.GetDropletArgsForCall(0)).To(Equal("some-droplet-guid"))
   346  			})
   347  
   348  			When("getting the droplet is successful", func() {
   349  				BeforeEach(func() {
   350  					fakeCloudControllerClient.GetDropletReturns(resources.Droplet{GUID: "some-droplet-guid", CreatedAt: "some-droplet-time", State: constant.DropletStaged}, ccv3.Warnings{"some-get-droplet-warnings"}, nil)
   351  				})
   352  
   353  				It("returns the droplet and warnings", func() {
   354  					Expect(executeErr).ToNot(HaveOccurred())
   355  
   356  					Expect(warnings).To(ConsistOf("some-get-build-warnings", "some-get-build-warnings", "some-get-droplet-warnings"))
   357  					Expect(droplet).To(Equal(resources.Droplet{
   358  						GUID:      "some-droplet-guid",
   359  						CreatedAt: "some-droplet-time",
   360  						State:     constant.DropletStaged,
   361  					}))
   362  				})
   363  			})
   364  
   365  			When("getting the droplet fails", func() {
   366  				BeforeEach(func() {
   367  					fakeCloudControllerClient.GetDropletReturns(resources.Droplet{}, ccv3.Warnings{"some-get-droplet-warnings"}, errors.New("no rain"))
   368  				})
   369  
   370  				It("returns the error and warnings", func() {
   371  					Expect(executeErr).To(MatchError("no rain"))
   372  					Expect(warnings).To(ConsistOf("some-get-build-warnings", "some-get-build-warnings", "some-get-droplet-warnings"))
   373  				})
   374  			})
   375  		})
   376  
   377  		When("getting the build yields a 'Failed' build", func() {
   378  			BeforeEach(func() {
   379  				fakeCloudControllerClient.GetBuildReturnsOnCall(0, ccv3.Build{State: constant.BuildFailed, Error: "ded build"}, ccv3.Warnings{"some-get-build-warnings"}, nil)
   380  				fakeConfig.StagingTimeoutReturns(500 * time.Millisecond)
   381  			})
   382  
   383  			It("returns the error and warnings", func() {
   384  				Expect(executeErr).To(MatchError("ded build"))
   385  				Expect(warnings).To(ConsistOf("some-get-build-warnings"))
   386  			})
   387  		})
   388  
   389  		When("getting the build fails", func() {
   390  			BeforeEach(func() {
   391  				fakeCloudControllerClient.GetBuildReturnsOnCall(0, ccv3.Build{}, ccv3.Warnings{"some-get-build-warnings"}, errors.New("some-poll-build-error"))
   392  				fakeConfig.StagingTimeoutReturns(500 * time.Millisecond)
   393  			})
   394  
   395  			It("returns the error and warnings", func() {
   396  				Expect(executeErr).To(MatchError("some-poll-build-error"))
   397  				Expect(warnings).To(ConsistOf("some-get-build-warnings"))
   398  			})
   399  		})
   400  
   401  		When("polling the build times out", func() {
   402  			BeforeEach(func() {
   403  				fakeCloudControllerClient.GetBuildReturnsOnCall(0, ccv3.Build{}, ccv3.Warnings{"some-get-build-warnings"}, nil)
   404  				fakeConfig.StagingTimeoutReturns(500 * time.Millisecond)
   405  			})
   406  
   407  			It("returns the error and warnings", func() {
   408  				Expect(executeErr).To(MatchError(actionerror.StagingTimeoutError{AppName: "some-app", Timeout: 500 * time.Millisecond}))
   409  				Expect(warnings).To(ConsistOf("some-get-build-warnings"))
   410  			})
   411  		})
   412  	})
   413  })