github.com/cloudfoundry-attic/cli-with-i18n@v6.32.1-0.20171002233121-7401370d3b85+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/types"
    13  
    14  	"github.com/cloudfoundry/sonde-go/events"
    15  	. "github.com/onsi/ginkgo"
    16  	. "github.com/onsi/gomega"
    17  )
    18  
    19  var _ = Describe("Application Actions", func() {
    20  	var (
    21  		actor                     *Actor
    22  		fakeCloudControllerClient *v2actionfakes.FakeCloudControllerClient
    23  	)
    24  
    25  	BeforeEach(func() {
    26  		fakeCloudControllerClient = new(v2actionfakes.FakeCloudControllerClient)
    27  		actor = NewActor(fakeCloudControllerClient, nil, nil)
    28  	})
    29  
    30  	Describe("Application", func() {
    31  		var app Application
    32  		BeforeEach(func() {
    33  			app = Application{}
    34  		})
    35  
    36  		Describe("CalculatedCommand", func() {
    37  			Context("when command is set", func() {
    38  				BeforeEach(func() {
    39  					app.Command = types.FilteredString{IsSet: true, Value: "foo"}
    40  					app.DetectedStartCommand = types.FilteredString{IsSet: true, Value: "bar"}
    41  				})
    42  
    43  				It("returns back the command", func() {
    44  					Expect(app.CalculatedCommand()).To(Equal("foo"))
    45  				})
    46  			})
    47  
    48  			Context("only detected start command is set", func() {
    49  				BeforeEach(func() {
    50  					app.DetectedStartCommand = types.FilteredString{IsSet: true, Value: "bar"}
    51  				})
    52  
    53  				It("returns back the detected start command", func() {
    54  					Expect(app.CalculatedCommand()).To(Equal("bar"))
    55  				})
    56  			})
    57  
    58  			Context("neither command nor detected start command are set", func() {
    59  				It("returns an empty string", func() {
    60  					Expect(app.CalculatedCommand()).To(BeEmpty())
    61  				})
    62  			})
    63  		})
    64  
    65  		Describe("CalculatedBuildpack", func() {
    66  			Context("when buildpack is set", func() {
    67  				BeforeEach(func() {
    68  					app.Buildpack = types.FilteredString{IsSet: true, Value: "foo"}
    69  					app.DetectedBuildpack = types.FilteredString{IsSet: true, Value: "bar"}
    70  				})
    71  
    72  				It("returns back the buildpack", func() {
    73  					Expect(app.CalculatedBuildpack()).To(Equal("foo"))
    74  				})
    75  			})
    76  
    77  			Context("only detected buildpack is set", func() {
    78  				BeforeEach(func() {
    79  					app.DetectedBuildpack = types.FilteredString{IsSet: true, Value: "bar"}
    80  				})
    81  
    82  				It("returns back the detected buildpack", func() {
    83  					Expect(app.CalculatedBuildpack()).To(Equal("bar"))
    84  				})
    85  			})
    86  
    87  			Context("neither buildpack nor detected buildpack are set", func() {
    88  				It("returns an empty string", func() {
    89  					Expect(app.CalculatedBuildpack()).To(BeEmpty())
    90  				})
    91  			})
    92  		})
    93  
    94  		Describe("CalculatedHealthCheckEndpoint", func() {
    95  			Context("when the health check type is http", func() {
    96  				BeforeEach(func() {
    97  					app.HealthCheckType = "http"
    98  					app.HealthCheckHTTPEndpoint = "/some-endpoint"
    99  				})
   100  
   101  				It("returns the endpoint field", func() {
   102  					Expect(app.CalculatedHealthCheckEndpoint()).To(Equal(
   103  						"/some-endpoint"))
   104  				})
   105  			})
   106  
   107  			Context("when the health check type is not http", func() {
   108  				BeforeEach(func() {
   109  					app.HealthCheckType = "process"
   110  					app.HealthCheckHTTPEndpoint = "/some-endpoint"
   111  				})
   112  
   113  				It("returns the empty string", func() {
   114  					Expect(app.CalculatedHealthCheckEndpoint()).To(Equal(""))
   115  				})
   116  			})
   117  		})
   118  
   119  		Describe("StagingCompleted", func() {
   120  			Context("when staging the application completes", func() {
   121  				It("returns true", func() {
   122  					app.PackageState = ccv2.ApplicationPackageStaged
   123  					Expect(app.StagingCompleted()).To(BeTrue())
   124  				})
   125  			})
   126  
   127  			Context("when the application is *not* staged", func() {
   128  				It("returns false", func() {
   129  					app.PackageState = ccv2.ApplicationPackageFailed
   130  					Expect(app.StagingCompleted()).To(BeFalse())
   131  				})
   132  			})
   133  		})
   134  
   135  		Describe("StagingFailed", func() {
   136  			Context("when staging the application fails", func() {
   137  				It("returns true", func() {
   138  					app.PackageState = ccv2.ApplicationPackageFailed
   139  					Expect(app.StagingFailed()).To(BeTrue())
   140  				})
   141  			})
   142  
   143  			Context("when staging the application does *not* fail", func() {
   144  				It("returns false", func() {
   145  					app.PackageState = ccv2.ApplicationPackageStaged
   146  					Expect(app.StagingFailed()).To(BeFalse())
   147  				})
   148  			})
   149  		})
   150  
   151  		Describe("StagingFailedMessage", func() {
   152  			Context("when the application has a staging failed description", func() {
   153  				BeforeEach(func() {
   154  					app.StagingFailedDescription = "An app was not successfully detected by any available buildpack"
   155  					app.StagingFailedReason = "NoAppDetectedError"
   156  				})
   157  				It("returns that description", func() {
   158  					Expect(app.StagingFailedMessage()).To(Equal("An app was not successfully detected by any available buildpack"))
   159  				})
   160  			})
   161  
   162  			Context("when the application does not have a staging failed description", func() {
   163  				BeforeEach(func() {
   164  					app.StagingFailedDescription = ""
   165  					app.StagingFailedReason = "NoAppDetectedError"
   166  				})
   167  				It("returns the staging failed code", func() {
   168  					Expect(app.StagingFailedMessage()).To(Equal("NoAppDetectedError"))
   169  				})
   170  			})
   171  		})
   172  
   173  		Describe("StagingFailedNoAppDetected", func() {
   174  			Context("when staging the application fails due to a no app detected error", func() {
   175  				It("returns true", func() {
   176  					app.StagingFailedReason = "NoAppDetectedError"
   177  					Expect(app.StagingFailedNoAppDetected()).To(BeTrue())
   178  				})
   179  			})
   180  
   181  			Context("when staging the application fails due to any other reason", func() {
   182  				It("returns false", func() {
   183  					app.StagingFailedReason = "InsufficientResources"
   184  					Expect(app.StagingFailedNoAppDetected()).To(BeFalse())
   185  				})
   186  			})
   187  		})
   188  
   189  		Describe("Started", func() {
   190  			Context("when app is started", func() {
   191  				It("returns true", func() {
   192  					Expect(Application{State: ccv2.ApplicationStarted}.Started()).To(BeTrue())
   193  				})
   194  			})
   195  
   196  			Context("when app is stopped", func() {
   197  				It("returns false", func() {
   198  					Expect(Application{State: ccv2.ApplicationStopped}.Started()).To(BeFalse())
   199  				})
   200  			})
   201  		})
   202  
   203  		Describe("Stopped", func() {
   204  			Context("when app is started", func() {
   205  				It("returns true", func() {
   206  					Expect(Application{State: ccv2.ApplicationStopped}.Stopped()).To(BeTrue())
   207  				})
   208  			})
   209  
   210  			Context("when app is stopped", func() {
   211  				It("returns false", func() {
   212  					Expect(Application{State: ccv2.ApplicationStarted}.Stopped()).To(BeFalse())
   213  				})
   214  			})
   215  		})
   216  	})
   217  
   218  	Describe("CreateApplication", func() {
   219  		Context("when the create is successful", func() {
   220  			var expectedApp ccv2.Application
   221  			BeforeEach(func() {
   222  				expectedApp = ccv2.Application{
   223  					GUID:      "some-app-guid",
   224  					Name:      "some-app-name",
   225  					SpaceGUID: "some-space-guid",
   226  				}
   227  				fakeCloudControllerClient.CreateApplicationReturns(expectedApp, ccv2.Warnings{"some-app-warning-1"}, nil)
   228  			})
   229  
   230  			It("creates and returns the application", func() {
   231  				newApp := Application{
   232  					Name:      "some-app-name",
   233  					SpaceGUID: "some-space-guid",
   234  				}
   235  				app, warnings, err := actor.CreateApplication(newApp)
   236  				Expect(err).ToNot(HaveOccurred())
   237  				Expect(warnings).To(ConsistOf("some-app-warning-1"))
   238  				Expect(app).To(Equal(Application(expectedApp)))
   239  
   240  				Expect(fakeCloudControllerClient.CreateApplicationCallCount()).To(Equal(1))
   241  				Expect(fakeCloudControllerClient.CreateApplicationArgsForCall(0)).To(Equal(ccv2.Application(newApp)))
   242  			})
   243  		})
   244  
   245  		Context("when the client returns back an error", func() {
   246  			var expectedErr error
   247  			BeforeEach(func() {
   248  				expectedErr = errors.New("some create app error")
   249  				fakeCloudControllerClient.CreateApplicationReturns(ccv2.Application{}, ccv2.Warnings{"some-app-warning-1"}, expectedErr)
   250  			})
   251  
   252  			It("returns warnings and an error", func() {
   253  				newApp := Application{
   254  					Name:      "some-app-name",
   255  					SpaceGUID: "some-space-guid",
   256  				}
   257  				_, warnings, err := actor.CreateApplication(newApp)
   258  				Expect(warnings).To(ConsistOf("some-app-warning-1"))
   259  				Expect(err).To(MatchError(expectedErr))
   260  			})
   261  		})
   262  	})
   263  
   264  	Describe("GetApplication", func() {
   265  		Context("when the application exists", func() {
   266  			BeforeEach(func() {
   267  				fakeCloudControllerClient.GetApplicationReturns(
   268  					ccv2.Application{
   269  						GUID: "some-app-guid",
   270  						Name: "some-app",
   271  					},
   272  					ccv2.Warnings{"foo"},
   273  					nil,
   274  				)
   275  			})
   276  
   277  			It("returns the application and warnings", func() {
   278  				app, warnings, err := actor.GetApplication("some-app-guid")
   279  				Expect(err).ToNot(HaveOccurred())
   280  				Expect(app).To(Equal(Application{
   281  					GUID: "some-app-guid",
   282  					Name: "some-app",
   283  				}))
   284  				Expect(warnings).To(Equal(Warnings{"foo"}))
   285  
   286  				Expect(fakeCloudControllerClient.GetApplicationCallCount()).To(Equal(1))
   287  				Expect(fakeCloudControllerClient.GetApplicationArgsForCall(0)).To(Equal("some-app-guid"))
   288  			})
   289  		})
   290  
   291  		Context("when the application does not exist", func() {
   292  			BeforeEach(func() {
   293  				fakeCloudControllerClient.GetApplicationReturns(ccv2.Application{}, nil, ccerror.ResourceNotFoundError{})
   294  			})
   295  
   296  			It("returns an ApplicationNotFoundError", func() {
   297  				_, _, err := actor.GetApplication("some-app-guid")
   298  				Expect(err).To(MatchError(actionerror.ApplicationNotFoundError{GUID: "some-app-guid"}))
   299  			})
   300  		})
   301  	})
   302  
   303  	Describe("GetApplicationByNameAndSpace", func() {
   304  		Context("when the application exists", func() {
   305  			BeforeEach(func() {
   306  				fakeCloudControllerClient.GetApplicationsReturns(
   307  					[]ccv2.Application{
   308  						{
   309  							GUID: "some-app-guid",
   310  							Name: "some-app",
   311  						},
   312  					},
   313  					ccv2.Warnings{"foo"},
   314  					nil,
   315  				)
   316  			})
   317  
   318  			It("returns the application and warnings", func() {
   319  				app, warnings, err := actor.GetApplicationByNameAndSpace("some-app", "some-space-guid")
   320  				Expect(err).ToNot(HaveOccurred())
   321  				Expect(app).To(Equal(Application{
   322  					GUID: "some-app-guid",
   323  					Name: "some-app",
   324  				}))
   325  				Expect(warnings).To(Equal(Warnings{"foo"}))
   326  
   327  				Expect(fakeCloudControllerClient.GetApplicationsCallCount()).To(Equal(1))
   328  				Expect(fakeCloudControllerClient.GetApplicationsArgsForCall(0)).To(ConsistOf([]ccv2.Query{
   329  					ccv2.Query{
   330  						Filter:   ccv2.NameFilter,
   331  						Operator: ccv2.EqualOperator,
   332  						Values:   []string{"some-app"},
   333  					},
   334  					ccv2.Query{
   335  						Filter:   ccv2.SpaceGUIDFilter,
   336  						Operator: ccv2.EqualOperator,
   337  						Values:   []string{"some-space-guid"},
   338  					},
   339  				}))
   340  			})
   341  		})
   342  
   343  		Context("when the application does not exists", func() {
   344  			BeforeEach(func() {
   345  				fakeCloudControllerClient.GetApplicationsReturns([]ccv2.Application{}, nil, nil)
   346  			})
   347  
   348  			It("returns an ApplicationNotFoundError", func() {
   349  				_, _, err := actor.GetApplicationByNameAndSpace("some-app", "some-space-guid")
   350  				Expect(err).To(MatchError(actionerror.ApplicationNotFoundError{Name: "some-app"}))
   351  			})
   352  		})
   353  
   354  		Context("when the cloud controller client returns an error", func() {
   355  			var expectedError error
   356  
   357  			BeforeEach(func() {
   358  				expectedError = errors.New("I am a CloudControllerClient Error")
   359  				fakeCloudControllerClient.GetApplicationsReturns([]ccv2.Application{}, nil, expectedError)
   360  			})
   361  
   362  			It("returns the error", func() {
   363  				_, _, err := actor.GetApplicationByNameAndSpace("some-app", "some-space-guid")
   364  				Expect(err).To(MatchError(expectedError))
   365  			})
   366  		})
   367  	})
   368  
   369  	Describe("GetApplicationsBySpace", func() {
   370  		Context("when the there are applications in the space", func() {
   371  			BeforeEach(func() {
   372  				fakeCloudControllerClient.GetApplicationsReturns(
   373  					[]ccv2.Application{
   374  						{
   375  							GUID: "some-app-guid-1",
   376  							Name: "some-app-1",
   377  						},
   378  						{
   379  							GUID: "some-app-guid-2",
   380  							Name: "some-app-2",
   381  						},
   382  					},
   383  					ccv2.Warnings{"warning-1", "warning-2"},
   384  					nil,
   385  				)
   386  			})
   387  
   388  			It("returns the application and warnings", func() {
   389  				apps, warnings, err := actor.GetApplicationsBySpace("some-space-guid")
   390  				Expect(err).ToNot(HaveOccurred())
   391  				Expect(apps).To(ConsistOf(
   392  					Application{
   393  						GUID: "some-app-guid-1",
   394  						Name: "some-app-1",
   395  					},
   396  					Application{
   397  						GUID: "some-app-guid-2",
   398  						Name: "some-app-2",
   399  					},
   400  				))
   401  				Expect(warnings).To(ConsistOf("warning-1", "warning-2"))
   402  
   403  				Expect(fakeCloudControllerClient.GetApplicationsCallCount()).To(Equal(1))
   404  				Expect(fakeCloudControllerClient.GetApplicationsArgsForCall(0)).To(ConsistOf([]ccv2.Query{
   405  					ccv2.Query{
   406  						Filter:   ccv2.SpaceGUIDFilter,
   407  						Operator: ccv2.EqualOperator,
   408  						Values:   []string{"some-space-guid"},
   409  					},
   410  				}))
   411  			})
   412  		})
   413  
   414  		Context("when the cloud controller client returns an error", func() {
   415  			var expectedError error
   416  
   417  			BeforeEach(func() {
   418  				expectedError = errors.New("some cc error")
   419  				fakeCloudControllerClient.GetApplicationsReturns(
   420  					[]ccv2.Application{},
   421  					ccv2.Warnings{"warning-1", "warning-2"},
   422  					expectedError)
   423  			})
   424  
   425  			It("returns the error and warnings", func() {
   426  				_, warnings, err := actor.GetApplicationsBySpace("some-space-guid")
   427  				Expect(warnings).To(ConsistOf("warning-1", "warning-2"))
   428  				Expect(err).To(MatchError(expectedError))
   429  			})
   430  		})
   431  	})
   432  
   433  	Describe("GetRouteApplications", func() {
   434  		Context("when the CC client returns no errors", func() {
   435  			BeforeEach(func() {
   436  				fakeCloudControllerClient.GetRouteApplicationsReturns(
   437  					[]ccv2.Application{
   438  						{
   439  							GUID: "application-guid",
   440  							Name: "application-name",
   441  						},
   442  					}, ccv2.Warnings{"route-applications-warning"}, nil)
   443  			})
   444  			It("returns the applications bound to the route and warnings", func() {
   445  				applications, warnings, err := actor.GetRouteApplications("route-guid")
   446  				Expect(fakeCloudControllerClient.GetRouteApplicationsCallCount()).To(Equal(1))
   447  				Expect(fakeCloudControllerClient.GetRouteApplicationsArgsForCall(0)).To(Equal("route-guid"))
   448  
   449  				Expect(err).ToNot(HaveOccurred())
   450  				Expect(warnings).To(ConsistOf("route-applications-warning"))
   451  				Expect(applications).To(ConsistOf(
   452  					Application{
   453  						GUID: "application-guid",
   454  						Name: "application-name",
   455  					},
   456  				))
   457  			})
   458  		})
   459  
   460  		Context("when the CC client returns an error", func() {
   461  			BeforeEach(func() {
   462  				fakeCloudControllerClient.GetRouteApplicationsReturns(
   463  					[]ccv2.Application{}, ccv2.Warnings{"route-applications-warning"}, errors.New("get-route-applications-error"))
   464  			})
   465  
   466  			It("returns the error and warnings", func() {
   467  				apps, warnings, err := actor.GetRouteApplications("route-guid")
   468  				Expect(fakeCloudControllerClient.GetRouteApplicationsCallCount()).To(Equal(1))
   469  				Expect(fakeCloudControllerClient.GetRouteApplicationsArgsForCall(0)).To(Equal("route-guid"))
   470  
   471  				Expect(err).To(MatchError("get-route-applications-error"))
   472  				Expect(warnings).To(ConsistOf("route-applications-warning"))
   473  				Expect(apps).To(BeNil())
   474  			})
   475  		})
   476  	})
   477  
   478  	Describe("SetApplicationHealthCheckTypeByNameAndSpace", func() {
   479  		Context("when setting an http endpoint with a health check that is not http", func() {
   480  			It("returns an http health check invalid error", func() {
   481  				_, _, err := actor.SetApplicationHealthCheckTypeByNameAndSpace(
   482  					"some-app", "some-space-guid", "some-health-check-type", "/foo")
   483  				Expect(err).To(MatchError(actionerror.HTTPHealthCheckInvalidError{}))
   484  			})
   485  		})
   486  
   487  		Context("when the app exists", func() {
   488  			Context("when the desired health check type is different", func() {
   489  				BeforeEach(func() {
   490  					fakeCloudControllerClient.GetApplicationsReturns(
   491  						[]ccv2.Application{
   492  							{GUID: "some-app-guid"},
   493  						},
   494  						ccv2.Warnings{"get application warning"},
   495  						nil,
   496  					)
   497  					fakeCloudControllerClient.UpdateApplicationReturns(
   498  						ccv2.Application{
   499  							GUID:            "some-app-guid",
   500  							HealthCheckType: "process",
   501  						},
   502  						ccv2.Warnings{"update warnings"},
   503  						nil,
   504  					)
   505  				})
   506  
   507  				It("sets the desired health check type and returns the warnings", func() {
   508  					returnedApp, warnings, err := actor.SetApplicationHealthCheckTypeByNameAndSpace(
   509  						"some-app", "some-space-guid", "process", "/")
   510  					Expect(err).ToNot(HaveOccurred())
   511  					Expect(warnings).To(ConsistOf("get application warning", "update warnings"))
   512  
   513  					Expect(returnedApp).To(Equal(Application{
   514  						GUID:            "some-app-guid",
   515  						HealthCheckType: "process",
   516  					}))
   517  
   518  					Expect(fakeCloudControllerClient.UpdateApplicationCallCount()).To(Equal(1))
   519  					app := fakeCloudControllerClient.UpdateApplicationArgsForCall(0)
   520  					Expect(app).To(Equal(ccv2.Application{
   521  						GUID:            "some-app-guid",
   522  						HealthCheckType: "process",
   523  					}))
   524  				})
   525  			})
   526  
   527  			Context("when the desired health check type is 'http'", func() {
   528  				Context("when the desired http endpoint is already set", func() {
   529  					BeforeEach(func() {
   530  						fakeCloudControllerClient.GetApplicationsReturns(
   531  							[]ccv2.Application{
   532  								{GUID: "some-app-guid", HealthCheckType: "http", HealthCheckHTTPEndpoint: "/"},
   533  							},
   534  							ccv2.Warnings{"get application warning"},
   535  							nil,
   536  						)
   537  					})
   538  
   539  					It("does not send the update", func() {
   540  						_, warnings, err := actor.SetApplicationHealthCheckTypeByNameAndSpace(
   541  							"some-app", "some-space-guid", "http", "/")
   542  						Expect(err).ToNot(HaveOccurred())
   543  						Expect(warnings).To(ConsistOf("get application warning"))
   544  
   545  						Expect(fakeCloudControllerClient.UpdateApplicationCallCount()).To(Equal(0))
   546  					})
   547  				})
   548  
   549  				Context("when the desired http endpoint is not set", func() {
   550  					BeforeEach(func() {
   551  						fakeCloudControllerClient.GetApplicationsReturns(
   552  							[]ccv2.Application{
   553  								{GUID: "some-app-guid", HealthCheckType: "http", HealthCheckHTTPEndpoint: "/"},
   554  							},
   555  							ccv2.Warnings{"get application warning"},
   556  							nil,
   557  						)
   558  						fakeCloudControllerClient.UpdateApplicationReturns(
   559  							ccv2.Application{},
   560  							ccv2.Warnings{"update warnings"},
   561  							nil,
   562  						)
   563  					})
   564  
   565  					It("sets the desired health check type and returns the warnings", func() {
   566  						_, warnings, err := actor.SetApplicationHealthCheckTypeByNameAndSpace(
   567  							"some-app", "some-space-guid", "http", "/v2/anything")
   568  						Expect(err).ToNot(HaveOccurred())
   569  
   570  						Expect(fakeCloudControllerClient.UpdateApplicationCallCount()).To(Equal(1))
   571  						app := fakeCloudControllerClient.UpdateApplicationArgsForCall(0)
   572  						Expect(app).To(Equal(ccv2.Application{
   573  							GUID:                    "some-app-guid",
   574  							HealthCheckType:         "http",
   575  							HealthCheckHTTPEndpoint: "/v2/anything",
   576  						}))
   577  
   578  						Expect(warnings).To(ConsistOf("get application warning", "update warnings"))
   579  					})
   580  				})
   581  			})
   582  
   583  			Context("when the application health check type is already set to the desired type", func() {
   584  				BeforeEach(func() {
   585  					fakeCloudControllerClient.GetApplicationsReturns(
   586  						[]ccv2.Application{
   587  							{
   588  								GUID:            "some-app-guid",
   589  								HealthCheckType: "process",
   590  							},
   591  						},
   592  						ccv2.Warnings{"get application warning"},
   593  						nil,
   594  					)
   595  				})
   596  
   597  				It("does not update the health check type", func() {
   598  					returnedApp, warnings, err := actor.SetApplicationHealthCheckTypeByNameAndSpace(
   599  						"some-app", "some-space-guid", "process", "/")
   600  					Expect(err).ToNot(HaveOccurred())
   601  					Expect(warnings).To(ConsistOf("get application warning"))
   602  					Expect(returnedApp).To(Equal(Application{
   603  						GUID:            "some-app-guid",
   604  						HealthCheckType: "process",
   605  					}))
   606  
   607  					Expect(fakeCloudControllerClient.UpdateApplicationCallCount()).To(Equal(0))
   608  				})
   609  			})
   610  		})
   611  
   612  		Context("when getting the application returns an error", func() {
   613  			BeforeEach(func() {
   614  				fakeCloudControllerClient.GetApplicationsReturns(
   615  					[]ccv2.Application{}, ccv2.Warnings{"get application warning"}, errors.New("get application error"))
   616  			})
   617  
   618  			It("returns the error and warnings", func() {
   619  				_, warnings, err := actor.SetApplicationHealthCheckTypeByNameAndSpace(
   620  					"some-app", "some-space-guid", "process", "/")
   621  
   622  				Expect(warnings).To(ConsistOf("get application warning"))
   623  				Expect(err).To(MatchError("get application error"))
   624  			})
   625  		})
   626  
   627  		Context("when updating the application returns an error", func() {
   628  			var expectedErr error
   629  
   630  			BeforeEach(func() {
   631  				expectedErr = errors.New("foo bar")
   632  				fakeCloudControllerClient.GetApplicationsReturns(
   633  					[]ccv2.Application{
   634  						{GUID: "some-app-guid"},
   635  					},
   636  					ccv2.Warnings{"get application warning"},
   637  					nil,
   638  				)
   639  				fakeCloudControllerClient.UpdateApplicationReturns(
   640  					ccv2.Application{},
   641  					ccv2.Warnings{"update warnings"},
   642  					expectedErr,
   643  				)
   644  			})
   645  
   646  			It("returns the error and warnings", func() {
   647  				_, warnings, err := actor.SetApplicationHealthCheckTypeByNameAndSpace(
   648  					"some-app", "some-space-guid", "process", "/")
   649  				Expect(err).To(MatchError(expectedErr))
   650  				Expect(warnings).To(ConsistOf("get application warning", "update warnings"))
   651  			})
   652  		})
   653  	})
   654  
   655  	Describe("StartApplication/RestartApplication", func() {
   656  		var (
   657  			app            Application
   658  			fakeNOAAClient *v2actionfakes.FakeNOAAClient
   659  			fakeConfig     *v2actionfakes.FakeConfig
   660  
   661  			messages <-chan *LogMessage
   662  			logErrs  <-chan error
   663  			appState <-chan ApplicationStateChange
   664  			warnings <-chan string
   665  			errs     <-chan error
   666  
   667  			eventStream chan *events.LogMessage
   668  			errStream   chan error
   669  		)
   670  
   671  		BeforeEach(func() {
   672  			fakeConfig = new(v2actionfakes.FakeConfig)
   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 += 1
   703  					return ccv2.Application{
   704  						GUID:         "some-app-guid",
   705  						Instances:    types.NullInt{Value: 2, IsSet: true},
   706  						Name:         "some-app",
   707  						PackageState: ccv2.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: ccv2.ApplicationPackageStaged,
   716  				}, ccv2.Warnings{"app-warnings-2"}, nil
   717  			}
   718  
   719  			instanceCount := 0
   720  			fakeCloudControllerClient.GetApplicationInstancesByApplicationStub = func(guid string) (map[int]ccv2.ApplicationInstance, ccv2.Warnings, error) {
   721  				if instanceCount == 0 {
   722  					instanceCount += 1
   723  					return map[int]ccv2.ApplicationInstance{
   724  						0: {State: ccv2.ApplicationInstanceStarting},
   725  						1: {State: ccv2.ApplicationInstanceStarting},
   726  					}, ccv2.Warnings{"app-instance-warnings-1"}, nil
   727  				}
   728  
   729  				return map[int]ccv2.ApplicationInstance{
   730  					0: {State: ccv2.ApplicationInstanceStarting},
   731  					1: {State: ccv2.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  				Context("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.GetApplicationInstancesByApplicationCallCount()).To(Equal(0))
   764  					})
   765  				})
   766  
   767  				Context("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:        ccv2.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.GetApplicationInstancesByApplicationCallCount()).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:        ccv2.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.GetApplicationInstancesByApplicationCallCount()).To(Equal(0))
   817  						})
   818  					})
   819  				})
   820  
   821  				Context("when the application takes too long to stage", func() {
   822  					BeforeEach(func() {
   823  						fakeConfig.StagingTimeoutReturns(0)
   824  						fakeCloudControllerClient.GetApplicationInstancesByApplicationStub = 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{Name: "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.GetApplicationInstancesByApplicationCallCount()).To(Equal(0))
   836  					})
   837  				})
   838  			})
   839  		}
   840  
   841  		var ItHandlesStartingIssues = func() {
   842  			Context("starting issues", func() {
   843  				Context("when polling fails", func() {
   844  					var expectedErr error
   845  					BeforeEach(func() {
   846  						expectedErr = errors.New("I am a banana!!!!")
   847  						fakeCloudControllerClient.GetApplicationInstancesByApplicationStub = 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.GetApplicationInstancesByApplicationCallCount()).To(Equal(1))
   863  					})
   864  				})
   865  
   866  				Context("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.GetApplicationInstancesByApplicationCallCount()).To(Equal(0))
   882  					})
   883  				})
   884  
   885  				Context("when the application crashes", func() {
   886  					BeforeEach(func() {
   887  						fakeCloudControllerClient.GetApplicationInstancesByApplicationStub = func(guid string) (map[int]ccv2.ApplicationInstance, ccv2.Warnings, error) {
   888  							return map[int]ccv2.ApplicationInstance{
   889  								0: {State: ccv2.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.GetApplicationInstancesByApplicationCallCount()).To(Equal(1))
   906  					})
   907  				})
   908  
   909  				Context("when the application flaps", func() {
   910  					BeforeEach(func() {
   911  						fakeCloudControllerClient.GetApplicationInstancesByApplicationStub = func(guid string) (map[int]ccv2.ApplicationInstance, ccv2.Warnings, error) {
   912  							return map[int]ccv2.ApplicationInstance{
   913  								0: {State: ccv2.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.GetApplicationInstancesByApplicationCallCount()).To(Equal(1))
   930  					})
   931  				})
   932  			})
   933  		}
   934  
   935  		var ItStartsApplication = func() {
   936  			Context("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: ccv2.ApplicationStarted,
   953  					}))
   954  
   955  					Expect(fakeCloudControllerClient.GetApplicationCallCount()).To(Equal(2))
   956  					Expect(fakeCloudControllerClient.GetApplicationInstancesByApplicationCallCount()).To(Equal(2))
   957  					Eventually(fakeNOAAClient.CloseCallCount).Should(Equal(2))
   958  				})
   959  			})
   960  
   961  			Context("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: ccv2.ApplicationStarted,
   983  					}))
   984  
   985  					Expect(fakeCloudControllerClient.GetApplicationCallCount()).To(Equal(2))
   986  					Expect(fakeCloudControllerClient.GetApplicationInstancesByApplicationCallCount()).To(Equal(0))
   987  				})
   988  			})
   989  
   990  			Context("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.GetApplicationInstancesByApplicationCallCount()).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, fakeConfig)
  1023  			})
  1024  
  1025  			Context("when the app is already staged", func() {
  1026  				BeforeEach(func() {
  1027  					app.PackageState = ccv2.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: ccv2.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, fakeConfig)
  1061  			})
  1062  
  1063  			Context("when application is running", func() {
  1064  				BeforeEach(func() {
  1065  					app.State = ccv2.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: ccv2.ApplicationStopped,
  1086  					}))
  1087  
  1088  					passedApp = fakeCloudControllerClient.UpdateApplicationArgsForCall(1)
  1089  					Expect(passedApp).To(Equal(ccv2.Application{
  1090  						GUID:  "some-app-guid",
  1091  						State: ccv2.ApplicationStarted,
  1092  					}))
  1093  
  1094  					Expect(fakeCloudControllerClient.GetApplicationCallCount()).To(Equal(2))
  1095  					Expect(fakeCloudControllerClient.GetApplicationInstancesByApplicationCallCount()).To(Equal(2))
  1096  					Eventually(fakeNOAAClient.CloseCallCount).Should(Equal(2))
  1097  				})
  1098  
  1099  				Context("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.GetApplicationInstancesByApplicationCallCount()).To(Equal(0))
  1126  					})
  1127  				})
  1128  			})
  1129  
  1130  			Context("when the app is not running", func() {
  1131  				BeforeEach(func() {
  1132  					app.State = ccv2.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: ccv2.ApplicationStarted,
  1149  					}))
  1150  				})
  1151  			})
  1152  
  1153  			Context("when the app is already staged", func() {
  1154  				BeforeEach(func() {
  1155  					app.PackageState = ccv2.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: ccv2.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, fakeConfig)
  1182  			})
  1183  
  1184  			Context("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.GetApplicationInstancesByApplicationCallCount()).To(Equal(2))
  1211  					Eventually(fakeNOAAClient.CloseCallCount).Should(Equal(2))
  1212  				})
  1213  
  1214  				ItHandlesStagingIssues()
  1215  
  1216  				ItHandlesStartingIssues()
  1217  			})
  1218  
  1219  			Context("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.GetApplicationInstancesByApplicationCallCount()).To(Equal(0))
  1235  				})
  1236  			})
  1237  		})
  1238  	})
  1239  
  1240  	Describe("UpdateApplication", func() {
  1241  		Context("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  		Context("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  })