github.com/willmadison/cli@v6.40.1-0.20181018160101-29d5937903ff+incompatible/actor/v2action/application_test.go (about)

     1  package v2action_test
     2  
     3  import (
     4  	"errors"
     5  	"time"
     6  
     7  	"code.cloudfoundry.org/cli/actor/actionerror"
     8  	. "code.cloudfoundry.org/cli/actor/v2action"
     9  	"code.cloudfoundry.org/cli/actor/v2action/v2actionfakes"
    10  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
    11  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv2"
    12  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv2/constant"
    13  	"code.cloudfoundry.org/cli/types"
    14  
    15  	"github.com/cloudfoundry/sonde-go/events"
    16  	. "github.com/onsi/ginkgo"
    17  	. "github.com/onsi/gomega"
    18  )
    19  
    20  var _ = Describe("Application Actions", func() {
    21  	var (
    22  		actor                     *Actor
    23  		fakeCloudControllerClient *v2actionfakes.FakeCloudControllerClient
    24  		fakeConfig                *v2actionfakes.FakeConfig
    25  	)
    26  
    27  	BeforeEach(func() {
    28  		actor, fakeCloudControllerClient, _, fakeConfig = NewTestActor()
    29  		fakeConfig.DialTimeoutReturns(time.Millisecond)
    30  	})
    31  
    32  	Describe("Application", func() {
    33  		var app Application
    34  		BeforeEach(func() {
    35  			app = Application{}
    36  		})
    37  
    38  		Describe("CalculatedCommand", func() {
    39  			When("command is set", func() {
    40  				BeforeEach(func() {
    41  					app.Command = types.FilteredString{IsSet: true, Value: "foo"}
    42  					app.DetectedStartCommand = types.FilteredString{IsSet: true, Value: "bar"}
    43  				})
    44  
    45  				It("returns back the command", func() {
    46  					Expect(app.CalculatedCommand()).To(Equal("foo"))
    47  				})
    48  			})
    49  
    50  			Context("only detected start command is set", func() {
    51  				BeforeEach(func() {
    52  					app.DetectedStartCommand = types.FilteredString{IsSet: true, Value: "bar"}
    53  				})
    54  
    55  				It("returns back the detected start command", func() {
    56  					Expect(app.CalculatedCommand()).To(Equal("bar"))
    57  				})
    58  			})
    59  
    60  			Context("neither command nor detected start command are set", func() {
    61  				It("returns an empty string", func() {
    62  					Expect(app.CalculatedCommand()).To(BeEmpty())
    63  				})
    64  			})
    65  		})
    66  
    67  		Describe("CalculatedBuildpack", func() {
    68  			When("buildpack is set", func() {
    69  				BeforeEach(func() {
    70  					app.Buildpack = types.FilteredString{IsSet: true, Value: "foo"}
    71  					app.DetectedBuildpack = types.FilteredString{IsSet: true, Value: "bar"}
    72  				})
    73  
    74  				It("returns back the buildpack", func() {
    75  					Expect(app.CalculatedBuildpack()).To(Equal("foo"))
    76  				})
    77  			})
    78  
    79  			Context("only detected buildpack is set", func() {
    80  				BeforeEach(func() {
    81  					app.DetectedBuildpack = types.FilteredString{IsSet: true, Value: "bar"}
    82  				})
    83  
    84  				It("returns back the detected buildpack", func() {
    85  					Expect(app.CalculatedBuildpack()).To(Equal("bar"))
    86  				})
    87  			})
    88  
    89  			Context("neither buildpack nor detected buildpack are set", func() {
    90  				It("returns an empty string", func() {
    91  					Expect(app.CalculatedBuildpack()).To(BeEmpty())
    92  				})
    93  			})
    94  		})
    95  
    96  		Describe("CalculatedHealthCheckEndpoint", func() {
    97  			When("the health check type is http", func() {
    98  				BeforeEach(func() {
    99  					app.HealthCheckType = "http"
   100  					app.HealthCheckHTTPEndpoint = "/some-endpoint"
   101  				})
   102  
   103  				It("returns the endpoint field", func() {
   104  					Expect(app.CalculatedHealthCheckEndpoint()).To(Equal(
   105  						"/some-endpoint"))
   106  				})
   107  			})
   108  
   109  			When("the health check type is not http", func() {
   110  				BeforeEach(func() {
   111  					app.HealthCheckType = "process"
   112  					app.HealthCheckHTTPEndpoint = "/some-endpoint"
   113  				})
   114  
   115  				It("returns the empty string", func() {
   116  					Expect(app.CalculatedHealthCheckEndpoint()).To(Equal(""))
   117  				})
   118  			})
   119  		})
   120  
   121  		Describe("StagingCompleted", func() {
   122  			When("staging the application completes", func() {
   123  				It("returns true", func() {
   124  					app.PackageState = constant.ApplicationPackageStaged
   125  					Expect(app.StagingCompleted()).To(BeTrue())
   126  				})
   127  			})
   128  
   129  			When("the application is *not* staged", func() {
   130  				It("returns false", func() {
   131  					app.PackageState = constant.ApplicationPackageFailed
   132  					Expect(app.StagingCompleted()).To(BeFalse())
   133  				})
   134  			})
   135  		})
   136  
   137  		Describe("StagingFailed", func() {
   138  			When("staging the application fails", func() {
   139  				It("returns true", func() {
   140  					app.PackageState = constant.ApplicationPackageFailed
   141  					Expect(app.StagingFailed()).To(BeTrue())
   142  				})
   143  			})
   144  
   145  			When("staging the application does *not* fail", func() {
   146  				It("returns false", func() {
   147  					app.PackageState = constant.ApplicationPackageStaged
   148  					Expect(app.StagingFailed()).To(BeFalse())
   149  				})
   150  			})
   151  		})
   152  
   153  		Describe("StagingFailedMessage", func() {
   154  			When("the application has a staging failed description", func() {
   155  				BeforeEach(func() {
   156  					app.StagingFailedDescription = "An app was not successfully detected by any available buildpack"
   157  					app.StagingFailedReason = "NoAppDetectedError"
   158  				})
   159  				It("returns that description", func() {
   160  					Expect(app.StagingFailedMessage()).To(Equal("An app was not successfully detected by any available buildpack"))
   161  				})
   162  			})
   163  
   164  			When("the application does not have a staging failed description", func() {
   165  				BeforeEach(func() {
   166  					app.StagingFailedDescription = ""
   167  					app.StagingFailedReason = "NoAppDetectedError"
   168  				})
   169  				It("returns the staging failed code", func() {
   170  					Expect(app.StagingFailedMessage()).To(Equal("NoAppDetectedError"))
   171  				})
   172  			})
   173  		})
   174  
   175  		Describe("StagingFailedNoAppDetected", func() {
   176  			When("staging the application fails due to a no app detected error", func() {
   177  				It("returns true", func() {
   178  					app.StagingFailedReason = "NoAppDetectedError"
   179  					Expect(app.StagingFailedNoAppDetected()).To(BeTrue())
   180  				})
   181  			})
   182  
   183  			When("staging the application fails due to any other reason", func() {
   184  				It("returns false", func() {
   185  					app.StagingFailedReason = "InsufficientResources"
   186  					Expect(app.StagingFailedNoAppDetected()).To(BeFalse())
   187  				})
   188  			})
   189  		})
   190  
   191  		Describe("Started", func() {
   192  			When("app is started", func() {
   193  				It("returns true", func() {
   194  					Expect(Application{State: constant.ApplicationStarted}.Started()).To(BeTrue())
   195  				})
   196  			})
   197  
   198  			When("app is stopped", func() {
   199  				It("returns false", func() {
   200  					Expect(Application{State: constant.ApplicationStopped}.Started()).To(BeFalse())
   201  				})
   202  			})
   203  		})
   204  
   205  		Describe("Stopped", func() {
   206  			When("app is started", func() {
   207  				It("returns true", func() {
   208  					Expect(Application{State: constant.ApplicationStopped}.Stopped()).To(BeTrue())
   209  				})
   210  			})
   211  
   212  			When("app is stopped", func() {
   213  				It("returns false", func() {
   214  					Expect(Application{State: constant.ApplicationStarted}.Stopped()).To(BeFalse())
   215  				})
   216  			})
   217  		})
   218  	})
   219  
   220  	Describe("CreateApplication", func() {
   221  		When("the create is successful", func() {
   222  			var expectedApp ccv2.Application
   223  			BeforeEach(func() {
   224  				expectedApp = ccv2.Application{
   225  					GUID:      "some-app-guid",
   226  					Name:      "some-app-name",
   227  					SpaceGUID: "some-space-guid",
   228  				}
   229  				fakeCloudControllerClient.CreateApplicationReturns(expectedApp, ccv2.Warnings{"some-app-warning-1"}, nil)
   230  			})
   231  
   232  			It("creates and returns the application", func() {
   233  				newApp := Application{
   234  					Name:      "some-app-name",
   235  					SpaceGUID: "some-space-guid",
   236  				}
   237  				app, warnings, err := actor.CreateApplication(newApp)
   238  				Expect(err).ToNot(HaveOccurred())
   239  				Expect(warnings).To(ConsistOf("some-app-warning-1"))
   240  				Expect(app).To(Equal(Application(expectedApp)))
   241  
   242  				Expect(fakeCloudControllerClient.CreateApplicationCallCount()).To(Equal(1))
   243  				Expect(fakeCloudControllerClient.CreateApplicationArgsForCall(0)).To(Equal(ccv2.Application(newApp)))
   244  			})
   245  		})
   246  
   247  		When("the client returns back an error", func() {
   248  			var expectedErr error
   249  			BeforeEach(func() {
   250  				expectedErr = errors.New("some create app error")
   251  				fakeCloudControllerClient.CreateApplicationReturns(ccv2.Application{}, ccv2.Warnings{"some-app-warning-1"}, expectedErr)
   252  			})
   253  
   254  			It("returns warnings and an error", func() {
   255  				newApp := Application{
   256  					Name:      "some-app-name",
   257  					SpaceGUID: "some-space-guid",
   258  				}
   259  				_, warnings, err := actor.CreateApplication(newApp)
   260  				Expect(warnings).To(ConsistOf("some-app-warning-1"))
   261  				Expect(err).To(MatchError(expectedErr))
   262  			})
   263  		})
   264  	})
   265  
   266  	Describe("GetApplication", func() {
   267  		When("the application exists", func() {
   268  			BeforeEach(func() {
   269  				fakeCloudControllerClient.GetApplicationReturns(
   270  					ccv2.Application{
   271  						GUID: "some-app-guid",
   272  						Name: "some-app",
   273  					},
   274  					ccv2.Warnings{"foo"},
   275  					nil,
   276  				)
   277  			})
   278  
   279  			It("returns the application and warnings", func() {
   280  				app, warnings, err := actor.GetApplication("some-app-guid")
   281  				Expect(err).ToNot(HaveOccurred())
   282  				Expect(app).To(Equal(Application{
   283  					GUID: "some-app-guid",
   284  					Name: "some-app",
   285  				}))
   286  				Expect(warnings).To(Equal(Warnings{"foo"}))
   287  
   288  				Expect(fakeCloudControllerClient.GetApplicationCallCount()).To(Equal(1))
   289  				Expect(fakeCloudControllerClient.GetApplicationArgsForCall(0)).To(Equal("some-app-guid"))
   290  			})
   291  		})
   292  
   293  		When("the application does not exist", func() {
   294  			BeforeEach(func() {
   295  				fakeCloudControllerClient.GetApplicationReturns(ccv2.Application{}, nil, ccerror.ResourceNotFoundError{})
   296  			})
   297  
   298  			It("returns an ApplicationNotFoundError", func() {
   299  				_, _, err := actor.GetApplication("some-app-guid")
   300  				Expect(err).To(MatchError(actionerror.ApplicationNotFoundError{GUID: "some-app-guid"}))
   301  			})
   302  		})
   303  	})
   304  
   305  	Describe("GetApplicationByNameAndSpace", func() {
   306  		When("the application exists", func() {
   307  			BeforeEach(func() {
   308  				fakeCloudControllerClient.GetApplicationsReturns(
   309  					[]ccv2.Application{
   310  						{
   311  							GUID: "some-app-guid",
   312  							Name: "some-app",
   313  						},
   314  					},
   315  					ccv2.Warnings{"foo"},
   316  					nil,
   317  				)
   318  			})
   319  
   320  			It("returns the application and warnings", func() {
   321  				app, warnings, err := actor.GetApplicationByNameAndSpace("some-app", "some-space-guid")
   322  				Expect(err).ToNot(HaveOccurred())
   323  				Expect(app).To(Equal(Application{
   324  					GUID: "some-app-guid",
   325  					Name: "some-app",
   326  				}))
   327  				Expect(warnings).To(Equal(Warnings{"foo"}))
   328  
   329  				Expect(fakeCloudControllerClient.GetApplicationsCallCount()).To(Equal(1))
   330  				Expect(fakeCloudControllerClient.GetApplicationsArgsForCall(0)).To(ConsistOf([]ccv2.Filter{
   331  					ccv2.Filter{
   332  						Type:     constant.NameFilter,
   333  						Operator: constant.EqualOperator,
   334  						Values:   []string{"some-app"},
   335  					},
   336  					ccv2.Filter{
   337  						Type:     constant.SpaceGUIDFilter,
   338  						Operator: constant.EqualOperator,
   339  						Values:   []string{"some-space-guid"},
   340  					},
   341  				}))
   342  			})
   343  		})
   344  
   345  		When("the application does not exists", func() {
   346  			BeforeEach(func() {
   347  				fakeCloudControllerClient.GetApplicationsReturns([]ccv2.Application{}, nil, nil)
   348  			})
   349  
   350  			It("returns an ApplicationNotFoundError", func() {
   351  				_, _, err := actor.GetApplicationByNameAndSpace("some-app", "some-space-guid")
   352  				Expect(err).To(MatchError(actionerror.ApplicationNotFoundError{Name: "some-app"}))
   353  			})
   354  		})
   355  
   356  		When("the cloud controller client returns an error", func() {
   357  			var expectedError error
   358  
   359  			BeforeEach(func() {
   360  				expectedError = errors.New("I am a CloudControllerClient Error")
   361  				fakeCloudControllerClient.GetApplicationsReturns([]ccv2.Application{}, nil, expectedError)
   362  			})
   363  
   364  			It("returns the error", func() {
   365  				_, _, err := actor.GetApplicationByNameAndSpace("some-app", "some-space-guid")
   366  				Expect(err).To(MatchError(expectedError))
   367  			})
   368  		})
   369  	})
   370  
   371  	Describe("GetApplicationsBySpace", func() {
   372  		When("the there are applications in the space", func() {
   373  			BeforeEach(func() {
   374  				fakeCloudControllerClient.GetApplicationsReturns(
   375  					[]ccv2.Application{
   376  						{
   377  							GUID: "some-app-guid-1",
   378  							Name: "some-app-1",
   379  						},
   380  						{
   381  							GUID: "some-app-guid-2",
   382  							Name: "some-app-2",
   383  						},
   384  					},
   385  					ccv2.Warnings{"warning-1", "warning-2"},
   386  					nil,
   387  				)
   388  			})
   389  
   390  			It("returns the application and warnings", func() {
   391  				apps, warnings, err := actor.GetApplicationsBySpace("some-space-guid")
   392  				Expect(err).ToNot(HaveOccurred())
   393  				Expect(apps).To(ConsistOf(
   394  					Application{
   395  						GUID: "some-app-guid-1",
   396  						Name: "some-app-1",
   397  					},
   398  					Application{
   399  						GUID: "some-app-guid-2",
   400  						Name: "some-app-2",
   401  					},
   402  				))
   403  				Expect(warnings).To(ConsistOf("warning-1", "warning-2"))
   404  
   405  				Expect(fakeCloudControllerClient.GetApplicationsCallCount()).To(Equal(1))
   406  				Expect(fakeCloudControllerClient.GetApplicationsArgsForCall(0)).To(ConsistOf([]ccv2.Filter{
   407  					ccv2.Filter{
   408  						Type:     constant.SpaceGUIDFilter,
   409  						Operator: constant.EqualOperator,
   410  						Values:   []string{"some-space-guid"},
   411  					},
   412  				}))
   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("some cc error")
   421  				fakeCloudControllerClient.GetApplicationsReturns(
   422  					[]ccv2.Application{},
   423  					ccv2.Warnings{"warning-1", "warning-2"},
   424  					expectedError)
   425  			})
   426  
   427  			It("returns the error and warnings", func() {
   428  				_, warnings, err := actor.GetApplicationsBySpace("some-space-guid")
   429  				Expect(warnings).To(ConsistOf("warning-1", "warning-2"))
   430  				Expect(err).To(MatchError(expectedError))
   431  			})
   432  		})
   433  	})
   434  
   435  	Describe("GetRouteApplications", func() {
   436  		When("the CC client returns no errors", func() {
   437  			BeforeEach(func() {
   438  				fakeCloudControllerClient.GetRouteApplicationsReturns(
   439  					[]ccv2.Application{
   440  						{
   441  							GUID: "application-guid",
   442  							Name: "application-name",
   443  						},
   444  					}, ccv2.Warnings{"route-applications-warning"}, nil)
   445  			})
   446  			It("returns the applications bound to the route and warnings", func() {
   447  				applications, warnings, err := actor.GetRouteApplications("route-guid")
   448  				Expect(fakeCloudControllerClient.GetRouteApplicationsCallCount()).To(Equal(1))
   449  				Expect(fakeCloudControllerClient.GetRouteApplicationsArgsForCall(0)).To(Equal("route-guid"))
   450  
   451  				Expect(err).ToNot(HaveOccurred())
   452  				Expect(warnings).To(ConsistOf("route-applications-warning"))
   453  				Expect(applications).To(ConsistOf(
   454  					Application{
   455  						GUID: "application-guid",
   456  						Name: "application-name",
   457  					},
   458  				))
   459  			})
   460  		})
   461  
   462  		When("the CC client returns an error", func() {
   463  			BeforeEach(func() {
   464  				fakeCloudControllerClient.GetRouteApplicationsReturns(
   465  					[]ccv2.Application{}, ccv2.Warnings{"route-applications-warning"}, errors.New("get-route-applications-error"))
   466  			})
   467  
   468  			It("returns the error and warnings", func() {
   469  				apps, warnings, err := actor.GetRouteApplications("route-guid")
   470  				Expect(fakeCloudControllerClient.GetRouteApplicationsCallCount()).To(Equal(1))
   471  				Expect(fakeCloudControllerClient.GetRouteApplicationsArgsForCall(0)).To(Equal("route-guid"))
   472  
   473  				Expect(err).To(MatchError("get-route-applications-error"))
   474  				Expect(warnings).To(ConsistOf("route-applications-warning"))
   475  				Expect(apps).To(BeNil())
   476  			})
   477  		})
   478  	})
   479  
   480  	Describe("SetApplicationHealthCheckTypeByNameAndSpace", func() {
   481  		When("setting an http endpoint with a health check that is not http", func() {
   482  			It("returns an http health check invalid error", func() {
   483  				_, _, err := actor.SetApplicationHealthCheckTypeByNameAndSpace(
   484  					"some-app", "some-space-guid", "some-health-check-type", "/foo")
   485  				Expect(err).To(MatchError(actionerror.HTTPHealthCheckInvalidError{}))
   486  			})
   487  		})
   488  
   489  		When("the app exists", func() {
   490  			When("the desired health check type is different", func() {
   491  				BeforeEach(func() {
   492  					fakeCloudControllerClient.GetApplicationsReturns(
   493  						[]ccv2.Application{
   494  							{GUID: "some-app-guid"},
   495  						},
   496  						ccv2.Warnings{"get application warning"},
   497  						nil,
   498  					)
   499  					fakeCloudControllerClient.UpdateApplicationReturns(
   500  						ccv2.Application{
   501  							GUID:            "some-app-guid",
   502  							HealthCheckType: "process",
   503  						},
   504  						ccv2.Warnings{"update warnings"},
   505  						nil,
   506  					)
   507  				})
   508  
   509  				It("sets the desired health check type and returns the warnings", func() {
   510  					returnedApp, warnings, err := actor.SetApplicationHealthCheckTypeByNameAndSpace(
   511  						"some-app", "some-space-guid", "process", "/")
   512  					Expect(err).ToNot(HaveOccurred())
   513  					Expect(warnings).To(ConsistOf("get application warning", "update warnings"))
   514  
   515  					Expect(returnedApp).To(Equal(Application{
   516  						GUID:            "some-app-guid",
   517  						HealthCheckType: "process",
   518  					}))
   519  
   520  					Expect(fakeCloudControllerClient.UpdateApplicationCallCount()).To(Equal(1))
   521  					app := fakeCloudControllerClient.UpdateApplicationArgsForCall(0)
   522  					Expect(app).To(Equal(ccv2.Application{
   523  						GUID:            "some-app-guid",
   524  						HealthCheckType: "process",
   525  					}))
   526  				})
   527  			})
   528  
   529  			When("the desired health check type is 'http'", func() {
   530  				When("the desired http endpoint is already set", func() {
   531  					BeforeEach(func() {
   532  						fakeCloudControllerClient.GetApplicationsReturns(
   533  							[]ccv2.Application{
   534  								{GUID: "some-app-guid", HealthCheckType: "http", HealthCheckHTTPEndpoint: "/"},
   535  							},
   536  							ccv2.Warnings{"get application warning"},
   537  							nil,
   538  						)
   539  					})
   540  
   541  					It("does not send the update", func() {
   542  						_, warnings, err := actor.SetApplicationHealthCheckTypeByNameAndSpace(
   543  							"some-app", "some-space-guid", "http", "/")
   544  						Expect(err).ToNot(HaveOccurred())
   545  						Expect(warnings).To(ConsistOf("get application warning"))
   546  
   547  						Expect(fakeCloudControllerClient.UpdateApplicationCallCount()).To(Equal(0))
   548  					})
   549  				})
   550  
   551  				When("the desired http endpoint is not set", func() {
   552  					BeforeEach(func() {
   553  						fakeCloudControllerClient.GetApplicationsReturns(
   554  							[]ccv2.Application{
   555  								{GUID: "some-app-guid", HealthCheckType: "http", HealthCheckHTTPEndpoint: "/"},
   556  							},
   557  							ccv2.Warnings{"get application warning"},
   558  							nil,
   559  						)
   560  						fakeCloudControllerClient.UpdateApplicationReturns(
   561  							ccv2.Application{},
   562  							ccv2.Warnings{"update warnings"},
   563  							nil,
   564  						)
   565  					})
   566  
   567  					It("sets the desired health check type and returns the warnings", func() {
   568  						_, warnings, err := actor.SetApplicationHealthCheckTypeByNameAndSpace(
   569  							"some-app", "some-space-guid", "http", "/v2/anything")
   570  						Expect(err).ToNot(HaveOccurred())
   571  
   572  						Expect(fakeCloudControllerClient.UpdateApplicationCallCount()).To(Equal(1))
   573  						app := fakeCloudControllerClient.UpdateApplicationArgsForCall(0)
   574  						Expect(app).To(Equal(ccv2.Application{
   575  							GUID:                    "some-app-guid",
   576  							HealthCheckType:         "http",
   577  							HealthCheckHTTPEndpoint: "/v2/anything",
   578  						}))
   579  
   580  						Expect(warnings).To(ConsistOf("get application warning", "update warnings"))
   581  					})
   582  				})
   583  			})
   584  
   585  			When("the application health check type is already set to the desired type", func() {
   586  				BeforeEach(func() {
   587  					fakeCloudControllerClient.GetApplicationsReturns(
   588  						[]ccv2.Application{
   589  							{
   590  								GUID:            "some-app-guid",
   591  								HealthCheckType: "process",
   592  							},
   593  						},
   594  						ccv2.Warnings{"get application warning"},
   595  						nil,
   596  					)
   597  				})
   598  
   599  				It("does not update the health check type", func() {
   600  					returnedApp, warnings, err := actor.SetApplicationHealthCheckTypeByNameAndSpace(
   601  						"some-app", "some-space-guid", "process", "/")
   602  					Expect(err).ToNot(HaveOccurred())
   603  					Expect(warnings).To(ConsistOf("get application warning"))
   604  					Expect(returnedApp).To(Equal(Application{
   605  						GUID:            "some-app-guid",
   606  						HealthCheckType: "process",
   607  					}))
   608  
   609  					Expect(fakeCloudControllerClient.UpdateApplicationCallCount()).To(Equal(0))
   610  				})
   611  			})
   612  		})
   613  
   614  		When("getting the application returns an error", func() {
   615  			BeforeEach(func() {
   616  				fakeCloudControllerClient.GetApplicationsReturns(
   617  					[]ccv2.Application{}, ccv2.Warnings{"get application warning"}, errors.New("get application error"))
   618  			})
   619  
   620  			It("returns the error and warnings", func() {
   621  				_, warnings, err := actor.SetApplicationHealthCheckTypeByNameAndSpace(
   622  					"some-app", "some-space-guid", "process", "/")
   623  
   624  				Expect(warnings).To(ConsistOf("get application warning"))
   625  				Expect(err).To(MatchError("get application error"))
   626  			})
   627  		})
   628  
   629  		When("updating the application returns an error", func() {
   630  			var expectedErr error
   631  
   632  			BeforeEach(func() {
   633  				expectedErr = errors.New("foo bar")
   634  				fakeCloudControllerClient.GetApplicationsReturns(
   635  					[]ccv2.Application{
   636  						{GUID: "some-app-guid"},
   637  					},
   638  					ccv2.Warnings{"get application warning"},
   639  					nil,
   640  				)
   641  				fakeCloudControllerClient.UpdateApplicationReturns(
   642  					ccv2.Application{},
   643  					ccv2.Warnings{"update warnings"},
   644  					expectedErr,
   645  				)
   646  			})
   647  
   648  			It("returns the error and warnings", func() {
   649  				_, warnings, err := actor.SetApplicationHealthCheckTypeByNameAndSpace(
   650  					"some-app", "some-space-guid", "process", "/")
   651  				Expect(err).To(MatchError(expectedErr))
   652  				Expect(warnings).To(ConsistOf("get application warning", "update warnings"))
   653  			})
   654  		})
   655  	})
   656  
   657  	Describe("StartApplication/RestartApplication", func() {
   658  		var (
   659  			app            Application
   660  			fakeNOAAClient *v2actionfakes.FakeNOAAClient
   661  
   662  			messages <-chan *LogMessage
   663  			logErrs  <-chan error
   664  			appState <-chan ApplicationStateChange
   665  			warnings <-chan string
   666  			errs     <-chan error
   667  
   668  			eventStream chan *events.LogMessage
   669  			errStream   chan error
   670  		)
   671  
   672  		BeforeEach(func() {
   673  			fakeConfig.StagingTimeoutReturns(time.Minute)
   674  			fakeConfig.StartupTimeoutReturns(time.Minute)
   675  
   676  			app = Application{
   677  				GUID:      "some-app-guid",
   678  				Name:      "some-app",
   679  				Instances: types.NullInt{Value: 2, IsSet: true},
   680  			}
   681  
   682  			fakeNOAAClient = new(v2actionfakes.FakeNOAAClient)
   683  			fakeNOAAClient.TailingLogsStub = func(_ string, _ string) (<-chan *events.LogMessage, <-chan error) {
   684  				eventStream = make(chan *events.LogMessage)
   685  				errStream = make(chan error)
   686  				return eventStream, errStream
   687  			}
   688  
   689  			closed := false
   690  			fakeNOAAClient.CloseStub = func() error {
   691  				if !closed {
   692  					closed = true
   693  					close(errStream)
   694  					close(eventStream)
   695  				}
   696  				return nil
   697  			}
   698  
   699  			appCount := 0
   700  			fakeCloudControllerClient.GetApplicationStub = func(appGUID string) (ccv2.Application, ccv2.Warnings, error) {
   701  				if appCount == 0 {
   702  					appCount++
   703  					return ccv2.Application{
   704  						GUID:         "some-app-guid",
   705  						Instances:    types.NullInt{Value: 2, IsSet: true},
   706  						Name:         "some-app",
   707  						PackageState: constant.ApplicationPackagePending,
   708  					}, ccv2.Warnings{"app-warnings-1"}, nil
   709  				}
   710  
   711  				return ccv2.Application{
   712  					GUID:         "some-app-guid",
   713  					Name:         "some-app",
   714  					Instances:    types.NullInt{Value: 2, IsSet: true},
   715  					PackageState: constant.ApplicationPackageStaged,
   716  				}, ccv2.Warnings{"app-warnings-2"}, nil
   717  			}
   718  
   719  			instanceCount := 0
   720  			fakeCloudControllerClient.GetApplicationApplicationInstancesStub = func(guid string) (map[int]ccv2.ApplicationInstance, ccv2.Warnings, error) {
   721  				if instanceCount == 0 {
   722  					instanceCount++
   723  					return map[int]ccv2.ApplicationInstance{
   724  						0: {State: constant.ApplicationInstanceStarting},
   725  						1: {State: constant.ApplicationInstanceStarting},
   726  					}, ccv2.Warnings{"app-instance-warnings-1"}, nil
   727  				}
   728  
   729  				return map[int]ccv2.ApplicationInstance{
   730  					0: {State: constant.ApplicationInstanceStarting},
   731  					1: {State: constant.ApplicationInstanceRunning},
   732  				}, ccv2.Warnings{"app-instance-warnings-2"}, nil
   733  			}
   734  		})
   735  
   736  		AfterEach(func() {
   737  			Eventually(messages).Should(BeClosed())
   738  			Eventually(logErrs).Should(BeClosed())
   739  			Eventually(appState).Should(BeClosed())
   740  			Eventually(warnings).Should(BeClosed())
   741  			Eventually(errs).Should(BeClosed())
   742  		})
   743  
   744  		var ItHandlesStagingIssues = func() {
   745  			Context("staging issues", func() {
   746  				When("polling fails", func() {
   747  					var expectedErr error
   748  					BeforeEach(func() {
   749  						expectedErr = errors.New("I am a banana!!!!")
   750  						fakeCloudControllerClient.GetApplicationStub = func(appGUID string) (ccv2.Application, ccv2.Warnings, error) {
   751  							return ccv2.Application{}, ccv2.Warnings{"app-warnings-1"}, expectedErr
   752  						}
   753  					})
   754  
   755  					It("sends the error and stops polling", func() {
   756  						Eventually(appState).Should(Receive(Equal(ApplicationStateStaging)))
   757  						Eventually(warnings).Should(Receive(Equal("state-warning")))
   758  						Eventually(warnings).Should(Receive(Equal("app-warnings-1")))
   759  						Eventually(errs).Should(Receive(MatchError(expectedErr)))
   760  
   761  						Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(0))
   762  						Expect(fakeCloudControllerClient.GetApplicationCallCount()).To(Equal(1))
   763  						Expect(fakeCloudControllerClient.GetApplicationApplicationInstancesCallCount()).To(Equal(0))
   764  					})
   765  				})
   766  
   767  				When("the application fails to stage", func() {
   768  					Context("due to a NoAppDetectedError", func() {
   769  						BeforeEach(func() {
   770  							fakeCloudControllerClient.GetApplicationStub = func(appGUID string) (ccv2.Application, ccv2.Warnings, error) {
   771  								return ccv2.Application{
   772  									GUID:                "some-app-guid",
   773  									Name:                "some-app",
   774  									Instances:           types.NullInt{Value: 2, IsSet: true},
   775  									PackageState:        constant.ApplicationPackageFailed,
   776  									StagingFailedReason: "NoAppDetectedError",
   777  								}, ccv2.Warnings{"app-warnings-1"}, nil
   778  							}
   779  						})
   780  
   781  						It("sends a StagingFailedNoAppDetectedError and stops polling", func() {
   782  							Eventually(appState).Should(Receive(Equal(ApplicationStateStaging)))
   783  							Eventually(warnings).Should(Receive(Equal("state-warning")))
   784  							Eventually(warnings).Should(Receive(Equal("app-warnings-1")))
   785  							Eventually(errs).Should(Receive(MatchError(actionerror.StagingFailedNoAppDetectedError{Reason: "NoAppDetectedError"})))
   786  
   787  							Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(0))
   788  							Expect(fakeConfig.StagingTimeoutCallCount()).To(Equal(1))
   789  							Expect(fakeCloudControllerClient.GetApplicationCallCount()).To(Equal(1))
   790  							Expect(fakeCloudControllerClient.GetApplicationApplicationInstancesCallCount()).To(Equal(0))
   791  						})
   792  					})
   793  
   794  					Context("due to any other error", func() {
   795  						BeforeEach(func() {
   796  							fakeCloudControllerClient.GetApplicationStub = func(appGUID string) (ccv2.Application, ccv2.Warnings, error) {
   797  								return ccv2.Application{
   798  									GUID:                "some-app-guid",
   799  									Name:                "some-app",
   800  									Instances:           types.NullInt{Value: 2, IsSet: true},
   801  									PackageState:        constant.ApplicationPackageFailed,
   802  									StagingFailedReason: "OhNoes",
   803  								}, ccv2.Warnings{"app-warnings-1"}, nil
   804  							}
   805  						})
   806  
   807  						It("sends a StagingFailedError and stops polling", func() {
   808  							Eventually(appState).Should(Receive(Equal(ApplicationStateStaging)))
   809  							Eventually(warnings).Should(Receive(Equal("state-warning")))
   810  							Eventually(warnings).Should(Receive(Equal("app-warnings-1")))
   811  							Eventually(errs).Should(Receive(MatchError(actionerror.StagingFailedError{Reason: "OhNoes"})))
   812  
   813  							Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(0))
   814  							Expect(fakeConfig.StagingTimeoutCallCount()).To(Equal(1))
   815  							Expect(fakeCloudControllerClient.GetApplicationCallCount()).To(Equal(1))
   816  							Expect(fakeCloudControllerClient.GetApplicationApplicationInstancesCallCount()).To(Equal(0))
   817  						})
   818  					})
   819  				})
   820  
   821  				When("the application takes too long to stage", func() {
   822  					BeforeEach(func() {
   823  						fakeConfig.StagingTimeoutReturns(0)
   824  						fakeCloudControllerClient.GetApplicationApplicationInstancesStub = nil
   825  					})
   826  
   827  					It("sends a timeout error and stops polling", func() {
   828  						Eventually(appState).Should(Receive(Equal(ApplicationStateStaging)))
   829  						Eventually(warnings).Should(Receive(Equal("state-warning")))
   830  						Eventually(errs).Should(Receive(MatchError(actionerror.StagingTimeoutError{AppName: "some-app", Timeout: 0})))
   831  
   832  						Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(0))
   833  						Expect(fakeConfig.StagingTimeoutCallCount()).To(Equal(2))
   834  						Expect(fakeCloudControllerClient.GetApplicationCallCount()).To(Equal(0))
   835  						Expect(fakeCloudControllerClient.GetApplicationApplicationInstancesCallCount()).To(Equal(0))
   836  					})
   837  				})
   838  			})
   839  		}
   840  
   841  		var ItHandlesStartingIssues = func() {
   842  			Context("starting issues", func() {
   843  				When("polling fails", func() {
   844  					var expectedErr error
   845  					BeforeEach(func() {
   846  						expectedErr = errors.New("I am a banana!!!!")
   847  						fakeCloudControllerClient.GetApplicationApplicationInstancesStub = func(guid string) (map[int]ccv2.ApplicationInstance, ccv2.Warnings, error) {
   848  							return nil, ccv2.Warnings{"app-instance-warnings-1"}, expectedErr
   849  						}
   850  					})
   851  
   852  					It("sends the error and stops polling", func() {
   853  						Eventually(appState).Should(Receive(Equal(ApplicationStateStaging)))
   854  						Eventually(warnings).Should(Receive(Equal("state-warning")))
   855  						Eventually(warnings).Should(Receive(Equal("app-warnings-1")))
   856  						Eventually(warnings).Should(Receive(Equal("app-warnings-2")))
   857  						Eventually(appState).Should(Receive(Equal(ApplicationStateStarting)))
   858  						Eventually(warnings).Should(Receive(Equal("app-instance-warnings-1")))
   859  						Eventually(errs).Should(Receive(MatchError(expectedErr)))
   860  
   861  						Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(1))
   862  						Expect(fakeCloudControllerClient.GetApplicationApplicationInstancesCallCount()).To(Equal(1))
   863  					})
   864  				})
   865  
   866  				When("the application takes too long to start", func() {
   867  					BeforeEach(func() {
   868  						fakeConfig.StartupTimeoutReturns(0)
   869  					})
   870  
   871  					It("sends a timeout error and stops polling", func() {
   872  						Eventually(appState).Should(Receive(Equal(ApplicationStateStaging)))
   873  						Eventually(warnings).Should(Receive(Equal("state-warning")))
   874  						Eventually(warnings).Should(Receive(Equal("app-warnings-1")))
   875  						Eventually(warnings).Should(Receive(Equal("app-warnings-2")))
   876  						Eventually(appState).Should(Receive(Equal(ApplicationStateStarting)))
   877  						Eventually(errs).Should(Receive(MatchError(actionerror.StartupTimeoutError{Name: "some-app"})))
   878  
   879  						Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(1))
   880  						Expect(fakeConfig.StartupTimeoutCallCount()).To(Equal(1))
   881  						Expect(fakeCloudControllerClient.GetApplicationApplicationInstancesCallCount()).To(Equal(0))
   882  					})
   883  				})
   884  
   885  				When("the application crashes", func() {
   886  					BeforeEach(func() {
   887  						fakeCloudControllerClient.GetApplicationApplicationInstancesStub = func(guid string) (map[int]ccv2.ApplicationInstance, ccv2.Warnings, error) {
   888  							return map[int]ccv2.ApplicationInstance{
   889  								0: {State: constant.ApplicationInstanceCrashed},
   890  							}, ccv2.Warnings{"app-instance-warnings-1"}, nil
   891  						}
   892  					})
   893  
   894  					It("returns an ApplicationInstanceCrashedError and stops polling", func() {
   895  						Eventually(appState).Should(Receive(Equal(ApplicationStateStaging)))
   896  						Eventually(warnings).Should(Receive(Equal("state-warning")))
   897  						Eventually(warnings).Should(Receive(Equal("app-warnings-1")))
   898  						Eventually(warnings).Should(Receive(Equal("app-warnings-2")))
   899  						Eventually(appState).Should(Receive(Equal(ApplicationStateStarting)))
   900  						Eventually(warnings).Should(Receive(Equal("app-instance-warnings-1")))
   901  						Eventually(errs).Should(Receive(MatchError(actionerror.ApplicationInstanceCrashedError{Name: "some-app"})))
   902  
   903  						Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(1))
   904  						Expect(fakeConfig.StartupTimeoutCallCount()).To(Equal(1))
   905  						Expect(fakeCloudControllerClient.GetApplicationApplicationInstancesCallCount()).To(Equal(1))
   906  					})
   907  				})
   908  
   909  				When("the application flaps", func() {
   910  					BeforeEach(func() {
   911  						fakeCloudControllerClient.GetApplicationApplicationInstancesStub = func(guid string) (map[int]ccv2.ApplicationInstance, ccv2.Warnings, error) {
   912  							return map[int]ccv2.ApplicationInstance{
   913  								0: {State: constant.ApplicationInstanceFlapping},
   914  							}, ccv2.Warnings{"app-instance-warnings-1"}, nil
   915  						}
   916  					})
   917  
   918  					It("returns an ApplicationInstanceFlappingError and stops polling", func() {
   919  						Eventually(appState).Should(Receive(Equal(ApplicationStateStaging)))
   920  						Eventually(warnings).Should(Receive(Equal("state-warning")))
   921  						Eventually(warnings).Should(Receive(Equal("app-warnings-1")))
   922  						Eventually(warnings).Should(Receive(Equal("app-warnings-2")))
   923  						Eventually(appState).Should(Receive(Equal(ApplicationStateStarting)))
   924  						Eventually(warnings).Should(Receive(Equal("app-instance-warnings-1")))
   925  						Eventually(errs).Should(Receive(MatchError(actionerror.ApplicationInstanceFlappingError{Name: "some-app"})))
   926  
   927  						Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(1))
   928  						Expect(fakeConfig.StartupTimeoutCallCount()).To(Equal(1))
   929  						Expect(fakeCloudControllerClient.GetApplicationApplicationInstancesCallCount()).To(Equal(1))
   930  					})
   931  				})
   932  			})
   933  		}
   934  
   935  		var ItStartsApplication = func() {
   936  			When("the app is not running", func() {
   937  				It("starts and polls for an app instance", func() {
   938  					Eventually(appState).Should(Receive(Equal(ApplicationStateStaging)))
   939  					Eventually(warnings).Should(Receive(Equal("state-warning")))
   940  					Eventually(warnings).Should(Receive(Equal("app-warnings-1")))
   941  					Eventually(warnings).Should(Receive(Equal("app-warnings-2")))
   942  					Eventually(appState).Should(Receive(Equal(ApplicationStateStarting)))
   943  					Eventually(warnings).Should(Receive(Equal("app-instance-warnings-1")))
   944  					Eventually(warnings).Should(Receive(Equal("app-instance-warnings-2")))
   945  
   946  					Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(2))
   947  
   948  					Expect(fakeCloudControllerClient.UpdateApplicationCallCount()).To(Equal(1))
   949  					passedApp := fakeCloudControllerClient.UpdateApplicationArgsForCall(0)
   950  					Expect(passedApp).To(Equal(ccv2.Application{
   951  						GUID:  "some-app-guid",
   952  						State: constant.ApplicationStarted,
   953  					}))
   954  
   955  					Expect(fakeCloudControllerClient.GetApplicationCallCount()).To(Equal(2))
   956  					Expect(fakeCloudControllerClient.GetApplicationApplicationInstancesCallCount()).To(Equal(2))
   957  					Eventually(fakeNOAAClient.CloseCallCount).Should(Equal(2))
   958  				})
   959  			})
   960  
   961  			When("the app has zero instances", func() {
   962  				BeforeEach(func() {
   963  					fakeCloudControllerClient.UpdateApplicationReturns(ccv2.Application{GUID: "some-app-guid",
   964  						Instances: types.NullInt{Value: 0, IsSet: true},
   965  						Name:      "some-app",
   966  					}, ccv2.Warnings{"state-warning"}, nil)
   967  				})
   968  
   969  				It("starts and only polls for staging to finish", func() {
   970  					Eventually(appState).Should(Receive(Equal(ApplicationStateStaging)))
   971  					Eventually(warnings).Should(Receive(Equal("state-warning")))
   972  					Eventually(warnings).Should(Receive(Equal("app-warnings-1")))
   973  					Eventually(warnings).Should(Receive(Equal("app-warnings-2")))
   974  					Consistently(appState).ShouldNot(Receive(Equal(ApplicationStateStarting)))
   975  
   976  					Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(1))
   977  
   978  					Expect(fakeCloudControllerClient.UpdateApplicationCallCount()).To(Equal(1))
   979  					passedApp := fakeCloudControllerClient.UpdateApplicationArgsForCall(0)
   980  					Expect(passedApp).To(Equal(ccv2.Application{
   981  						GUID:  "some-app-guid",
   982  						State: constant.ApplicationStarted,
   983  					}))
   984  
   985  					Expect(fakeCloudControllerClient.GetApplicationCallCount()).To(Equal(2))
   986  					Expect(fakeCloudControllerClient.GetApplicationApplicationInstancesCallCount()).To(Equal(0))
   987  				})
   988  			})
   989  
   990  			When("updating the application fails", func() {
   991  				var expectedErr error
   992  				BeforeEach(func() {
   993  					expectedErr = errors.New("I am a banana!!!!")
   994  					fakeCloudControllerClient.UpdateApplicationReturns(ccv2.Application{}, ccv2.Warnings{"state-warning"}, expectedErr)
   995  				})
   996  
   997  				It("sends the update error and never polls", func() {
   998  					Eventually(appState).Should(Receive(Equal(ApplicationStateStaging)))
   999  					Eventually(warnings).Should(Receive(Equal("state-warning")))
  1000  					Eventually(errs).Should(Receive(MatchError(expectedErr)))
  1001  
  1002  					Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(0))
  1003  					Expect(fakeCloudControllerClient.GetApplicationCallCount()).To(Equal(0))
  1004  					Expect(fakeCloudControllerClient.GetApplicationApplicationInstancesCallCount()).To(Equal(0))
  1005  				})
  1006  			})
  1007  
  1008  			ItHandlesStagingIssues()
  1009  
  1010  			ItHandlesStartingIssues()
  1011  		}
  1012  
  1013  		Describe("StartApplication", func() {
  1014  			BeforeEach(func() {
  1015  				fakeCloudControllerClient.UpdateApplicationReturns(ccv2.Application{GUID: "some-app-guid",
  1016  					Instances: types.NullInt{Value: 2, IsSet: true},
  1017  					Name:      "some-app",
  1018  				}, ccv2.Warnings{"state-warning"}, nil)
  1019  			})
  1020  
  1021  			JustBeforeEach(func() {
  1022  				messages, logErrs, appState, warnings, errs = actor.StartApplication(app, fakeNOAAClient)
  1023  			})
  1024  
  1025  			When("the app is already staged", func() {
  1026  				BeforeEach(func() {
  1027  					app.PackageState = constant.ApplicationPackageStaged
  1028  				})
  1029  
  1030  				It("does not send ApplicationStateStaging", func() {
  1031  					Consistently(appState).ShouldNot(Receive(Equal(ApplicationStateStaging)))
  1032  					Eventually(warnings).Should(Receive(Equal("state-warning")))
  1033  					Eventually(warnings).Should(Receive(Equal("app-warnings-1")))
  1034  					Eventually(warnings).Should(Receive(Equal("app-warnings-2")))
  1035  					Eventually(appState).Should(Receive(Equal(ApplicationStateStarting)))
  1036  					Eventually(warnings).Should(Receive(Equal("app-instance-warnings-1")))
  1037  					Eventually(warnings).Should(Receive(Equal("app-instance-warnings-2")))
  1038  
  1039  					Expect(fakeCloudControllerClient.UpdateApplicationCallCount()).To(Equal(1))
  1040  					passedApp := fakeCloudControllerClient.UpdateApplicationArgsForCall(0)
  1041  					Expect(passedApp).To(Equal(ccv2.Application{
  1042  						GUID:  "some-app-guid",
  1043  						State: constant.ApplicationStarted,
  1044  					}))
  1045  				})
  1046  			})
  1047  
  1048  			ItStartsApplication()
  1049  		})
  1050  
  1051  		Describe("RestartApplication", func() {
  1052  			BeforeEach(func() {
  1053  				fakeCloudControllerClient.UpdateApplicationReturns(ccv2.Application{GUID: "some-app-guid",
  1054  					Instances: types.NullInt{Value: 2, IsSet: true},
  1055  					Name:      "some-app",
  1056  				}, ccv2.Warnings{"state-warning"}, nil)
  1057  			})
  1058  
  1059  			JustBeforeEach(func() {
  1060  				messages, logErrs, appState, warnings, errs = actor.RestartApplication(app, fakeNOAAClient)
  1061  			})
  1062  
  1063  			When("application is running", func() {
  1064  				BeforeEach(func() {
  1065  					app.State = constant.ApplicationStarted
  1066  				})
  1067  
  1068  				It("stops, starts and polls for an app instance", func() {
  1069  					Eventually(appState).Should(Receive(Equal(ApplicationStateStopping)))
  1070  					Eventually(warnings).Should(Receive(Equal("state-warning")))
  1071  					Eventually(appState).Should(Receive(Equal(ApplicationStateStaging)))
  1072  					Eventually(warnings).Should(Receive(Equal("state-warning")))
  1073  					Eventually(warnings).Should(Receive(Equal("app-warnings-1")))
  1074  					Eventually(warnings).Should(Receive(Equal("app-warnings-2")))
  1075  					Eventually(appState).Should(Receive(Equal(ApplicationStateStarting)))
  1076  					Eventually(warnings).Should(Receive(Equal("app-instance-warnings-1")))
  1077  					Eventually(warnings).Should(Receive(Equal("app-instance-warnings-2")))
  1078  
  1079  					Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(2))
  1080  
  1081  					Expect(fakeCloudControllerClient.UpdateApplicationCallCount()).To(Equal(2))
  1082  					passedApp := fakeCloudControllerClient.UpdateApplicationArgsForCall(0)
  1083  					Expect(passedApp).To(Equal(ccv2.Application{
  1084  						GUID:  "some-app-guid",
  1085  						State: constant.ApplicationStopped,
  1086  					}))
  1087  
  1088  					passedApp = fakeCloudControllerClient.UpdateApplicationArgsForCall(1)
  1089  					Expect(passedApp).To(Equal(ccv2.Application{
  1090  						GUID:  "some-app-guid",
  1091  						State: constant.ApplicationStarted,
  1092  					}))
  1093  
  1094  					Expect(fakeCloudControllerClient.GetApplicationCallCount()).To(Equal(2))
  1095  					Expect(fakeCloudControllerClient.GetApplicationApplicationInstancesCallCount()).To(Equal(2))
  1096  					Eventually(fakeNOAAClient.CloseCallCount).Should(Equal(2))
  1097  				})
  1098  
  1099  				When("updating the application to stop fails", func() {
  1100  					var expectedErr error
  1101  					BeforeEach(func() {
  1102  						expectedErr = errors.New("I am a banana!!!!")
  1103  						updateApplicationCalled := false
  1104  						fakeCloudControllerClient.UpdateApplicationStub = func(app ccv2.Application) (ccv2.Application, ccv2.Warnings, error) {
  1105  							if !updateApplicationCalled {
  1106  								return ccv2.Application{}, ccv2.Warnings{"state-warning"}, expectedErr
  1107  							}
  1108  
  1109  							updateApplicationCalled = true
  1110  							return ccv2.Application{GUID: "some-app-guid",
  1111  								Instances: types.NullInt{Value: 2, IsSet: true},
  1112  								Name:      "some-app",
  1113  							}, ccv2.Warnings{"state-warning"}, nil
  1114  						}
  1115  					})
  1116  
  1117  					It("sends the update error and never polls", func() {
  1118  						Eventually(appState).Should(Receive(Equal(ApplicationStateStopping)))
  1119  						Eventually(warnings).Should(Receive(Equal("state-warning")))
  1120  						Eventually(errs).Should(Receive(MatchError(expectedErr)))
  1121  						Eventually(appState).ShouldNot(Receive(Equal(ApplicationStateStaging)))
  1122  
  1123  						Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(0))
  1124  						Expect(fakeCloudControllerClient.GetApplicationCallCount()).To(Equal(0))
  1125  						Expect(fakeCloudControllerClient.GetApplicationApplicationInstancesCallCount()).To(Equal(0))
  1126  					})
  1127  				})
  1128  			})
  1129  
  1130  			When("the app is not running", func() {
  1131  				BeforeEach(func() {
  1132  					app.State = constant.ApplicationStopped
  1133  				})
  1134  
  1135  				It("does not stop an app instance", func() {
  1136  					Eventually(appState).Should(Receive(Equal(ApplicationStateStaging)))
  1137  					Eventually(warnings).Should(Receive(Equal("state-warning")))
  1138  					Eventually(warnings).Should(Receive(Equal("app-warnings-1")))
  1139  					Eventually(warnings).Should(Receive(Equal("app-warnings-2")))
  1140  					Eventually(appState).Should(Receive(Equal(ApplicationStateStarting)))
  1141  					Eventually(warnings).Should(Receive(Equal("app-instance-warnings-1")))
  1142  					Eventually(warnings).Should(Receive(Equal("app-instance-warnings-2")))
  1143  
  1144  					Expect(fakeCloudControllerClient.UpdateApplicationCallCount()).To(Equal(1))
  1145  					passedApp := fakeCloudControllerClient.UpdateApplicationArgsForCall(0)
  1146  					Expect(passedApp).To(Equal(ccv2.Application{
  1147  						GUID:  "some-app-guid",
  1148  						State: constant.ApplicationStarted,
  1149  					}))
  1150  				})
  1151  			})
  1152  
  1153  			When("the app is already staged", func() {
  1154  				BeforeEach(func() {
  1155  					app.PackageState = constant.ApplicationPackageStaged
  1156  				})
  1157  
  1158  				It("does not send ApplicationStateStaging", func() {
  1159  					Consistently(appState).ShouldNot(Receive(Equal(ApplicationStateStaging)))
  1160  					Eventually(warnings).Should(Receive(Equal("state-warning")))
  1161  					Eventually(warnings).Should(Receive(Equal("app-warnings-1")))
  1162  					Eventually(warnings).Should(Receive(Equal("app-warnings-2")))
  1163  					Eventually(appState).Should(Receive(Equal(ApplicationStateStarting)))
  1164  					Eventually(warnings).Should(Receive(Equal("app-instance-warnings-1")))
  1165  					Eventually(warnings).Should(Receive(Equal("app-instance-warnings-2")))
  1166  
  1167  					Expect(fakeCloudControllerClient.UpdateApplicationCallCount()).To(Equal(1))
  1168  					passedApp := fakeCloudControllerClient.UpdateApplicationArgsForCall(0)
  1169  					Expect(passedApp).To(Equal(ccv2.Application{
  1170  						GUID:  "some-app-guid",
  1171  						State: constant.ApplicationStarted,
  1172  					}))
  1173  				})
  1174  			})
  1175  
  1176  			ItStartsApplication()
  1177  		})
  1178  
  1179  		Describe("RestageApplication", func() {
  1180  			JustBeforeEach(func() {
  1181  				messages, logErrs, appState, warnings, errs = actor.RestageApplication(app, fakeNOAAClient)
  1182  			})
  1183  
  1184  			When("restaging succeeds", func() {
  1185  				BeforeEach(func() {
  1186  					fakeCloudControllerClient.RestageApplicationReturns(ccv2.Application{GUID: "some-app-guid",
  1187  						Instances: types.NullInt{Value: 2, IsSet: true},
  1188  						Name:      "some-app",
  1189  					}, ccv2.Warnings{"state-warning"}, nil)
  1190  				})
  1191  
  1192  				It("restages and polls for app instances", func() {
  1193  					Eventually(appState).Should(Receive(Equal(ApplicationStateStaging)))
  1194  					Eventually(warnings).Should(Receive(Equal("state-warning")))
  1195  					Eventually(warnings).Should(Receive(Equal("app-warnings-1")))
  1196  					Eventually(warnings).Should(Receive(Equal("app-warnings-2")))
  1197  					Eventually(appState).Should(Receive(Equal(ApplicationStateStarting)))
  1198  					Eventually(warnings).Should(Receive(Equal("app-instance-warnings-1")))
  1199  					Eventually(warnings).Should(Receive(Equal("app-instance-warnings-2")))
  1200  
  1201  					Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(2))
  1202  
  1203  					Expect(fakeCloudControllerClient.RestageApplicationCallCount()).To(Equal(1))
  1204  					app := fakeCloudControllerClient.RestageApplicationArgsForCall(0)
  1205  					Expect(app).To(Equal(ccv2.Application{
  1206  						GUID: "some-app-guid",
  1207  					}))
  1208  
  1209  					Expect(fakeCloudControllerClient.GetApplicationCallCount()).To(Equal(2))
  1210  					Expect(fakeCloudControllerClient.GetApplicationApplicationInstancesCallCount()).To(Equal(2))
  1211  					Eventually(fakeNOAAClient.CloseCallCount).Should(Equal(2))
  1212  				})
  1213  
  1214  				ItHandlesStagingIssues()
  1215  
  1216  				ItHandlesStartingIssues()
  1217  			})
  1218  
  1219  			When("restaging errors", func() {
  1220  				BeforeEach(func() {
  1221  					fakeCloudControllerClient.RestageApplicationReturns(ccv2.Application{GUID: "some-app-guid",
  1222  						Instances: types.NullInt{Value: 2, IsSet: true},
  1223  						Name:      "some-app",
  1224  					}, ccv2.Warnings{"state-warning"}, errors.New("some-error"))
  1225  				})
  1226  
  1227  				It("sends the restage error and never polls", func() {
  1228  					Eventually(appState).Should(Receive(Equal(ApplicationStateStaging)))
  1229  					Eventually(warnings).Should(Receive(Equal("state-warning")))
  1230  					Eventually(errs).Should(Receive(MatchError("some-error")))
  1231  
  1232  					Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(0))
  1233  					Expect(fakeCloudControllerClient.GetApplicationCallCount()).To(Equal(0))
  1234  					Expect(fakeCloudControllerClient.GetApplicationApplicationInstancesCallCount()).To(Equal(0))
  1235  				})
  1236  			})
  1237  		})
  1238  	})
  1239  
  1240  	Describe("UpdateApplication", func() {
  1241  		When("the update is successful", func() {
  1242  			var expectedApp ccv2.Application
  1243  			BeforeEach(func() {
  1244  				expectedApp = ccv2.Application{
  1245  					GUID:      "some-app-guid",
  1246  					Name:      "some-app-name",
  1247  					SpaceGUID: "some-space-guid",
  1248  				}
  1249  				fakeCloudControllerClient.UpdateApplicationReturns(expectedApp, ccv2.Warnings{"some-app-warning-1"}, nil)
  1250  			})
  1251  
  1252  			It("updates and returns the application", func() {
  1253  				newApp := Application{
  1254  					Name:      "some-app-name",
  1255  					SpaceGUID: "some-space-guid",
  1256  				}
  1257  				app, warnings, err := actor.UpdateApplication(newApp)
  1258  				Expect(err).ToNot(HaveOccurred())
  1259  				Expect(warnings).To(ConsistOf("some-app-warning-1"))
  1260  				Expect(app).To(Equal(Application(expectedApp)))
  1261  
  1262  				Expect(fakeCloudControllerClient.UpdateApplicationCallCount()).To(Equal(1))
  1263  				Expect(fakeCloudControllerClient.UpdateApplicationArgsForCall(0)).To(Equal(ccv2.Application(newApp)))
  1264  			})
  1265  		})
  1266  
  1267  		When("the client returns back an error", func() {
  1268  			var expectedErr error
  1269  			BeforeEach(func() {
  1270  				expectedErr = errors.New("some update app error")
  1271  				fakeCloudControllerClient.UpdateApplicationReturns(ccv2.Application{}, ccv2.Warnings{"some-app-warning-1"}, expectedErr)
  1272  			})
  1273  
  1274  			It("returns warnings and an error", func() {
  1275  				newApp := Application{
  1276  					Name:      "some-app-name",
  1277  					SpaceGUID: "some-space-guid",
  1278  				}
  1279  				_, warnings, err := actor.UpdateApplication(newApp)
  1280  				Expect(warnings).To(ConsistOf("some-app-warning-1"))
  1281  				Expect(err).To(MatchError(expectedErr))
  1282  			})
  1283  		})
  1284  	})
  1285  })