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