github.com/lukasheimann/cloudfoundrycli@v7.1.0+incompatible/actor/v7action/application_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/ccerror"
    11  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3"
    12  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
    13  	"code.cloudfoundry.org/cli/resources"
    14  	"code.cloudfoundry.org/cli/types"
    15  	"code.cloudfoundry.org/clock/fakeclock"
    16  
    17  	. "github.com/onsi/ginkgo"
    18  	. "github.com/onsi/gomega"
    19  )
    20  
    21  var _ = Describe("Application Actions", func() {
    22  	var (
    23  		actor                     *Actor
    24  		fakeCloudControllerClient *v7actionfakes.FakeCloudControllerClient
    25  		fakeConfig                *v7actionfakes.FakeConfig
    26  		fakeClock                 *fakeclock.FakeClock
    27  	)
    28  
    29  	BeforeEach(func() {
    30  		fakeCloudControllerClient = new(v7actionfakes.FakeCloudControllerClient)
    31  		fakeConfig = new(v7actionfakes.FakeConfig)
    32  		fakeClock = fakeclock.NewFakeClock(time.Now())
    33  		actor = NewActor(fakeCloudControllerClient, fakeConfig, nil, nil, nil, fakeClock)
    34  	})
    35  
    36  	Describe("DeleteApplicationByNameAndSpace", func() {
    37  		var (
    38  			warnings           Warnings
    39  			executeErr         error
    40  			deleteMappedRoutes bool
    41  			appName            string
    42  		)
    43  
    44  		JustBeforeEach(func() {
    45  			appName = "some-app"
    46  			warnings, executeErr = actor.DeleteApplicationByNameAndSpace(appName, "some-space-guid", deleteMappedRoutes)
    47  		})
    48  
    49  		When("looking up the app guid fails", func() {
    50  			BeforeEach(func() {
    51  				fakeCloudControllerClient.GetApplicationsReturns([]resources.Application{}, ccv3.Warnings{"some-get-app-warning"}, errors.New("some-get-app-error"))
    52  			})
    53  
    54  			It("returns the warnings and error", func() {
    55  				Expect(warnings).To(ConsistOf("some-get-app-warning"))
    56  				Expect(executeErr).To(MatchError("some-get-app-error"))
    57  			})
    58  		})
    59  
    60  		When("looking up the app guid succeeds without routes", func() {
    61  			BeforeEach(func() {
    62  				fakeCloudControllerClient.GetApplicationsReturns([]resources.Application{{Name: "some-app", GUID: "abc123"}}, ccv3.Warnings{"some-get-app-warning"}, nil)
    63  				deleteMappedRoutes = false
    64  			})
    65  
    66  			When("sending the delete fails", func() {
    67  				BeforeEach(func() {
    68  					fakeCloudControllerClient.DeleteApplicationReturns("", ccv3.Warnings{"some-delete-app-warning"}, errors.New("some-delete-app-error"))
    69  				})
    70  
    71  				It("returns the warnings and error", func() {
    72  					Expect(warnings).To(ConsistOf("some-get-app-warning", "some-delete-app-warning"))
    73  					Expect(executeErr).To(MatchError("some-delete-app-error"))
    74  				})
    75  			})
    76  
    77  			When("sending the delete succeeds", func() {
    78  				BeforeEach(func() {
    79  					fakeCloudControllerClient.DeleteApplicationReturns("/some-job-url", ccv3.Warnings{"some-delete-app-warning"}, nil)
    80  				})
    81  
    82  				When("polling fails", func() {
    83  					BeforeEach(func() {
    84  						fakeCloudControllerClient.PollJobReturns(ccv3.Warnings{"some-poll-warning"}, errors.New("some-poll-error"))
    85  					})
    86  
    87  					It("returns the warnings and poll error", func() {
    88  						Expect(warnings).To(ConsistOf("some-get-app-warning", "some-delete-app-warning", "some-poll-warning"))
    89  						Expect(executeErr).To(MatchError("some-poll-error"))
    90  					})
    91  				})
    92  
    93  				When("polling succeeds", func() {
    94  					BeforeEach(func() {
    95  						fakeCloudControllerClient.PollJobReturns(ccv3.Warnings{"some-poll-warning"}, nil)
    96  					})
    97  
    98  					It("returns all the warnings and no error", func() {
    99  						Expect(warnings).To(ConsistOf("some-get-app-warning", "some-delete-app-warning", "some-poll-warning"))
   100  						Expect(executeErr).ToNot(HaveOccurred())
   101  					})
   102  				})
   103  			})
   104  		})
   105  
   106  		When("looking up the app guid succeeds with routes", func() {
   107  			BeforeEach(func() {
   108  				deleteMappedRoutes = true
   109  				fakeCloudControllerClient.GetApplicationsReturns([]resources.Application{{Name: "some-app", GUID: "abc123"}}, nil, nil)
   110  			})
   111  
   112  			When("getting the routes fails", func() {
   113  				BeforeEach(func() {
   114  					fakeCloudControllerClient.GetApplicationRoutesReturns(nil, ccv3.Warnings{"get-routes-warning"}, errors.New("get-routes-error"))
   115  				})
   116  
   117  				It("returns the warnings and an error", func() {
   118  					Expect(warnings).To(ConsistOf("get-routes-warning"))
   119  					Expect(executeErr).To(MatchError("get-routes-error"))
   120  				})
   121  			})
   122  
   123  			When("getting the routes succeeds", func() {
   124  				When("there are no routes", func() {
   125  					BeforeEach(func() {
   126  						fakeCloudControllerClient.GetApplicationRoutesReturns([]resources.Route{}, nil, nil)
   127  					})
   128  
   129  					It("does not delete any routes", func() {
   130  						Expect(fakeCloudControllerClient.DeleteRouteCallCount()).To(Equal(0))
   131  					})
   132  				})
   133  
   134  				When("there are routes", func() {
   135  					BeforeEach(func() {
   136  						fakeCloudControllerClient.GetApplicationRoutesReturns([]resources.Route{{GUID: "route-1-guid"}, {GUID: "route-2-guid", URL: "route-2.example.com"}}, nil, nil)
   137  					})
   138  
   139  					It("deletes the routes", func() {
   140  						Expect(fakeCloudControllerClient.GetApplicationRoutesCallCount()).To(Equal(1))
   141  						Expect(fakeCloudControllerClient.GetApplicationRoutesArgsForCall(0)).To(Equal("abc123"))
   142  						Expect(fakeCloudControllerClient.DeleteRouteCallCount()).To(Equal(2))
   143  						guids := []string{fakeCloudControllerClient.DeleteRouteArgsForCall(0), fakeCloudControllerClient.DeleteRouteArgsForCall(1)}
   144  						Expect(guids).To(ConsistOf("route-1-guid", "route-2-guid"))
   145  					})
   146  
   147  					When("the route has already been deleted", func() {
   148  						BeforeEach(func() {
   149  							fakeCloudControllerClient.DeleteRouteReturnsOnCall(0,
   150  								"",
   151  								ccv3.Warnings{"delete-route-1-warning"},
   152  								ccerror.ResourceNotFoundError{},
   153  							)
   154  							fakeCloudControllerClient.DeleteRouteReturnsOnCall(1,
   155  								"poll-job-url",
   156  								ccv3.Warnings{"delete-route-2-warning"},
   157  								nil,
   158  							)
   159  							fakeCloudControllerClient.PollJobReturnsOnCall(1, ccv3.Warnings{"poll-job-warning"}, nil)
   160  						})
   161  
   162  						It("does **not** fail", func() {
   163  							Expect(executeErr).ToNot(HaveOccurred())
   164  							Expect(warnings).To(ConsistOf("delete-route-1-warning", "delete-route-2-warning", "poll-job-warning"))
   165  							Expect(fakeCloudControllerClient.DeleteRouteCallCount()).To(Equal(2))
   166  							Expect(fakeCloudControllerClient.PollJobCallCount()).To(Equal(2))
   167  							Expect(fakeCloudControllerClient.PollJobArgsForCall(1)).To(BeEquivalentTo("poll-job-url"))
   168  						})
   169  					})
   170  
   171  					When("app to delete has a route bound to another app", func() {
   172  						BeforeEach(func() {
   173  							fakeCloudControllerClient.GetApplicationRoutesReturns(
   174  								[]resources.Route{
   175  									{GUID: "route-1-guid"},
   176  									{GUID: "route-2-guid",
   177  										URL: "route-2.example.com",
   178  										Destinations: []resources.RouteDestination{
   179  											{App: resources.RouteDestinationApp{GUID: "abc123"}},
   180  											{App: resources.RouteDestinationApp{GUID: "different-app-guid"}},
   181  										},
   182  									},
   183  								},
   184  								nil,
   185  								nil,
   186  							)
   187  						})
   188  
   189  						It("refuses the entire operation", func() {
   190  							Expect(executeErr).To(MatchError(actionerror.RouteBoundToMultipleAppsError{AppName: "some-app", RouteURL: "route-2.example.com"}))
   191  							Expect(warnings).To(BeEmpty())
   192  							Expect(fakeCloudControllerClient.DeleteApplicationCallCount()).To(Equal(0))
   193  							Expect(fakeCloudControllerClient.DeleteRouteCallCount()).To(Equal(0))
   194  						})
   195  					})
   196  
   197  					When("deleting the route fails", func() {
   198  						BeforeEach(func() {
   199  							fakeCloudControllerClient.DeleteRouteReturnsOnCall(0,
   200  								"poll-job-url",
   201  								ccv3.Warnings{"delete-route-1-warning"},
   202  								nil,
   203  							)
   204  							fakeCloudControllerClient.DeleteRouteReturnsOnCall(1,
   205  								"",
   206  								ccv3.Warnings{"delete-route-2-warning"},
   207  								errors.New("delete-route-2-error"),
   208  							)
   209  						})
   210  
   211  						It("returns the error", func() {
   212  							Expect(executeErr).To(MatchError("delete-route-2-error"))
   213  							Expect(warnings).To(ConsistOf("delete-route-1-warning", "delete-route-2-warning"))
   214  						})
   215  					})
   216  
   217  					When("the polling job fails", func() {
   218  						BeforeEach(func() {
   219  							fakeCloudControllerClient.PollJobReturns(ccv3.Warnings{"poll-job-warning"}, errors.New("poll-job-error"))
   220  						})
   221  
   222  						It("returns the error", func() {
   223  							Expect(executeErr).To(MatchError("poll-job-error"))
   224  						})
   225  					})
   226  
   227  				})
   228  			})
   229  		})
   230  	})
   231  
   232  	Describe("GetApplicationsByGUIDs", func() {
   233  		When("all of the requested apps exist", func() {
   234  			BeforeEach(func() {
   235  				fakeCloudControllerClient.GetApplicationsReturns(
   236  					[]resources.Application{
   237  						{
   238  							Name: "some-app-name",
   239  							GUID: "some-app-guid",
   240  						},
   241  						{
   242  							Name: "other-app-name",
   243  							GUID: "other-app-guid",
   244  						},
   245  					},
   246  					ccv3.Warnings{"some-warning"},
   247  					nil,
   248  				)
   249  			})
   250  
   251  			It("returns the applications and warnings", func() {
   252  				apps, warnings, err := actor.GetApplicationsByGUIDs([]string{"some-app-guid", "other-app-guid"})
   253  				Expect(err).ToNot(HaveOccurred())
   254  				Expect(apps).To(ConsistOf(
   255  					resources.Application{
   256  						Name: "some-app-name",
   257  						GUID: "some-app-guid",
   258  					},
   259  					resources.Application{
   260  						Name: "other-app-name",
   261  						GUID: "other-app-guid",
   262  					},
   263  				))
   264  				Expect(warnings).To(ConsistOf("some-warning"))
   265  
   266  				Expect(fakeCloudControllerClient.GetApplicationsCallCount()).To(Equal(1))
   267  				Expect(fakeCloudControllerClient.GetApplicationsArgsForCall(0)).To(ConsistOf(
   268  					ccv3.Query{Key: ccv3.GUIDFilter, Values: []string{"some-app-guid", "other-app-guid"}},
   269  				))
   270  			})
   271  		})
   272  
   273  		When("at least one of the requested apps does not exist", func() {
   274  			BeforeEach(func() {
   275  				fakeCloudControllerClient.GetApplicationsReturns(
   276  					[]resources.Application{
   277  						{
   278  							Name: "some-app-name",
   279  							GUID: "some-app-guid",
   280  						},
   281  					},
   282  					ccv3.Warnings{"some-warning"},
   283  					nil,
   284  				)
   285  			})
   286  
   287  			It("returns an ApplicationNotFoundError and the warnings", func() {
   288  				_, warnings, err := actor.GetApplicationsByGUIDs([]string{"some-app-guid", "non-existent-app-guid"})
   289  				Expect(warnings).To(ConsistOf("some-warning"))
   290  				Expect(err).To(MatchError(actionerror.ApplicationsNotFoundError{}))
   291  			})
   292  		})
   293  
   294  		When("a single app has two routes", func() {
   295  			BeforeEach(func() {
   296  				fakeCloudControllerClient.GetApplicationsReturns(
   297  					[]resources.Application{
   298  						{
   299  							Name: "some-app-name",
   300  							GUID: "some-app-guid",
   301  						},
   302  					},
   303  					ccv3.Warnings{"some-warning"},
   304  					nil,
   305  				)
   306  			})
   307  
   308  			It("returns an ApplicationNotFoundError and the warnings", func() {
   309  				_, warnings, err := actor.GetApplicationsByGUIDs([]string{"some-app-guid", "some-app-guid"})
   310  				Expect(err).ToNot(HaveOccurred())
   311  				Expect(warnings).To(ConsistOf("some-warning"))
   312  			})
   313  		})
   314  
   315  		When("the cloud controller client returns an error", func() {
   316  			var expectedError error
   317  
   318  			BeforeEach(func() {
   319  				expectedError = errors.New("I am a CloudControllerClient Error")
   320  				fakeCloudControllerClient.GetApplicationsReturns(
   321  					[]resources.Application{},
   322  					ccv3.Warnings{"some-warning"},
   323  					expectedError)
   324  			})
   325  
   326  			It("returns the warnings and the error", func() {
   327  				_, warnings, err := actor.GetApplicationsByGUIDs([]string{"some-app-guid"})
   328  				Expect(warnings).To(ConsistOf("some-warning"))
   329  				Expect(err).To(MatchError(expectedError))
   330  			})
   331  		})
   332  	})
   333  
   334  	Describe("GetApplicationsByNameAndSpace", func() {
   335  		When("all of the requested apps exist", func() {
   336  			BeforeEach(func() {
   337  				fakeCloudControllerClient.GetApplicationsReturns(
   338  					[]resources.Application{
   339  						{
   340  							Name: "some-app-name",
   341  							GUID: "some-app-guid",
   342  						},
   343  						{
   344  							Name: "other-app-name",
   345  							GUID: "other-app-guid",
   346  						},
   347  					},
   348  					ccv3.Warnings{"some-warning"},
   349  					nil,
   350  				)
   351  			})
   352  
   353  			It("returns the applications and warnings", func() {
   354  				apps, warnings, err := actor.GetApplicationsByNamesAndSpace([]string{"some-app-name", "other-app-name"}, "some-space-guid")
   355  				Expect(err).ToNot(HaveOccurred())
   356  				Expect(apps).To(ConsistOf(
   357  					resources.Application{
   358  						Name: "some-app-name",
   359  						GUID: "some-app-guid",
   360  					},
   361  					resources.Application{
   362  						Name: "other-app-name",
   363  						GUID: "other-app-guid",
   364  					},
   365  				))
   366  				Expect(warnings).To(ConsistOf("some-warning"))
   367  
   368  				Expect(fakeCloudControllerClient.GetApplicationsCallCount()).To(Equal(1))
   369  				Expect(fakeCloudControllerClient.GetApplicationsArgsForCall(0)).To(ConsistOf(
   370  					ccv3.Query{Key: ccv3.NameFilter, Values: []string{"some-app-name", "other-app-name"}},
   371  					ccv3.Query{Key: ccv3.SpaceGUIDFilter, Values: []string{"some-space-guid"}},
   372  				))
   373  			})
   374  		})
   375  
   376  		When("at least one of the requested apps does not exist", func() {
   377  			BeforeEach(func() {
   378  				fakeCloudControllerClient.GetApplicationsReturns(
   379  					[]resources.Application{
   380  						{
   381  							Name: "some-app-name",
   382  						},
   383  					},
   384  					ccv3.Warnings{"some-warning"},
   385  					nil,
   386  				)
   387  			})
   388  
   389  			It("returns an ApplicationNotFoundError and the warnings", func() {
   390  				_, warnings, err := actor.GetApplicationsByNamesAndSpace([]string{"some-app-name", "other-app-name"}, "some-space-guid")
   391  				Expect(warnings).To(ConsistOf("some-warning"))
   392  				Expect(err).To(MatchError(actionerror.ApplicationsNotFoundError{}))
   393  			})
   394  		})
   395  
   396  		When("a given app has two routes", func() {
   397  			BeforeEach(func() {
   398  				fakeCloudControllerClient.GetApplicationsReturns(
   399  					[]resources.Application{
   400  						{
   401  							Name: "some-app-name",
   402  						},
   403  					},
   404  					ccv3.Warnings{"some-warning"},
   405  					nil,
   406  				)
   407  			})
   408  
   409  			It("returns an ApplicationNotFoundError and the warnings", func() {
   410  				_, warnings, err := actor.GetApplicationsByNamesAndSpace([]string{"some-app-name", "some-app-name"}, "some-space-guid")
   411  				Expect(err).ToNot(HaveOccurred())
   412  				Expect(warnings).To(ConsistOf("some-warning"))
   413  			})
   414  		})
   415  
   416  		When("the cloud controller client returns an error", func() {
   417  			var expectedError error
   418  
   419  			BeforeEach(func() {
   420  				expectedError = errors.New("I am a CloudControllerClient Error")
   421  				fakeCloudControllerClient.GetApplicationsReturns(
   422  					[]resources.Application{},
   423  					ccv3.Warnings{"some-warning"},
   424  					expectedError)
   425  			})
   426  
   427  			It("returns the warnings and the error", func() {
   428  				_, warnings, err := actor.GetApplicationsByNamesAndSpace([]string{"some-app-name"}, "some-space-guid")
   429  				Expect(warnings).To(ConsistOf("some-warning"))
   430  				Expect(err).To(MatchError(expectedError))
   431  			})
   432  		})
   433  	})
   434  
   435  	Describe("GetApplicationByNameAndSpace", func() {
   436  		When("the app exists", func() {
   437  			BeforeEach(func() {
   438  				fakeCloudControllerClient.GetApplicationsReturns(
   439  					[]resources.Application{
   440  						{
   441  							Name: "some-app-name",
   442  							GUID: "some-app-guid",
   443  							Metadata: &resources.Metadata{
   444  								Labels: map[string]types.NullString{
   445  									"some-key": types.NewNullString("some-value"),
   446  								},
   447  							},
   448  						},
   449  					},
   450  					ccv3.Warnings{"some-warning"},
   451  					nil,
   452  				)
   453  			})
   454  
   455  			It("returns the application and warnings", func() {
   456  				app, warnings, err := actor.GetApplicationByNameAndSpace("some-app-name", "some-space-guid")
   457  				Expect(err).ToNot(HaveOccurred())
   458  				Expect(app).To(Equal(resources.Application{
   459  					Name: "some-app-name",
   460  					GUID: "some-app-guid",
   461  					Metadata: &resources.Metadata{
   462  						Labels: map[string]types.NullString{"some-key": types.NewNullString("some-value")},
   463  					},
   464  				}))
   465  				Expect(warnings).To(ConsistOf("some-warning"))
   466  
   467  				Expect(fakeCloudControllerClient.GetApplicationsCallCount()).To(Equal(1))
   468  				Expect(fakeCloudControllerClient.GetApplicationsArgsForCall(0)).To(ConsistOf(
   469  					ccv3.Query{Key: ccv3.NameFilter, Values: []string{"some-app-name"}},
   470  					ccv3.Query{Key: ccv3.SpaceGUIDFilter, Values: []string{"some-space-guid"}},
   471  				))
   472  			})
   473  		})
   474  
   475  		When("the cloud controller client returns an error", func() {
   476  			var expectedError error
   477  
   478  			BeforeEach(func() {
   479  				expectedError = errors.New("I am a CloudControllerClient Error")
   480  				fakeCloudControllerClient.GetApplicationsReturns(
   481  					[]resources.Application{},
   482  					ccv3.Warnings{"some-warning"},
   483  					expectedError)
   484  			})
   485  
   486  			It("returns the warnings and the error", func() {
   487  				_, warnings, err := actor.GetApplicationByNameAndSpace("some-app-name", "some-space-guid")
   488  				Expect(warnings).To(ConsistOf("some-warning"))
   489  				Expect(err).To(MatchError(expectedError))
   490  			})
   491  		})
   492  
   493  		When("the app does not exist", func() {
   494  			BeforeEach(func() {
   495  				fakeCloudControllerClient.GetApplicationsReturns(
   496  					[]resources.Application{},
   497  					ccv3.Warnings{"some-warning"},
   498  					nil,
   499  				)
   500  			})
   501  
   502  			It("returns an ApplicationNotFoundError and the warnings", func() {
   503  				_, warnings, err := actor.GetApplicationByNameAndSpace("some-app-name", "some-space-guid")
   504  				Expect(warnings).To(ConsistOf("some-warning"))
   505  				Expect(err).To(MatchError(actionerror.ApplicationNotFoundError{Name: "some-app-name"}))
   506  			})
   507  		})
   508  	})
   509  
   510  	Describe("GetApplicationsBySpace", func() {
   511  		When("the there are applications in the space", func() {
   512  			BeforeEach(func() {
   513  				fakeCloudControllerClient.GetApplicationsReturns(
   514  					[]resources.Application{
   515  						{
   516  							GUID: "some-app-guid-1",
   517  							Name: "some-app-1",
   518  						},
   519  						{
   520  							GUID: "some-app-guid-2",
   521  							Name: "some-app-2",
   522  						},
   523  					},
   524  					ccv3.Warnings{"warning-1", "warning-2"},
   525  					nil,
   526  				)
   527  			})
   528  
   529  			It("returns the application and warnings", func() {
   530  				apps, warnings, err := actor.GetApplicationsBySpace("some-space-guid")
   531  				Expect(err).ToNot(HaveOccurred())
   532  				Expect(apps).To(ConsistOf(
   533  					resources.Application{
   534  						GUID: "some-app-guid-1",
   535  						Name: "some-app-1",
   536  					},
   537  					resources.Application{
   538  						GUID: "some-app-guid-2",
   539  						Name: "some-app-2",
   540  					},
   541  				))
   542  				Expect(warnings).To(ConsistOf("warning-1", "warning-2"))
   543  
   544  				Expect(fakeCloudControllerClient.GetApplicationsCallCount()).To(Equal(1))
   545  				Expect(fakeCloudControllerClient.GetApplicationsArgsForCall(0)).To(ConsistOf(
   546  					ccv3.Query{Key: ccv3.SpaceGUIDFilter, Values: []string{"some-space-guid"}},
   547  				))
   548  			})
   549  		})
   550  
   551  		When("the cloud controller client returns an error", func() {
   552  			var expectedError error
   553  
   554  			BeforeEach(func() {
   555  				expectedError = errors.New("I am a CloudControllerClient Error")
   556  				fakeCloudControllerClient.GetApplicationsReturns(
   557  					[]resources.Application{},
   558  					ccv3.Warnings{"some-warning"},
   559  					expectedError)
   560  			})
   561  
   562  			It("returns the error and warnings", func() {
   563  				_, warnings, err := actor.GetApplicationsBySpace("some-space-guid")
   564  				Expect(warnings).To(ConsistOf("some-warning"))
   565  				Expect(err).To(MatchError(expectedError))
   566  			})
   567  		})
   568  	})
   569  
   570  	Describe("CreateApplicationInSpace", func() {
   571  		var (
   572  			application resources.Application
   573  			warnings    Warnings
   574  			err         error
   575  		)
   576  
   577  		JustBeforeEach(func() {
   578  			application, warnings, err = actor.CreateApplicationInSpace(resources.Application{
   579  				Name:                "some-app-name",
   580  				LifecycleType:       constant.AppLifecycleTypeBuildpack,
   581  				LifecycleBuildpacks: []string{"buildpack-1", "buildpack-2"},
   582  			}, "some-space-guid")
   583  		})
   584  
   585  		When("the app successfully gets created", func() {
   586  			BeforeEach(func() {
   587  				fakeCloudControllerClient.CreateApplicationReturns(
   588  					resources.Application{
   589  						Name:                "some-app-name",
   590  						GUID:                "some-app-guid",
   591  						LifecycleType:       constant.AppLifecycleTypeBuildpack,
   592  						LifecycleBuildpacks: []string{"buildpack-1", "buildpack-2"},
   593  					},
   594  					ccv3.Warnings{"some-warning"},
   595  					nil,
   596  				)
   597  			})
   598  
   599  			It("creates and returns the application and warnings", func() {
   600  				Expect(err).ToNot(HaveOccurred())
   601  				Expect(application).To(Equal(resources.Application{
   602  					Name:                "some-app-name",
   603  					GUID:                "some-app-guid",
   604  					LifecycleType:       constant.AppLifecycleTypeBuildpack,
   605  					LifecycleBuildpacks: []string{"buildpack-1", "buildpack-2"},
   606  				}))
   607  				Expect(warnings).To(ConsistOf("some-warning"))
   608  
   609  				Expect(fakeCloudControllerClient.CreateApplicationCallCount()).To(Equal(1))
   610  				Expect(fakeCloudControllerClient.CreateApplicationArgsForCall(0)).To(Equal(resources.Application{
   611  					Name:                "some-app-name",
   612  					SpaceGUID:           "some-space-guid",
   613  					LifecycleType:       constant.AppLifecycleTypeBuildpack,
   614  					LifecycleBuildpacks: []string{"buildpack-1", "buildpack-2"},
   615  				}))
   616  			})
   617  		})
   618  
   619  		When("the cc client returns an error", func() {
   620  			var expectedError error
   621  
   622  			BeforeEach(func() {
   623  				expectedError = errors.New("I am a CloudControllerClient Error")
   624  				fakeCloudControllerClient.CreateApplicationReturns(
   625  					resources.Application{},
   626  					ccv3.Warnings{"some-warning"},
   627  					expectedError,
   628  				)
   629  			})
   630  
   631  			It("raises the error and warnings", func() {
   632  				Expect(err).To(MatchError(expectedError))
   633  				Expect(warnings).To(ConsistOf("some-warning"))
   634  			})
   635  		})
   636  
   637  		When("the cc client returns an NameNotUniqueInSpaceError", func() {
   638  			BeforeEach(func() {
   639  				fakeCloudControllerClient.CreateApplicationReturns(
   640  					resources.Application{},
   641  					ccv3.Warnings{"some-warning"},
   642  					ccerror.NameNotUniqueInSpaceError{},
   643  				)
   644  			})
   645  
   646  			It("returns the NameNotUniqueInSpaceError and warnings", func() {
   647  				Expect(err).To(MatchError(ccerror.NameNotUniqueInSpaceError{}))
   648  				Expect(warnings).To(ConsistOf("some-warning"))
   649  			})
   650  		})
   651  	})
   652  
   653  	Describe("UpdateApplication", func() {
   654  		var (
   655  			submitApp, resultApp resources.Application
   656  			warnings             Warnings
   657  			err                  error
   658  		)
   659  
   660  		JustBeforeEach(func() {
   661  			submitApp = resources.Application{
   662  				GUID:                "some-app-guid",
   663  				StackName:           "some-stack-name",
   664  				Name:                "some-app-name",
   665  				LifecycleType:       constant.AppLifecycleTypeBuildpack,
   666  				LifecycleBuildpacks: []string{"buildpack-1", "buildpack-2"},
   667  				Metadata: &resources.Metadata{Labels: map[string]types.NullString{
   668  					"some-label":  types.NewNullString("some-value"),
   669  					"other-label": types.NewNullString("other-value"),
   670  				}},
   671  			}
   672  
   673  			resultApp, warnings, err = actor.UpdateApplication(submitApp)
   674  		})
   675  
   676  		When("the app successfully gets updated", func() {
   677  			var apiResponseApp resources.Application
   678  
   679  			BeforeEach(func() {
   680  				apiResponseApp = resources.Application{
   681  					GUID:                "response-app-guid",
   682  					StackName:           "response-stack-name",
   683  					Name:                "response-app-name",
   684  					LifecycleType:       constant.AppLifecycleTypeBuildpack,
   685  					LifecycleBuildpacks: []string{"response-buildpack-1", "response-buildpack-2"},
   686  				}
   687  				fakeCloudControllerClient.UpdateApplicationReturns(
   688  					apiResponseApp,
   689  					ccv3.Warnings{"some-warning"},
   690  					nil,
   691  				)
   692  			})
   693  
   694  			It("creates and returns the application and warnings", func() {
   695  				Expect(err).ToNot(HaveOccurred())
   696  				Expect(resultApp).To(Equal(resources.Application{
   697  					Name:                apiResponseApp.Name,
   698  					GUID:                apiResponseApp.GUID,
   699  					StackName:           apiResponseApp.StackName,
   700  					LifecycleType:       apiResponseApp.LifecycleType,
   701  					LifecycleBuildpacks: apiResponseApp.LifecycleBuildpacks,
   702  				}))
   703  				Expect(warnings).To(ConsistOf("some-warning"))
   704  
   705  				Expect(fakeCloudControllerClient.UpdateApplicationCallCount()).To(Equal(1))
   706  				Expect(fakeCloudControllerClient.UpdateApplicationArgsForCall(0)).To(Equal(resources.Application{
   707  					GUID:                submitApp.GUID,
   708  					StackName:           submitApp.StackName,
   709  					LifecycleType:       submitApp.LifecycleType,
   710  					LifecycleBuildpacks: submitApp.LifecycleBuildpacks,
   711  					Name:                submitApp.Name,
   712  					Metadata:            submitApp.Metadata,
   713  				}))
   714  			})
   715  		})
   716  
   717  		When("the cc client returns an error", func() {
   718  			var expectedError error
   719  
   720  			BeforeEach(func() {
   721  				expectedError = errors.New("I am a CloudControllerClient Error")
   722  				fakeCloudControllerClient.UpdateApplicationReturns(
   723  					resources.Application{},
   724  					ccv3.Warnings{"some-warning"},
   725  					expectedError,
   726  				)
   727  			})
   728  
   729  			It("raises the error and warnings", func() {
   730  				Expect(err).To(MatchError(expectedError))
   731  				Expect(warnings).To(ConsistOf("some-warning"))
   732  			})
   733  		})
   734  	})
   735  
   736  	Describe("PollStart", func() {
   737  		var (
   738  			app                   resources.Application
   739  			noWait                bool
   740  			handleInstanceDetails func(string)
   741  
   742  			done chan bool
   743  
   744  			warnings                Warnings
   745  			executeErr              error
   746  			reportedInstanceDetails []string
   747  		)
   748  
   749  		BeforeEach(func() {
   750  			done = make(chan bool)
   751  			fakeConfig.StartupTimeoutReturns(2 * time.Second)
   752  			fakeConfig.PollingIntervalReturns(1 * time.Second)
   753  			app = resources.Application{GUID: "some-guid"}
   754  			noWait = false
   755  
   756  			reportedInstanceDetails = []string{}
   757  			handleInstanceDetails = func(instanceDetails string) {
   758  				reportedInstanceDetails = append(reportedInstanceDetails, instanceDetails)
   759  			}
   760  		})
   761  
   762  		JustBeforeEach(func() {
   763  			go func() {
   764  				defer close(done)
   765  				warnings, executeErr = actor.PollStart(app, noWait, handleInstanceDetails)
   766  				done <- true
   767  			}()
   768  		})
   769  
   770  		It("gets the apps processes", func() {
   771  			// advanced clock so function exits
   772  			fakeClock.WaitForNWatchersAndIncrement(1*time.Millisecond, 2)
   773  
   774  			// wait for function to finish
   775  			Eventually(done).Should(Receive(BeTrue()))
   776  
   777  			Expect(fakeCloudControllerClient.GetApplicationProcessesCallCount()).To(Equal(1))
   778  			Expect(fakeCloudControllerClient.GetApplicationProcessesArgsForCall(0)).To(Equal("some-guid"))
   779  
   780  		})
   781  
   782  		When("getting the application processes fails", func() {
   783  			BeforeEach(func() {
   784  				fakeCloudControllerClient.GetApplicationProcessesReturns(nil, ccv3.Warnings{"get-app-warning-1", "get-app-warning-2"}, errors.New("some-error"))
   785  			})
   786  
   787  			It("returns the error and all warnings", func() {
   788  				// wait for function to finish
   789  				Eventually(done).Should(Receive(BeTrue()))
   790  
   791  				Expect(executeErr).To(MatchError(errors.New("some-error")))
   792  				Expect(warnings).To(ConsistOf("get-app-warning-1", "get-app-warning-2"))
   793  			})
   794  		})
   795  
   796  		When("getting the application process succeeds", func() {
   797  			BeforeEach(func() {
   798  				fakeCloudControllerClient.GetApplicationProcessesReturns(
   799  					[]ccv3.Process{
   800  						{GUID: "process1", Type: "web"},
   801  					},
   802  					ccv3.Warnings{"get-app-warning-1"},
   803  					nil,
   804  				)
   805  
   806  			})
   807  
   808  			It("gets the startup timeout", func() {
   809  				// advanced clock so function exits
   810  				fakeClock.WaitForNWatchersAndIncrement(1*time.Millisecond, 2)
   811  
   812  				// wait for function to finish
   813  				Eventually(done).Should(Receive(BeTrue()))
   814  
   815  				Expect(fakeConfig.StartupTimeoutCallCount()).To(Equal(1))
   816  			})
   817  
   818  			When("the no-wait flag is provided", func() {
   819  				BeforeEach(func() {
   820  					noWait = true
   821  					fakeCloudControllerClient.GetApplicationProcessesReturns(
   822  						[]ccv3.Process{
   823  							{GUID: "process1", Type: "web"},
   824  							{GUID: "process2", Type: "worker"},
   825  						},
   826  						ccv3.Warnings{"get-app-warning-1"},
   827  						nil,
   828  					)
   829  				})
   830  
   831  				It("filters out the non web processes", func() {
   832  					// send something on the timer channel
   833  					fakeClock.WaitForNWatchersAndIncrement(1*time.Millisecond, 2)
   834  
   835  					// Wait for function to finish
   836  					Eventually(done).Should(Receive(BeTrue()))
   837  
   838  					// assert on the cc call made within poll processes to make sure there is only the web process
   839  					Expect(fakeCloudControllerClient.GetProcessInstancesCallCount()).To(Equal(1))
   840  					Expect(fakeCloudControllerClient.GetProcessInstancesArgsForCall(0)).To(Equal("process1"))
   841  
   842  				})
   843  			})
   844  
   845  			When("polling processes returns an error", func() {
   846  				BeforeEach(func() {
   847  					fakeCloudControllerClient.GetProcessInstancesReturns(nil, ccv3.Warnings{"poll-process-warning"}, errors.New("poll-process-error"))
   848  				})
   849  
   850  				It("returns the error and warnings", func() {
   851  					// send something on the timer channel
   852  					fakeClock.WaitForNWatchersAndIncrement(1*time.Millisecond, 2)
   853  
   854  					// Wait for function to finish
   855  					Eventually(done).Should(Receive(BeTrue()))
   856  
   857  					Expect(executeErr).Should(MatchError("poll-process-error"))
   858  					Expect(warnings).Should(ConsistOf("poll-process-warning", "get-app-warning-1"))
   859  				})
   860  			})
   861  
   862  			When("polling start times out", func() {
   863  				BeforeEach(func() {
   864  					fakeCloudControllerClient.GetProcessInstancesReturns(
   865  						[]ccv3.ProcessInstance{
   866  							{State: constant.ProcessInstanceStarting},
   867  						},
   868  						ccv3.Warnings{"poll-process-warning"},
   869  						nil,
   870  					)
   871  
   872  					fakeConfig.StartupTimeoutReturns(2 * time.Millisecond)
   873  				})
   874  
   875  				It("returns a timeout error and any warnings", func() {
   876  					// send something on the timer channel for first tick
   877  					fakeClock.WaitForNWatchersAndIncrement(1*time.Millisecond, 2)
   878  
   879  					fakeClock.Increment(1 * time.Millisecond)
   880  
   881  					// Wait for function to finish
   882  					Eventually(done).Should(Receive(BeTrue()))
   883  
   884  					Expect(executeErr).To(MatchError(actionerror.StartupTimeoutError{}))
   885  					Expect(warnings).To(ConsistOf("poll-process-warning", "get-app-warning-1"))
   886  				})
   887  			})
   888  
   889  			When("polling process eventually returns we should stop polling", func() {
   890  				BeforeEach(func() {
   891  					fakeCloudControllerClient.GetProcessInstancesReturnsOnCall(0,
   892  						[]ccv3.ProcessInstance{
   893  							{State: constant.ProcessInstanceStarting},
   894  						},
   895  						ccv3.Warnings{"poll-process-warning1"},
   896  						nil,
   897  					)
   898  
   899  					fakeCloudControllerClient.GetProcessInstancesReturnsOnCall(1,
   900  						[]ccv3.ProcessInstance{
   901  							{State: constant.ProcessInstanceRunning},
   902  						},
   903  						ccv3.Warnings{"poll-process-warning2"},
   904  						nil,
   905  					)
   906  				})
   907  
   908  				It("returns success and any warnings", func() {
   909  					// send something on the timer channel
   910  					fakeClock.WaitForNWatchersAndIncrement(1*time.Millisecond, 2)
   911  
   912  					Eventually(fakeConfig.PollingIntervalCallCount).Should(Equal(1))
   913  
   914  					fakeClock.Increment(1 * time.Second)
   915  
   916  					// Wait for function to finish
   917  					Eventually(done).Should(Receive(BeTrue()))
   918  					Expect(executeErr).NotTo(HaveOccurred())
   919  					Expect(warnings).To(ConsistOf("poll-process-warning1", "get-app-warning-1", "poll-process-warning2"))
   920  				})
   921  
   922  			})
   923  		})
   924  	})
   925  
   926  	Describe("PollStartForRolling", func() {
   927  		var (
   928  			app                   resources.Application
   929  			deploymentGUID        string
   930  			noWait                bool
   931  			handleInstanceDetails func(string)
   932  
   933  			done chan bool
   934  
   935  			warnings                Warnings
   936  			executeErr              error
   937  			reportedInstanceDetails []string
   938  		)
   939  
   940  		BeforeEach(func() {
   941  			reportedInstanceDetails = []string{}
   942  			handleInstanceDetails = func(instanceDetails string) {
   943  				reportedInstanceDetails = append(reportedInstanceDetails, instanceDetails)
   944  			}
   945  
   946  			app = resources.Application{GUID: "some-rolling-app-guid"}
   947  			deploymentGUID = "some-deployment-guid"
   948  			noWait = false
   949  
   950  			done = make(chan bool)
   951  
   952  			fakeConfig.StartupTimeoutReturns(5 * time.Second)
   953  			fakeConfig.PollingIntervalReturns(1 * time.Second)
   954  		})
   955  
   956  		JustBeforeEach(func() {
   957  			go func() {
   958  				warnings, executeErr = actor.PollStartForRolling(app, deploymentGUID, noWait, handleInstanceDetails)
   959  				done <- true
   960  			}()
   961  		})
   962  
   963  		When("There is a non-timeout failure in the loop", func() {
   964  			// this may need to be expanded to also include when the deployment is superseded or cancelled
   965  			When("getting the deployment fails", func() {
   966  				When("it is because the deployment was cancelled", func() {
   967  					BeforeEach(func() {
   968  						fakeCloudControllerClient.GetDeploymentReturns(
   969  							ccv3.Deployment{
   970  								StatusValue:  constant.DeploymentStatusValueFinalized,
   971  								StatusReason: constant.DeploymentStatusReasonCanceled,
   972  							},
   973  							ccv3.Warnings{"get-deployment-warning"},
   974  							nil,
   975  						)
   976  					})
   977  
   978  					It("returns warnings and the error", func() {
   979  						// initial tick
   980  						fakeClock.WaitForNWatchersAndIncrement(1*time.Millisecond, 2)
   981  
   982  						// wait for func to finish
   983  						Eventually(done).Should(Receive(BeTrue()))
   984  
   985  						Expect(executeErr).To(MatchError("Deployment has been canceled"))
   986  						Expect(warnings).To(ConsistOf("get-deployment-warning"))
   987  
   988  						Expect(fakeCloudControllerClient.GetDeploymentCallCount()).To(Equal(1))
   989  						Expect(fakeCloudControllerClient.GetDeploymentArgsForCall(0)).To(Equal(deploymentGUID))
   990  
   991  						Expect(fakeCloudControllerClient.GetApplicationProcessesCallCount()).To(Equal(0))
   992  						Expect(fakeCloudControllerClient.GetProcessInstancesCallCount()).To(Equal(0))
   993  
   994  						Expect(fakeConfig.StartupTimeoutCallCount()).To(Equal(1))
   995  					})
   996  
   997  				})
   998  
   999  				When("it is because the deployment was superseded", func() {
  1000  					BeforeEach(func() {
  1001  						fakeCloudControllerClient.GetDeploymentReturns(
  1002  							ccv3.Deployment{
  1003  								StatusValue:  constant.DeploymentStatusValueFinalized,
  1004  								StatusReason: constant.DeploymentStatusReasonSuperseded,
  1005  							},
  1006  							ccv3.Warnings{"get-deployment-warning"},
  1007  							nil,
  1008  						)
  1009  					})
  1010  
  1011  					It("returns warnings and the error", func() {
  1012  						// initial tick
  1013  						fakeClock.WaitForNWatchersAndIncrement(1*time.Millisecond, 2)
  1014  
  1015  						// wait for func to finish
  1016  						Eventually(done).Should(Receive(BeTrue()))
  1017  
  1018  						Expect(executeErr).To(MatchError("Deployment has been superseded"))
  1019  						Expect(warnings).To(ConsistOf("get-deployment-warning"))
  1020  
  1021  						Expect(fakeCloudControllerClient.GetDeploymentCallCount()).To(Equal(1))
  1022  						Expect(fakeCloudControllerClient.GetDeploymentArgsForCall(0)).To(Equal(deploymentGUID))
  1023  
  1024  						Expect(fakeCloudControllerClient.GetApplicationProcessesCallCount()).To(Equal(0))
  1025  						Expect(fakeCloudControllerClient.GetProcessInstancesCallCount()).To(Equal(0))
  1026  
  1027  						Expect(fakeConfig.StartupTimeoutCallCount()).To(Equal(1))
  1028  					})
  1029  
  1030  				})
  1031  
  1032  				When("it is because of an API error", func() {
  1033  					BeforeEach(func() {
  1034  						fakeCloudControllerClient.GetDeploymentReturns(
  1035  							ccv3.Deployment{},
  1036  							ccv3.Warnings{"get-deployment-warning"},
  1037  							errors.New("get-deployment-error"),
  1038  						)
  1039  					})
  1040  
  1041  					It("returns warnings and the error", func() {
  1042  						// initial tick
  1043  						fakeClock.WaitForNWatchersAndIncrement(1*time.Millisecond, 2)
  1044  
  1045  						// wait for func to finish
  1046  						Eventually(done).Should(Receive(BeTrue()))
  1047  
  1048  						Expect(executeErr).To(MatchError("get-deployment-error"))
  1049  						Expect(warnings).To(ConsistOf("get-deployment-warning"))
  1050  
  1051  						Expect(fakeCloudControllerClient.GetDeploymentCallCount()).To(Equal(1))
  1052  						Expect(fakeCloudControllerClient.GetDeploymentArgsForCall(0)).To(Equal(deploymentGUID))
  1053  
  1054  						Expect(fakeCloudControllerClient.GetApplicationProcessesCallCount()).To(Equal(0))
  1055  						Expect(fakeCloudControllerClient.GetProcessInstancesCallCount()).To(Equal(0))
  1056  
  1057  						Expect(fakeConfig.StartupTimeoutCallCount()).To(Equal(1))
  1058  					})
  1059  
  1060  				})
  1061  			})
  1062  
  1063  			When("getting the deployment succeeds", func() {
  1064  				BeforeEach(func() {
  1065  					// get processes requires the deployment to be deployed so we need this to indirectly test the error case
  1066  					fakeCloudControllerClient.GetDeploymentReturns(
  1067  						ccv3.Deployment{StatusValue: constant.DeploymentStatusValueFinalized, StatusReason: constant.DeploymentStatusReasonDeployed},
  1068  						ccv3.Warnings{"get-deployment-warning"},
  1069  						nil,
  1070  					)
  1071  
  1072  				})
  1073  
  1074  				When("getting the processes fails", func() {
  1075  					BeforeEach(func() {
  1076  						fakeCloudControllerClient.GetApplicationProcessesReturns(
  1077  							[]ccv3.Process{},
  1078  							ccv3.Warnings{"get-processes-warning"},
  1079  							errors.New("get-processes-error"),
  1080  						)
  1081  					})
  1082  
  1083  					It("returns warnings and the error", func() {
  1084  						// initial tick
  1085  						fakeClock.WaitForNWatchersAndIncrement(1*time.Millisecond, 2)
  1086  
  1087  						// wait for func to finish
  1088  						Eventually(done).Should(Receive(BeTrue()))
  1089  
  1090  						Expect(executeErr).To(MatchError("get-processes-error"))
  1091  						Expect(warnings).To(ConsistOf("get-deployment-warning", "get-processes-warning"))
  1092  
  1093  						Expect(fakeCloudControllerClient.GetDeploymentCallCount()).To(Equal(1))
  1094  						Expect(fakeCloudControllerClient.GetDeploymentArgsForCall(0)).To(Equal(deploymentGUID))
  1095  
  1096  						Expect(fakeCloudControllerClient.GetApplicationProcessesCallCount()).To(Equal(1))
  1097  						Expect(fakeCloudControllerClient.GetApplicationProcessesArgsForCall(0)).To(Equal(app.GUID))
  1098  
  1099  						Expect(fakeCloudControllerClient.GetProcessInstancesCallCount()).To(Equal(0))
  1100  
  1101  					})
  1102  				})
  1103  
  1104  				When("getting the processes succeeds", func() {
  1105  					BeforeEach(func() {
  1106  						fakeCloudControllerClient.GetApplicationProcessesReturns(
  1107  							[]ccv3.Process{{GUID: "process-guid"}},
  1108  							ccv3.Warnings{"get-processes-warning"},
  1109  							nil,
  1110  						)
  1111  					})
  1112  
  1113  					When("polling the processes fails", func() {
  1114  						BeforeEach(func() {
  1115  							fakeCloudControllerClient.GetProcessInstancesReturns(
  1116  								[]ccv3.ProcessInstance{},
  1117  								ccv3.Warnings{"poll-processes-warning"},
  1118  								errors.New("poll-processes-error"),
  1119  							)
  1120  						})
  1121  
  1122  						It("returns all warnings and errors", func() {
  1123  							// initial tick
  1124  							fakeClock.WaitForNWatchersAndIncrement(1*time.Millisecond, 2)
  1125  
  1126  							// wait for func to finish
  1127  							Eventually(done).Should(Receive(BeTrue()))
  1128  
  1129  							Expect(executeErr).To(MatchError("poll-processes-error"))
  1130  							Expect(warnings).To(ConsistOf("get-deployment-warning", "get-processes-warning", "poll-processes-warning"))
  1131  
  1132  							Expect(fakeCloudControllerClient.GetDeploymentCallCount()).To(Equal(1))
  1133  							Expect(fakeCloudControllerClient.GetDeploymentArgsForCall(0)).To(Equal(deploymentGUID))
  1134  
  1135  							Expect(fakeCloudControllerClient.GetApplicationProcessesCallCount()).To(Equal(1))
  1136  							Expect(fakeCloudControllerClient.GetApplicationProcessesArgsForCall(0)).To(Equal(app.GUID))
  1137  
  1138  							Expect(fakeCloudControllerClient.GetProcessInstancesCallCount()).To(Equal(1))
  1139  							Expect(fakeCloudControllerClient.GetProcessInstancesArgsForCall(0)).To(Equal("process-guid"))
  1140  						})
  1141  
  1142  					})
  1143  				})
  1144  
  1145  			})
  1146  
  1147  		})
  1148  
  1149  		// intentionally ignore the no-wait flag here for simplicity. One of these two things must cause timeout regardless of no-wait state
  1150  		When("there is a timeout error", func() {
  1151  			BeforeEach(func() {
  1152  				// 1 millisecond for initial tick then 1 to trigger timeout
  1153  				fakeConfig.StartupTimeoutReturns(2 * time.Millisecond)
  1154  			})
  1155  
  1156  			When("the deployment never deploys", func() {
  1157  				BeforeEach(func() {
  1158  					fakeCloudControllerClient.GetDeploymentReturns(
  1159  						ccv3.Deployment{StatusValue: constant.DeploymentStatusValueActive},
  1160  						ccv3.Warnings{"get-deployment-warning"},
  1161  						nil,
  1162  					)
  1163  				})
  1164  
  1165  				It("returns a timeout error and any warnings", func() {
  1166  					// initial tick
  1167  					fakeClock.WaitForNWatchersAndIncrement(1*time.Millisecond, 2)
  1168  
  1169  					Eventually(fakeCloudControllerClient.GetDeploymentCallCount).Should(Equal(1))
  1170  
  1171  					// timeout tick
  1172  					fakeClock.Increment(1 * time.Millisecond)
  1173  
  1174  					// wait for func to finish
  1175  					Eventually(done).Should(Receive(BeTrue()))
  1176  
  1177  					Expect(executeErr).To(MatchError(actionerror.StartupTimeoutError{}))
  1178  					Expect(warnings).To(ConsistOf("get-deployment-warning"))
  1179  				})
  1180  			})
  1181  
  1182  			When("the processes dont become healthy", func() {
  1183  				BeforeEach(func() {
  1184  					fakeCloudControllerClient.GetDeploymentReturns(
  1185  						ccv3.Deployment{StatusValue: constant.DeploymentStatusValueFinalized, StatusReason: constant.DeploymentStatusReasonDeployed},
  1186  						ccv3.Warnings{"get-deployment-warning"},
  1187  						nil,
  1188  					)
  1189  
  1190  					fakeCloudControllerClient.GetApplicationProcessesReturns(
  1191  						[]ccv3.Process{{GUID: "process-guid"}},
  1192  						ccv3.Warnings{"get-processes-warning"},
  1193  						nil,
  1194  					)
  1195  
  1196  					fakeCloudControllerClient.GetProcessInstancesReturns(
  1197  						[]ccv3.ProcessInstance{{State: constant.ProcessInstanceStarting}},
  1198  						ccv3.Warnings{"poll-processes-warning"},
  1199  						nil,
  1200  					)
  1201  				})
  1202  
  1203  				It("returns a timeout error and any warnings", func() {
  1204  					// initial tick
  1205  					fakeClock.WaitForNWatchersAndIncrement(1*time.Millisecond, 2)
  1206  
  1207  					Eventually(fakeCloudControllerClient.GetDeploymentCallCount).Should(Equal(1))
  1208  					Eventually(fakeCloudControllerClient.GetApplicationProcessesCallCount).Should(Equal(1))
  1209  					Eventually(fakeCloudControllerClient.GetProcessInstancesCallCount).Should(Equal(1))
  1210  
  1211  					// timeout tick
  1212  					fakeClock.Increment(1 * time.Millisecond)
  1213  
  1214  					// wait for func to finish
  1215  					Eventually(done).Should(Receive(BeTrue()))
  1216  
  1217  					Expect(executeErr).To(MatchError(actionerror.StartupTimeoutError{}))
  1218  					Expect(warnings).To(ConsistOf("get-deployment-warning", "get-processes-warning", "poll-processes-warning"))
  1219  				})
  1220  
  1221  			})
  1222  		})
  1223  
  1224  		When("things eventually become healthy", func() {
  1225  			When("the no wait flag is given", func() {
  1226  				BeforeEach(func() {
  1227  					// in total three loops 1: deployment still deploying 2: deployment deployed processes starting 3: processes started
  1228  					noWait = true
  1229  
  1230  					// Always return deploying as a way to check we respect no wait
  1231  					fakeCloudControllerClient.GetDeploymentReturns(
  1232  						ccv3.Deployment{
  1233  							StatusValue:  constant.DeploymentStatusValueActive,
  1234  							NewProcesses: []ccv3.Process{{GUID: "new-deployment-process"}},
  1235  						},
  1236  						ccv3.Warnings{"get-deployment-warning"},
  1237  						nil,
  1238  					)
  1239  
  1240  					// We only poll the processes. Two loops for fun
  1241  					fakeCloudControllerClient.GetProcessInstancesReturnsOnCall(0,
  1242  						[]ccv3.ProcessInstance{{State: constant.ProcessInstanceStarting}},
  1243  						ccv3.Warnings{"poll-processes-warning-1"},
  1244  						nil,
  1245  					)
  1246  
  1247  					fakeCloudControllerClient.GetProcessInstancesReturnsOnCall(1,
  1248  						[]ccv3.ProcessInstance{{State: constant.ProcessInstanceRunning}},
  1249  						ccv3.Warnings{"poll-processes-warning-2"},
  1250  						nil,
  1251  					)
  1252  				})
  1253  
  1254  				It("polls the start of the application correctly and returns warnings and no error", func() {
  1255  					// Initial tick
  1256  					fakeClock.WaitForNWatchersAndIncrement(1*time.Millisecond, 2)
  1257  
  1258  					// assert one of our watcher is the timeout
  1259  					Expect(fakeConfig.StartupTimeoutCallCount()).To(Equal(1))
  1260  
  1261  					// the first time through we always get the deployment regardless of no-wait
  1262  					Eventually(fakeCloudControllerClient.GetDeploymentCallCount).Should(Equal(1))
  1263  					Expect(fakeCloudControllerClient.GetDeploymentArgsForCall(0)).To(Equal(deploymentGUID))
  1264  					Eventually(fakeCloudControllerClient.GetProcessInstancesCallCount).Should(Equal(1))
  1265  					Expect(fakeCloudControllerClient.GetProcessInstancesArgsForCall(0)).To(Equal("new-deployment-process"))
  1266  					Eventually(fakeConfig.PollingIntervalCallCount).Should(Equal(1))
  1267  
  1268  					fakeClock.Increment(1 * time.Second)
  1269  
  1270  					Eventually(fakeCloudControllerClient.GetDeploymentCallCount).Should(Equal(2))
  1271  					Expect(fakeCloudControllerClient.GetDeploymentArgsForCall(0)).To(Equal(deploymentGUID))
  1272  					Eventually(fakeCloudControllerClient.GetProcessInstancesCallCount).Should(Equal(2))
  1273  					Expect(fakeCloudControllerClient.GetProcessInstancesArgsForCall(0)).To(Equal("new-deployment-process"))
  1274  
  1275  					Eventually(done).Should(Receive(BeTrue()))
  1276  
  1277  					Expect(executeErr).NotTo(HaveOccurred())
  1278  					Expect(warnings).To(ConsistOf(
  1279  						"get-deployment-warning",
  1280  						"poll-processes-warning-1",
  1281  						"get-deployment-warning",
  1282  						"poll-processes-warning-2",
  1283  					))
  1284  
  1285  					Expect(fakeCloudControllerClient.GetDeploymentCallCount()).To(Equal(2))
  1286  					Expect(fakeCloudControllerClient.GetApplicationProcessesCallCount()).To(Equal(0))
  1287  					Expect(fakeCloudControllerClient.GetProcessInstancesCallCount()).To(Equal(2))
  1288  					Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(1))
  1289  
  1290  				})
  1291  
  1292  			})
  1293  
  1294  			When("the no wait flag is not given", func() {
  1295  				BeforeEach(func() {
  1296  					// in total three loops 1: deployment still deploying 2: deployment deployed processes starting 3: processes started
  1297  					fakeCloudControllerClient.GetDeploymentReturnsOnCall(0,
  1298  						ccv3.Deployment{StatusValue: constant.DeploymentStatusValueActive},
  1299  						ccv3.Warnings{"get-deployment-warning-1"},
  1300  						nil,
  1301  					)
  1302  
  1303  					// Poll the deployment twice to make sure we are polling (one in the above before each)
  1304  					fakeCloudControllerClient.GetDeploymentReturnsOnCall(1,
  1305  						ccv3.Deployment{StatusValue: constant.DeploymentStatusValueFinalized, StatusReason: constant.DeploymentStatusReasonDeployed},
  1306  						ccv3.Warnings{"get-deployment-warning-2"},
  1307  						nil,
  1308  					)
  1309  
  1310  					// then we get the processes. This should only be called once
  1311  					fakeCloudControllerClient.GetApplicationProcessesReturns(
  1312  						[]ccv3.Process{{GUID: "process-guid"}},
  1313  						ccv3.Warnings{"get-processes-warning"},
  1314  						nil,
  1315  					)
  1316  
  1317  					// then we poll the processes. Two loops for fun
  1318  					fakeCloudControllerClient.GetProcessInstancesReturnsOnCall(0,
  1319  						[]ccv3.ProcessInstance{{State: constant.ProcessInstanceStarting}},
  1320  						ccv3.Warnings{"poll-processes-warning-1"},
  1321  						nil,
  1322  					)
  1323  
  1324  					fakeCloudControllerClient.GetProcessInstancesReturnsOnCall(1,
  1325  						[]ccv3.ProcessInstance{{State: constant.ProcessInstanceRunning}},
  1326  						ccv3.Warnings{"poll-processes-warning-2"},
  1327  						nil,
  1328  					)
  1329  				})
  1330  
  1331  				It("polls the start of the application correctly and returns warnings and no error", func() {
  1332  					// Initial tick
  1333  					fakeClock.WaitForNWatchersAndIncrement(1*time.Millisecond, 2)
  1334  
  1335  					// assert one of our watchers is for the timeout
  1336  					Expect(fakeConfig.StartupTimeoutCallCount()).To(Equal(1))
  1337  
  1338  					Eventually(fakeCloudControllerClient.GetDeploymentCallCount).Should(Equal(1))
  1339  					Expect(fakeCloudControllerClient.GetDeploymentArgsForCall(0)).To(Equal(deploymentGUID))
  1340  					Eventually(fakeConfig.PollingIntervalCallCount).Should(Equal(1))
  1341  
  1342  					// start the second loop where the deployment is deployed so we poll processes
  1343  					fakeClock.Increment(1 * time.Second)
  1344  
  1345  					Eventually(fakeCloudControllerClient.GetDeploymentCallCount).Should(Equal(2))
  1346  					Expect(fakeCloudControllerClient.GetDeploymentArgsForCall(1)).To(Equal(deploymentGUID))
  1347  					Eventually(fakeCloudControllerClient.GetApplicationProcessesCallCount).Should(Equal(1))
  1348  					Expect(fakeCloudControllerClient.GetApplicationProcessesArgsForCall(0)).To(Equal(app.GUID))
  1349  					Eventually(fakeCloudControllerClient.GetProcessInstancesCallCount).Should(Equal(1))
  1350  					Expect(fakeCloudControllerClient.GetProcessInstancesArgsForCall(0)).To(Equal("process-guid"))
  1351  					Eventually(fakeConfig.PollingIntervalCallCount).Should(Equal(2))
  1352  
  1353  					fakeClock.Increment(1 * time.Second)
  1354  
  1355  					// we should stop polling because it is deployed
  1356  					Eventually(fakeCloudControllerClient.GetProcessInstancesCallCount).Should(Equal(2))
  1357  					Expect(fakeCloudControllerClient.GetProcessInstancesArgsForCall(0)).To(Equal("process-guid"))
  1358  
  1359  					Eventually(done).Should(Receive(BeTrue()))
  1360  
  1361  					Expect(executeErr).NotTo(HaveOccurred())
  1362  					Expect(warnings).To(ConsistOf(
  1363  						"get-deployment-warning-1",
  1364  						"get-deployment-warning-2",
  1365  						"get-processes-warning",
  1366  						"poll-processes-warning-1",
  1367  						"poll-processes-warning-2",
  1368  					))
  1369  
  1370  					Expect(fakeCloudControllerClient.GetDeploymentCallCount()).To(Equal(2))
  1371  					Expect(fakeCloudControllerClient.GetApplicationProcessesCallCount()).To(Equal(1))
  1372  					Expect(fakeCloudControllerClient.GetProcessInstancesCallCount()).To(Equal(2))
  1373  					Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(2))
  1374  
  1375  				})
  1376  
  1377  			})
  1378  
  1379  		})
  1380  	})
  1381  
  1382  	Describe("SetApplicationProcessHealthCheckTypeByNameAndSpace", func() {
  1383  		var (
  1384  			healthCheckType     constant.HealthCheckType
  1385  			healthCheckEndpoint string
  1386  
  1387  			warnings Warnings
  1388  			err      error
  1389  			app      resources.Application
  1390  		)
  1391  
  1392  		BeforeEach(func() {
  1393  			healthCheckType = constant.HTTP
  1394  			healthCheckEndpoint = "some-http-endpoint"
  1395  		})
  1396  
  1397  		JustBeforeEach(func() {
  1398  			app, warnings, err = actor.SetApplicationProcessHealthCheckTypeByNameAndSpace(
  1399  				"some-app-name",
  1400  				"some-space-guid",
  1401  				healthCheckType,
  1402  				healthCheckEndpoint,
  1403  				"some-process-type",
  1404  				42,
  1405  			)
  1406  		})
  1407  
  1408  		When("getting application returns an error", func() {
  1409  			var expectedErr error
  1410  
  1411  			BeforeEach(func() {
  1412  				expectedErr = errors.New("some-error")
  1413  				fakeCloudControllerClient.GetApplicationsReturns(
  1414  					[]resources.Application{},
  1415  					ccv3.Warnings{"some-warning"},
  1416  					expectedErr,
  1417  				)
  1418  			})
  1419  
  1420  			It("returns the error and warnings", func() {
  1421  				Expect(err).To(Equal(expectedErr))
  1422  				Expect(warnings).To(ConsistOf("some-warning"))
  1423  			})
  1424  		})
  1425  
  1426  		When("application exists", func() {
  1427  			var ccv3App resources.Application
  1428  
  1429  			BeforeEach(func() {
  1430  				ccv3App = resources.Application{
  1431  					GUID: "some-app-guid",
  1432  				}
  1433  
  1434  				fakeCloudControllerClient.GetApplicationsReturns(
  1435  					[]resources.Application{ccv3App},
  1436  					ccv3.Warnings{"some-warning"},
  1437  					nil,
  1438  				)
  1439  			})
  1440  
  1441  			When("setting the health check returns an error", func() {
  1442  				var expectedErr error
  1443  
  1444  				BeforeEach(func() {
  1445  					expectedErr = errors.New("some-error")
  1446  					fakeCloudControllerClient.GetApplicationProcessByTypeReturns(
  1447  						ccv3.Process{},
  1448  						ccv3.Warnings{"some-process-warning"},
  1449  						expectedErr,
  1450  					)
  1451  				})
  1452  
  1453  				It("returns the error and warnings", func() {
  1454  					Expect(err).To(Equal(expectedErr))
  1455  					Expect(warnings).To(ConsistOf("some-warning", "some-process-warning"))
  1456  				})
  1457  			})
  1458  
  1459  			When("application process exists", func() {
  1460  				BeforeEach(func() {
  1461  					fakeCloudControllerClient.GetApplicationProcessByTypeReturns(
  1462  						ccv3.Process{GUID: "some-process-guid"},
  1463  						ccv3.Warnings{"some-process-warning"},
  1464  						nil,
  1465  					)
  1466  
  1467  					fakeCloudControllerClient.UpdateProcessReturns(
  1468  						ccv3.Process{GUID: "some-process-guid"},
  1469  						ccv3.Warnings{"some-health-check-warning"},
  1470  						nil,
  1471  					)
  1472  				})
  1473  
  1474  				It("returns the application", func() {
  1475  					Expect(err).NotTo(HaveOccurred())
  1476  					Expect(warnings).To(ConsistOf("some-warning", "some-process-warning", "some-health-check-warning"))
  1477  
  1478  					Expect(app).To(Equal(resources.Application{
  1479  						GUID: ccv3App.GUID,
  1480  					}))
  1481  
  1482  					Expect(fakeCloudControllerClient.GetApplicationProcessByTypeCallCount()).To(Equal(1))
  1483  					appGUID, processType := fakeCloudControllerClient.GetApplicationProcessByTypeArgsForCall(0)
  1484  					Expect(appGUID).To(Equal("some-app-guid"))
  1485  					Expect(processType).To(Equal("some-process-type"))
  1486  
  1487  					Expect(fakeCloudControllerClient.UpdateProcessCallCount()).To(Equal(1))
  1488  					process := fakeCloudControllerClient.UpdateProcessArgsForCall(0)
  1489  					Expect(process.GUID).To(Equal("some-process-guid"))
  1490  					Expect(process.HealthCheckType).To(Equal(constant.HTTP))
  1491  					Expect(process.HealthCheckEndpoint).To(Equal("some-http-endpoint"))
  1492  					Expect(process.HealthCheckInvocationTimeout).To(BeEquivalentTo(42))
  1493  				})
  1494  			})
  1495  		})
  1496  	})
  1497  
  1498  	Describe("StopApplication", func() {
  1499  		var (
  1500  			warnings   Warnings
  1501  			executeErr error
  1502  		)
  1503  
  1504  		JustBeforeEach(func() {
  1505  			warnings, executeErr = actor.StopApplication("some-app-guid")
  1506  		})
  1507  
  1508  		When("there are no client errors", func() {
  1509  			BeforeEach(func() {
  1510  				fakeCloudControllerClient.UpdateApplicationStopReturns(
  1511  					resources.Application{GUID: "some-app-guid"},
  1512  					ccv3.Warnings{"stop-application-warning"},
  1513  					nil,
  1514  				)
  1515  			})
  1516  
  1517  			It("stops the application", func() {
  1518  				Expect(executeErr).ToNot(HaveOccurred())
  1519  				Expect(warnings).To(ConsistOf("stop-application-warning"))
  1520  
  1521  				Expect(fakeCloudControllerClient.UpdateApplicationStopCallCount()).To(Equal(1))
  1522  				Expect(fakeCloudControllerClient.UpdateApplicationStopArgsForCall(0)).To(Equal("some-app-guid"))
  1523  			})
  1524  		})
  1525  
  1526  		When("stopping the application fails", func() {
  1527  			var expectedErr error
  1528  			BeforeEach(func() {
  1529  				expectedErr = errors.New("some set stop-application error")
  1530  				fakeCloudControllerClient.UpdateApplicationStopReturns(
  1531  					resources.Application{},
  1532  					ccv3.Warnings{"stop-application-warning"},
  1533  					expectedErr,
  1534  				)
  1535  			})
  1536  
  1537  			It("returns the error", func() {
  1538  				Expect(executeErr).To(Equal(expectedErr))
  1539  				Expect(warnings).To(ConsistOf("stop-application-warning"))
  1540  			})
  1541  		})
  1542  	})
  1543  
  1544  	Describe("StartApplication", func() {
  1545  		var (
  1546  			warnings   Warnings
  1547  			executeErr error
  1548  		)
  1549  
  1550  		BeforeEach(func() {
  1551  			fakeConfig.StartupTimeoutReturns(time.Second)
  1552  			fakeConfig.PollingIntervalReturns(0)
  1553  		})
  1554  
  1555  		JustBeforeEach(func() {
  1556  			warnings, executeErr = actor.StartApplication("some-app-guid")
  1557  		})
  1558  
  1559  		When("there are no client errors", func() {
  1560  			BeforeEach(func() {
  1561  				fakeCloudControllerClient.UpdateApplicationStartReturns(
  1562  					resources.Application{GUID: "some-app-guid"},
  1563  					ccv3.Warnings{"start-application-warning"},
  1564  					nil,
  1565  				)
  1566  			})
  1567  
  1568  			It("starts the application", func() {
  1569  				Expect(executeErr).ToNot(HaveOccurred())
  1570  				Expect(warnings).To(ConsistOf("start-application-warning"))
  1571  
  1572  				Expect(fakeCloudControllerClient.UpdateApplicationStartCallCount()).To(Equal(1))
  1573  				Expect(fakeCloudControllerClient.UpdateApplicationStartArgsForCall(0)).To(Equal("some-app-guid"))
  1574  			})
  1575  		})
  1576  
  1577  		When("starting the application fails", func() {
  1578  			var expectedErr error
  1579  
  1580  			BeforeEach(func() {
  1581  				expectedErr = errors.New("some set start-application error")
  1582  				fakeCloudControllerClient.UpdateApplicationStartReturns(
  1583  					resources.Application{},
  1584  					ccv3.Warnings{"start-application-warning"},
  1585  					expectedErr,
  1586  				)
  1587  			})
  1588  
  1589  			It("returns the error", func() {
  1590  				warnings, err := actor.StartApplication("some-app-guid")
  1591  
  1592  				Expect(err).To(Equal(expectedErr))
  1593  				Expect(warnings).To(ConsistOf("start-application-warning"))
  1594  			})
  1595  		})
  1596  	})
  1597  
  1598  	Describe("RestartApplication", func() {
  1599  		var (
  1600  			warnings   Warnings
  1601  			executeErr error
  1602  			noWait     bool
  1603  		)
  1604  
  1605  		BeforeEach(func() {
  1606  			fakeConfig.StartupTimeoutReturns(time.Second)
  1607  			fakeConfig.PollingIntervalReturns(0)
  1608  			noWait = false
  1609  		})
  1610  
  1611  		JustBeforeEach(func() {
  1612  			warnings, executeErr = actor.RestartApplication("some-app-guid", noWait)
  1613  		})
  1614  
  1615  		When("restarting the application is successful", func() {
  1616  			BeforeEach(func() {
  1617  				fakeCloudControllerClient.UpdateApplicationRestartReturns(
  1618  					resources.Application{GUID: "some-app-guid"},
  1619  					ccv3.Warnings{"restart-application-warning"},
  1620  					nil,
  1621  				)
  1622  			})
  1623  
  1624  			It("does not error", func() {
  1625  				Expect(executeErr).ToNot(HaveOccurred())
  1626  				Expect(warnings).To(ConsistOf("restart-application-warning"))
  1627  			})
  1628  		})
  1629  
  1630  		When("restarting the application fails", func() {
  1631  			var expectedErr error
  1632  
  1633  			BeforeEach(func() {
  1634  				expectedErr = errors.New("some set restart-application error")
  1635  				fakeCloudControllerClient.UpdateApplicationRestartReturns(
  1636  					resources.Application{},
  1637  					ccv3.Warnings{"restart-application-warning"},
  1638  					expectedErr,
  1639  				)
  1640  			})
  1641  
  1642  			It("returns the warnings and error", func() {
  1643  				Expect(executeErr).To(Equal(expectedErr))
  1644  				Expect(warnings).To(ConsistOf("restart-application-warning"))
  1645  			})
  1646  		})
  1647  	})
  1648  
  1649  	Describe("PollProcesses", func() {
  1650  		var (
  1651  			processes               []ccv3.Process
  1652  			handleInstanceDetails   func(string)
  1653  			reportedInstanceDetails []string
  1654  
  1655  			keepPolling bool
  1656  			warnings    Warnings
  1657  			executeErr  error
  1658  		)
  1659  
  1660  		BeforeEach(func() {
  1661  			reportedInstanceDetails = []string{}
  1662  			handleInstanceDetails = func(instanceDetails string) {
  1663  				reportedInstanceDetails = append(reportedInstanceDetails, instanceDetails)
  1664  			}
  1665  
  1666  			processes = []ccv3.Process{
  1667  				{GUID: "process-1"},
  1668  				{GUID: "process-2"},
  1669  			}
  1670  		})
  1671  
  1672  		JustBeforeEach(func() {
  1673  			keepPolling, warnings, executeErr = actor.PollProcesses(processes, handleInstanceDetails)
  1674  		})
  1675  
  1676  		It("gets process instances for each process", func() {
  1677  			Expect(executeErr).NotTo(HaveOccurred())
  1678  			Expect(fakeCloudControllerClient.GetProcessInstancesCallCount()).To(Equal(2))
  1679  			Expect(fakeCloudControllerClient.GetProcessInstancesArgsForCall(0)).To(Equal("process-1"))
  1680  			Expect(fakeCloudControllerClient.GetProcessInstancesArgsForCall(1)).To(Equal("process-2"))
  1681  		})
  1682  
  1683  		When("getting the process instances fails", func() {
  1684  			BeforeEach(func() {
  1685  				fakeCloudControllerClient.GetProcessInstancesReturns(nil, ccv3.Warnings{"get-instances-warning"}, errors.New("get-instances-error"))
  1686  			})
  1687  
  1688  			It("returns an error and warnings and terminates the loop", func() {
  1689  				Expect(executeErr).To(MatchError("get-instances-error"))
  1690  				Expect(warnings).To(ConsistOf("get-instances-warning"))
  1691  				Expect(keepPolling).To(BeTrue())
  1692  
  1693  				Expect(fakeCloudControllerClient.GetProcessInstancesCallCount()).To(Equal(1))
  1694  				Expect(fakeCloudControllerClient.GetProcessInstancesArgsForCall(0)).To(Equal("process-1"))
  1695  			})
  1696  		})
  1697  
  1698  		When("getting the process instances is always successful", func() {
  1699  			When("a process has all instances crashed", func() {
  1700  				BeforeEach(func() {
  1701  					fakeCloudControllerClient.GetProcessInstancesReturns(
  1702  						[]ccv3.ProcessInstance{
  1703  							{State: constant.ProcessInstanceCrashed, Details: "details1"},
  1704  						},
  1705  						ccv3.Warnings{"get-process1-instances-warning"},
  1706  						nil,
  1707  					)
  1708  				})
  1709  
  1710  				It("calls the callback function with the retrieved instances", func() {
  1711  					Expect(reportedInstanceDetails).To(Equal([]string{
  1712  						"Error starting instances: 'details1'",
  1713  					}))
  1714  				})
  1715  
  1716  				It("returns an all instances crashed error", func() {
  1717  					Expect(executeErr).To(MatchError(actionerror.AllInstancesCrashedError{}))
  1718  					Expect(warnings).To(ConsistOf("get-process1-instances-warning"))
  1719  					Expect(keepPolling).To(BeTrue())
  1720  				})
  1721  			})
  1722  
  1723  			When("there are still instances in the starting state for a process", func() {
  1724  				BeforeEach(func() {
  1725  					fakeCloudControllerClient.GetProcessInstancesReturnsOnCall(0,
  1726  						[]ccv3.ProcessInstance{
  1727  							{State: constant.ProcessInstanceRunning},
  1728  						},
  1729  						ccv3.Warnings{"get-process1-instances-warning"},
  1730  						nil,
  1731  					)
  1732  
  1733  					fakeCloudControllerClient.GetProcessInstancesReturnsOnCall(1,
  1734  						[]ccv3.ProcessInstance{
  1735  							{State: constant.ProcessInstanceStarting, Details: "details2"},
  1736  						},
  1737  						ccv3.Warnings{"get-process2-instances-warning"},
  1738  						nil,
  1739  					)
  1740  				})
  1741  
  1742  				It("calls the callback function with the retrieved instances", func() {
  1743  					Expect(reportedInstanceDetails).To(Equal([]string{
  1744  						"Instances starting...",
  1745  						"Error starting instances: 'details2'",
  1746  					}))
  1747  				})
  1748  
  1749  				It("returns success and that we should keep polling", func() {
  1750  					Expect(executeErr).NotTo(HaveOccurred())
  1751  					Expect(warnings).To(ConsistOf("get-process1-instances-warning", "get-process2-instances-warning"))
  1752  					Expect(keepPolling).To(BeFalse())
  1753  				})
  1754  			})
  1755  
  1756  			When("all the instances of all processes are stable", func() {
  1757  				BeforeEach(func() {
  1758  					fakeCloudControllerClient.GetProcessInstancesReturnsOnCall(0,
  1759  						[]ccv3.ProcessInstance{
  1760  							{State: constant.ProcessInstanceRunning, Details: "details1"},
  1761  						},
  1762  						ccv3.Warnings{"get-process1-instances-warning"},
  1763  						nil,
  1764  					)
  1765  
  1766  					fakeCloudControllerClient.GetProcessInstancesReturnsOnCall(1,
  1767  						[]ccv3.ProcessInstance{
  1768  							{State: constant.ProcessInstanceRunning},
  1769  						},
  1770  						ccv3.Warnings{"get-process2-instances-warning"},
  1771  						nil,
  1772  					)
  1773  				})
  1774  
  1775  				It("calls the callback function with the retrieved instances", func() {
  1776  					Expect(reportedInstanceDetails).To(Equal([]string{
  1777  						"Error starting instances: 'details1'",
  1778  						"Instances starting...",
  1779  					}))
  1780  				})
  1781  
  1782  				It("returns success and that we should keep polling", func() {
  1783  					Expect(executeErr).NotTo(HaveOccurred())
  1784  					Expect(warnings).To(ConsistOf("get-process1-instances-warning", "get-process2-instances-warning"))
  1785  					Expect(keepPolling).To(BeTrue())
  1786  				})
  1787  
  1788  			})
  1789  		})
  1790  
  1791  	})
  1792  
  1793  	Describe("GetUnstagedNewestPackageGUID", func() {
  1794  		var (
  1795  			packageToStage string
  1796  			warnings       Warnings
  1797  			executeErr     error
  1798  		)
  1799  
  1800  		JustBeforeEach(func() {
  1801  			packageToStage, warnings, executeErr = actor.GetUnstagedNewestPackageGUID("some-app-guid")
  1802  		})
  1803  
  1804  		// Nothing to stage.
  1805  		When("There are no packages on the app", func() {
  1806  			When("getting the packages succeeds", func() {
  1807  				BeforeEach(func() {
  1808  					fakeCloudControllerClient.GetPackagesReturns([]ccv3.Package{}, ccv3.Warnings{"get-packages-warnings"}, nil)
  1809  				})
  1810  
  1811  				It("checks for packages", func() {
  1812  					Expect(fakeCloudControllerClient.GetPackagesCallCount()).To(Equal(1))
  1813  					Expect(fakeCloudControllerClient.GetPackagesArgsForCall(0)).To(ConsistOf(
  1814  						ccv3.Query{Key: ccv3.AppGUIDFilter, Values: []string{"some-app-guid"}},
  1815  						ccv3.Query{Key: ccv3.OrderBy, Values: []string{ccv3.CreatedAtDescendingOrder}},
  1816  						ccv3.Query{Key: ccv3.PerPage, Values: []string{"1"}},
  1817  					))
  1818  				})
  1819  
  1820  				It("returns empty string", func() {
  1821  					Expect(packageToStage).To(Equal(""))
  1822  					Expect(warnings).To(ConsistOf("get-packages-warnings"))
  1823  					Expect(executeErr).To(BeNil())
  1824  				})
  1825  			})
  1826  
  1827  			When("getting the packages fails", func() {
  1828  				BeforeEach(func() {
  1829  					fakeCloudControllerClient.GetPackagesReturns(
  1830  						nil,
  1831  						ccv3.Warnings{"get-packages-warnings"},
  1832  						errors.New("get-packages-error"),
  1833  					)
  1834  				})
  1835  
  1836  				It("returns the error", func() {
  1837  					Expect(warnings).To(ConsistOf("get-packages-warnings"))
  1838  					Expect(executeErr).To(MatchError("get-packages-error"))
  1839  				})
  1840  			})
  1841  		})
  1842  
  1843  		When("there are packages", func() {
  1844  			BeforeEach(func() {
  1845  				fakeCloudControllerClient.GetPackagesReturns(
  1846  					[]ccv3.Package{{GUID: "package-guid", CreatedAt: "2019-01-01T06:00:00Z"}},
  1847  					ccv3.Warnings{"get-packages-warning"},
  1848  					nil)
  1849  			})
  1850  
  1851  			It("checks for the packages latest droplet", func() {
  1852  				Expect(fakeCloudControllerClient.GetPackageDropletsCallCount()).To(Equal(1))
  1853  				packageGuid, queries := fakeCloudControllerClient.GetPackageDropletsArgsForCall(0)
  1854  				Expect(packageGuid).To(Equal("package-guid"))
  1855  				Expect(queries).To(ConsistOf(
  1856  					ccv3.Query{Key: ccv3.PerPage, Values: []string{"1"}},
  1857  					ccv3.Query{Key: ccv3.StatesFilter, Values: []string{"STAGED"}},
  1858  				))
  1859  			})
  1860  
  1861  			When("the newest package's has a STAGED droplet", func() {
  1862  				BeforeEach(func() {
  1863  					fakeCloudControllerClient.GetPackageDropletsReturns(
  1864  						[]resources.Droplet{{State: constant.DropletStaged}},
  1865  						ccv3.Warnings{"get-package-droplet-warning"},
  1866  						nil,
  1867  					)
  1868  				})
  1869  
  1870  				It("returns empty string", func() {
  1871  					Expect(packageToStage).To(Equal(""))
  1872  					Expect(warnings).To(ConsistOf("get-packages-warning", "get-package-droplet-warning"))
  1873  					Expect(executeErr).To(BeNil())
  1874  				})
  1875  			})
  1876  
  1877  			When("the package has no STAGED droplets", func() {
  1878  				BeforeEach(func() {
  1879  					fakeCloudControllerClient.GetPackageDropletsReturns(
  1880  						[]resources.Droplet{},
  1881  						ccv3.Warnings{"get-package-droplet-warning"},
  1882  						nil,
  1883  					)
  1884  				})
  1885  
  1886  				It("returns the guid of the newest package", func() {
  1887  					Expect(packageToStage).To(Equal("package-guid"))
  1888  					Expect(warnings).To(ConsistOf("get-packages-warning", "get-package-droplet-warning"))
  1889  					Expect(executeErr).To(BeNil())
  1890  				})
  1891  			})
  1892  		})
  1893  	})
  1894  
  1895  	Describe("RenameApplicationByNameAndSpaceGUID", func() {
  1896  		When("the app does not exist", func() {
  1897  			BeforeEach(func() {
  1898  				fakeCloudControllerClient.GetApplicationsReturns(
  1899  					[]resources.Application{},
  1900  					ccv3.Warnings{"some-warning"},
  1901  					nil,
  1902  				)
  1903  			})
  1904  
  1905  			It("returns an ApplicationNotFoundError and the warnings", func() {
  1906  				_, warnings, err := actor.RenameApplicationByNameAndSpaceGUID("old-app-name", "new-app-name", "space-guid")
  1907  				Expect(warnings).To(ConsistOf("some-warning"))
  1908  				Expect(err).To(MatchError(actionerror.ApplicationNotFoundError{Name: "old-app-name"}))
  1909  			})
  1910  		})
  1911  
  1912  		When("the cloud controller client returns an error on application find", func() {
  1913  			var expectedError error
  1914  
  1915  			BeforeEach(func() {
  1916  				expectedError = errors.New("I am a CloudControllerClient Error")
  1917  				fakeCloudControllerClient.GetApplicationsReturns(
  1918  					[]resources.Application{},
  1919  					ccv3.Warnings{"some-warning"},
  1920  					expectedError)
  1921  			})
  1922  
  1923  			It("returns the warnings and the error", func() {
  1924  				_, warnings, err := actor.RenameApplicationByNameAndSpaceGUID("old-app-name", "new-app-name", "space-guid")
  1925  				Expect(warnings).To(ConsistOf("some-warning"))
  1926  				Expect(err).To(MatchError(expectedError))
  1927  			})
  1928  		})
  1929  
  1930  		When("the cloud controller client returns an error on application update", func() {
  1931  			var expectedError error
  1932  
  1933  			BeforeEach(func() {
  1934  				expectedError = errors.New("I am a CloudControllerClient Error")
  1935  				fakeCloudControllerClient.GetApplicationsReturns(
  1936  					[]resources.Application{
  1937  						{
  1938  							Name: "old-app-name",
  1939  							GUID: "old-app-guid",
  1940  						},
  1941  					},
  1942  					ccv3.Warnings{"get-app-warning"},
  1943  					nil)
  1944  				fakeCloudControllerClient.UpdateApplicationReturns(
  1945  					resources.Application{},
  1946  					ccv3.Warnings{"update-app-warning"},
  1947  					expectedError)
  1948  			})
  1949  
  1950  			It("returns the warnings and the error", func() {
  1951  				_, warnings, err := actor.RenameApplicationByNameAndSpaceGUID("old-app-name", "new-app-name", "space-guid")
  1952  				Expect(warnings).To(ConsistOf("get-app-warning", "update-app-warning"))
  1953  				Expect(err).To(MatchError(expectedError))
  1954  			})
  1955  		})
  1956  
  1957  		When("the app exists", func() {
  1958  			BeforeEach(func() {
  1959  				fakeCloudControllerClient.GetApplicationsReturns(
  1960  					[]resources.Application{
  1961  						{
  1962  							Name: "old-app-name",
  1963  							GUID: "old-app-guid",
  1964  						},
  1965  					},
  1966  					ccv3.Warnings{"get-app-warning"},
  1967  					nil,
  1968  				)
  1969  
  1970  				fakeCloudControllerClient.UpdateApplicationReturns(
  1971  					resources.Application{
  1972  						Name: "new-app-name",
  1973  						GUID: "old-app-guid",
  1974  					},
  1975  					ccv3.Warnings{"update-app-warning"},
  1976  					nil,
  1977  				)
  1978  			})
  1979  
  1980  			It("changes the app name and returns the application and warnings", func() {
  1981  				app, warnings, err := actor.RenameApplicationByNameAndSpaceGUID("old-app-name", "new-app-name", "some-space-guid")
  1982  				Expect(err).ToNot(HaveOccurred())
  1983  				Expect(app).To(Equal(resources.Application{
  1984  					Name: "new-app-name",
  1985  					GUID: "old-app-guid",
  1986  				}))
  1987  				Expect(warnings).To(ConsistOf("get-app-warning", "update-app-warning"))
  1988  
  1989  				Expect(fakeCloudControllerClient.UpdateApplicationArgsForCall(0)).To(Equal(
  1990  					resources.Application{
  1991  						Name: "new-app-name",
  1992  						GUID: "old-app-guid",
  1993  					}))
  1994  
  1995  			})
  1996  		})
  1997  
  1998  	})
  1999  })