github.com/mook-as/cf-cli@v7.0.0-beta.28.0.20200120190804-b91c115fae48+incompatible/actor/v3action/application_test.go (about)

     1  package v3action_test
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"strings"
     7  	"time"
     8  
     9  	"code.cloudfoundry.org/cli/actor/actionerror"
    10  	. "code.cloudfoundry.org/cli/actor/v3action"
    11  	"code.cloudfoundry.org/cli/actor/v3action/v3actionfakes"
    12  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
    13  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3"
    14  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
    15  
    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 *v3actionfakes.FakeCloudControllerClient
    24  		fakeConfig                *v3actionfakes.FakeConfig
    25  	)
    26  
    27  	BeforeEach(func() {
    28  		fakeCloudControllerClient = new(v3actionfakes.FakeCloudControllerClient)
    29  		fakeConfig = new(v3actionfakes.FakeConfig)
    30  		actor = NewActor(fakeCloudControllerClient, fakeConfig, nil, nil)
    31  	})
    32  
    33  	Describe("DeleteApplicationByNameAndSpace", func() {
    34  		var (
    35  			warnings   Warnings
    36  			executeErr error
    37  		)
    38  
    39  		JustBeforeEach(func() {
    40  			warnings, executeErr = actor.DeleteApplicationByNameAndSpace("some-app", "some-space-guid")
    41  		})
    42  
    43  		When("looking up the app guid fails", func() {
    44  			BeforeEach(func() {
    45  				fakeCloudControllerClient.GetApplicationsReturns([]ccv3.Application{}, ccv3.Warnings{"some-get-app-warning"}, errors.New("some-get-app-error"))
    46  			})
    47  
    48  			It("returns the warnings and error", func() {
    49  				Expect(warnings).To(ConsistOf("some-get-app-warning"))
    50  				Expect(executeErr).To(MatchError("some-get-app-error"))
    51  			})
    52  		})
    53  
    54  		When("looking up the app guid succeeds", func() {
    55  			BeforeEach(func() {
    56  				fakeCloudControllerClient.GetApplicationsReturns([]ccv3.Application{{Name: "some-app", GUID: "abc123"}}, ccv3.Warnings{"some-get-app-warning"}, nil)
    57  			})
    58  
    59  			When("sending the delete fails", func() {
    60  				BeforeEach(func() {
    61  					fakeCloudControllerClient.DeleteApplicationReturns("", ccv3.Warnings{"some-delete-app-warning"}, errors.New("some-delete-app-error"))
    62  				})
    63  
    64  				It("returns the warnings and error", func() {
    65  					Expect(warnings).To(ConsistOf("some-get-app-warning", "some-delete-app-warning"))
    66  					Expect(executeErr).To(MatchError("some-delete-app-error"))
    67  				})
    68  			})
    69  
    70  			When("sending the delete succeeds", func() {
    71  				BeforeEach(func() {
    72  					fakeCloudControllerClient.DeleteApplicationReturns("/some-job-url", ccv3.Warnings{"some-delete-app-warning"}, nil)
    73  				})
    74  
    75  				When("polling fails", func() {
    76  					BeforeEach(func() {
    77  						fakeCloudControllerClient.PollJobReturns(ccv3.Warnings{"some-poll-warning"}, errors.New("some-poll-error"))
    78  					})
    79  
    80  					It("returns the warnings and poll error", func() {
    81  						Expect(warnings).To(ConsistOf("some-get-app-warning", "some-delete-app-warning", "some-poll-warning"))
    82  						Expect(executeErr).To(MatchError("some-poll-error"))
    83  					})
    84  				})
    85  
    86  				When("polling succeeds", func() {
    87  					BeforeEach(func() {
    88  						fakeCloudControllerClient.PollJobReturns(ccv3.Warnings{"some-poll-warning"}, nil)
    89  					})
    90  
    91  					It("returns all the warnings and no error", func() {
    92  						Expect(warnings).To(ConsistOf("some-get-app-warning", "some-delete-app-warning", "some-poll-warning"))
    93  						Expect(executeErr).ToNot(HaveOccurred())
    94  					})
    95  				})
    96  			})
    97  		})
    98  	})
    99  
   100  	Describe("GetApplicationByNameAndSpace", func() {
   101  		When("the app exists", func() {
   102  			var (
   103  				warnings   Warnings
   104  				executeErr error
   105  				app        Application
   106  			)
   107  
   108  			BeforeEach(func() {
   109  				fakeCloudControllerClient.GetApplicationsReturns(
   110  					[]ccv3.Application{
   111  						{
   112  							Name: "some-app-name",
   113  							GUID: "some-app-guid",
   114  							Relationships: ccv3.Relationships{
   115  								constant.RelationshipTypeSpace: ccv3.Relationship{GUID: "some-space-guid"},
   116  							},
   117  						},
   118  					},
   119  					ccv3.Warnings{"some-warning"},
   120  					nil,
   121  				)
   122  			})
   123  
   124  			JustBeforeEach(func() {
   125  				app, warnings, executeErr = actor.GetApplicationByNameAndSpace("some-app-name", "some-space-guid")
   126  			})
   127  
   128  			It("returns the application and warnings", func() {
   129  				Expect(executeErr).ToNot(HaveOccurred())
   130  				Expect(app).To(Equal(Application{
   131  					Name:      "some-app-name",
   132  					GUID:      "some-app-guid",
   133  					SpaceGUID: "some-space-guid",
   134  				}))
   135  				Expect(warnings).To(ConsistOf("some-warning"))
   136  
   137  				Expect(fakeCloudControllerClient.GetApplicationsCallCount()).To(Equal(1))
   138  				Expect(fakeCloudControllerClient.GetApplicationsArgsForCall(0)).To(ConsistOf(
   139  					ccv3.Query{Key: ccv3.NameFilter, Values: []string{"some-app-name"}},
   140  					ccv3.Query{Key: ccv3.SpaceGUIDFilter, Values: []string{"some-space-guid"}},
   141  				))
   142  			})
   143  		})
   144  
   145  		When("the cloud controller client returns an error", func() {
   146  			var expectedError error
   147  
   148  			BeforeEach(func() {
   149  				expectedError = errors.New("I am a CloudControllerClient Error")
   150  				fakeCloudControllerClient.GetApplicationsReturns(
   151  					[]ccv3.Application{},
   152  					ccv3.Warnings{"some-warning"},
   153  					expectedError)
   154  			})
   155  
   156  			It("returns the warnings and the error", func() {
   157  				_, warnings, err := actor.GetApplicationByNameAndSpace("some-app-name", "some-space-guid")
   158  				Expect(warnings).To(ConsistOf("some-warning"))
   159  				Expect(err).To(MatchError(expectedError))
   160  			})
   161  		})
   162  
   163  		When("the app does not exist", func() {
   164  			BeforeEach(func() {
   165  				fakeCloudControllerClient.GetApplicationsReturns(
   166  					[]ccv3.Application{},
   167  					ccv3.Warnings{"some-warning"},
   168  					nil,
   169  				)
   170  			})
   171  
   172  			It("returns an ApplicationNotFoundError and the warnings", func() {
   173  				_, warnings, err := actor.GetApplicationByNameAndSpace("some-app-name", "some-space-guid")
   174  				Expect(warnings).To(ConsistOf("some-warning"))
   175  				Expect(err).To(MatchError(actionerror.ApplicationNotFoundError{Name: "some-app-name"}))
   176  			})
   177  		})
   178  	})
   179  
   180  	Describe("GetApplicationsBySpace", func() {
   181  		When("there are applications in the space", func() {
   182  			BeforeEach(func() {
   183  				fakeCloudControllerClient.GetApplicationsReturns(
   184  					[]ccv3.Application{
   185  						{
   186  							GUID: "some-app-guid-1",
   187  							Name: "some-app-1",
   188  							Relationships: ccv3.Relationships{
   189  								constant.RelationshipTypeSpace: ccv3.Relationship{GUID: "some-space-guid-1"},
   190  							},
   191  						},
   192  						{
   193  							GUID: "some-app-guid-2",
   194  							Name: "some-app-2",
   195  							Relationships: ccv3.Relationships{
   196  								constant.RelationshipTypeSpace: ccv3.Relationship{GUID: "some-space-guid-2"},
   197  							},
   198  						},
   199  					},
   200  					ccv3.Warnings{"warning-1", "warning-2"},
   201  					nil,
   202  				)
   203  			})
   204  
   205  			It("returns the application and warnings", func() {
   206  				apps, warnings, err := actor.GetApplicationsBySpace("some-space-guid")
   207  				Expect(err).ToNot(HaveOccurred())
   208  				Expect(apps).To(ConsistOf(
   209  					Application{
   210  						GUID:      "some-app-guid-1",
   211  						Name:      "some-app-1",
   212  						SpaceGUID: "some-space-guid-1",
   213  					},
   214  					Application{
   215  						GUID:      "some-app-guid-2",
   216  						Name:      "some-app-2",
   217  						SpaceGUID: "some-space-guid-2",
   218  					},
   219  				))
   220  				Expect(warnings).To(ConsistOf("warning-1", "warning-2"))
   221  
   222  				Expect(fakeCloudControllerClient.GetApplicationsCallCount()).To(Equal(1))
   223  				Expect(fakeCloudControllerClient.GetApplicationsArgsForCall(0)).To(ConsistOf(
   224  					ccv3.Query{Key: ccv3.SpaceGUIDFilter, Values: []string{"some-space-guid"}},
   225  				))
   226  			})
   227  		})
   228  
   229  		When("the cloud controller client returns an error", func() {
   230  			var expectedError error
   231  
   232  			BeforeEach(func() {
   233  				expectedError = errors.New("I am a CloudControllerClient Error")
   234  				fakeCloudControllerClient.GetApplicationsReturns(
   235  					[]ccv3.Application{},
   236  					ccv3.Warnings{"some-warning"},
   237  					expectedError)
   238  			})
   239  
   240  			It("returns the error and warnings", func() {
   241  				_, warnings, err := actor.GetApplicationsBySpace("some-space-guid")
   242  				Expect(warnings).To(ConsistOf("some-warning"))
   243  				Expect(err).To(MatchError(expectedError))
   244  			})
   245  		})
   246  	})
   247  
   248  	Describe("GetApplicationsByGUIDs", func() {
   249  		When("there are applications that match provided guids", func() {
   250  			BeforeEach(func() {
   251  				fakeCloudControllerClient.GetApplicationsReturns(
   252  					[]ccv3.Application{
   253  						{
   254  							GUID: "some-app-guid-1",
   255  							Name: "some-app-1",
   256  							Relationships: ccv3.Relationships{
   257  								constant.RelationshipTypeSpace: ccv3.Relationship{GUID: "some-space-guid-1"},
   258  							},
   259  						},
   260  						{
   261  							GUID: "some-app-guid-2",
   262  							Name: "some-app-2",
   263  							Relationships: ccv3.Relationships{
   264  								constant.RelationshipTypeSpace: ccv3.Relationship{GUID: "some-space-guid-2"},
   265  							},
   266  						},
   267  					},
   268  					ccv3.Warnings{"warning-1", "warning-2"},
   269  					nil,
   270  				)
   271  			})
   272  
   273  			It("returns the applications and warnings", func() {
   274  				apps, warnings, err := actor.GetApplicationsByGUIDs("some-app-guid-1", "some-app-guid-2")
   275  				Expect(err).ToNot(HaveOccurred())
   276  				Expect(apps).To(ConsistOf(
   277  					Application{
   278  						GUID:      "some-app-guid-1",
   279  						Name:      "some-app-1",
   280  						SpaceGUID: "some-space-guid-1",
   281  					},
   282  					Application{
   283  						GUID:      "some-app-guid-2",
   284  						Name:      "some-app-2",
   285  						SpaceGUID: "some-space-guid-2",
   286  					},
   287  				))
   288  				Expect(warnings).To(ConsistOf("warning-1", "warning-2"))
   289  
   290  				Expect(fakeCloudControllerClient.GetApplicationsCallCount()).To(Equal(1))
   291  				Expect(fakeCloudControllerClient.GetApplicationsArgsForCall(0)).To(ConsistOf(
   292  					ccv3.Query{Key: ccv3.GUIDFilter, Values: []string{"some-app-guid-1", "some-app-guid-2"}},
   293  				))
   294  			})
   295  		})
   296  
   297  		When("the cloud controller client returns an error", func() {
   298  			var expectedError error
   299  
   300  			BeforeEach(func() {
   301  				expectedError = errors.New("I am a CloudControllerClient Error")
   302  				fakeCloudControllerClient.GetApplicationsReturns(
   303  					[]ccv3.Application{},
   304  					ccv3.Warnings{"some-warning"},
   305  					expectedError)
   306  			})
   307  
   308  			It("returns the error and warnings", func() {
   309  				_, warnings, err := actor.GetApplicationsByGUIDs("some-space-guid")
   310  				Expect(warnings).To(ConsistOf("some-warning"))
   311  				Expect(err).To(MatchError(expectedError))
   312  			})
   313  		})
   314  	})
   315  
   316  	Describe("CreateApplicationInSpace", func() {
   317  		var (
   318  			application Application
   319  			warnings    Warnings
   320  			err         error
   321  		)
   322  
   323  		JustBeforeEach(func() {
   324  			application, warnings, err = actor.CreateApplicationInSpace(Application{
   325  				Name:                "some-app-name",
   326  				LifecycleType:       constant.AppLifecycleTypeBuildpack,
   327  				LifecycleBuildpacks: []string{"buildpack-1", "buildpack-2"},
   328  			}, "some-space-guid")
   329  		})
   330  
   331  		When("the app successfully gets created", func() {
   332  			BeforeEach(func() {
   333  				fakeCloudControllerClient.CreateApplicationReturns(
   334  					ccv3.Application{
   335  						Name:                "some-app-name",
   336  						GUID:                "some-app-guid",
   337  						LifecycleType:       constant.AppLifecycleTypeBuildpack,
   338  						LifecycleBuildpacks: []string{"buildpack-1", "buildpack-2"},
   339  						Relationships: ccv3.Relationships{
   340  							constant.RelationshipTypeSpace: ccv3.Relationship{GUID: "some-space-guid"},
   341  						},
   342  					},
   343  					ccv3.Warnings{"some-warning"},
   344  					nil,
   345  				)
   346  			})
   347  
   348  			It("creates and returns the application and warnings", func() {
   349  				Expect(err).ToNot(HaveOccurred())
   350  				Expect(application).To(Equal(Application{
   351  					Name:                "some-app-name",
   352  					GUID:                "some-app-guid",
   353  					LifecycleType:       constant.AppLifecycleTypeBuildpack,
   354  					LifecycleBuildpacks: []string{"buildpack-1", "buildpack-2"},
   355  					SpaceGUID:           "some-space-guid",
   356  				}))
   357  				Expect(warnings).To(ConsistOf("some-warning"))
   358  
   359  				Expect(fakeCloudControllerClient.CreateApplicationCallCount()).To(Equal(1))
   360  				Expect(fakeCloudControllerClient.CreateApplicationArgsForCall(0)).To(Equal(ccv3.Application{
   361  					Name: "some-app-name",
   362  					Relationships: ccv3.Relationships{
   363  						constant.RelationshipTypeSpace: ccv3.Relationship{GUID: "some-space-guid"},
   364  					},
   365  					LifecycleType:       constant.AppLifecycleTypeBuildpack,
   366  					LifecycleBuildpacks: []string{"buildpack-1", "buildpack-2"},
   367  				}))
   368  			})
   369  		})
   370  
   371  		When("the cc client returns an error", func() {
   372  			var expectedError error
   373  
   374  			BeforeEach(func() {
   375  				expectedError = errors.New("I am a CloudControllerClient Error")
   376  				fakeCloudControllerClient.CreateApplicationReturns(
   377  					ccv3.Application{},
   378  					ccv3.Warnings{"some-warning"},
   379  					expectedError,
   380  				)
   381  			})
   382  
   383  			It("raises the error and warnings", func() {
   384  				Expect(err).To(MatchError(expectedError))
   385  				Expect(warnings).To(ConsistOf("some-warning"))
   386  			})
   387  		})
   388  
   389  		When("the cc client returns an NameNotUniqueInSpaceError", func() {
   390  			BeforeEach(func() {
   391  				fakeCloudControllerClient.CreateApplicationReturns(
   392  					ccv3.Application{},
   393  					ccv3.Warnings{"some-warning"},
   394  					ccerror.NameNotUniqueInSpaceError{},
   395  				)
   396  			})
   397  
   398  			It("returns the NameNotUniqueInSpaceError and warnings", func() {
   399  				Expect(err).To(MatchError(ccerror.NameNotUniqueInSpaceError{}))
   400  				Expect(warnings).To(ConsistOf("some-warning"))
   401  			})
   402  		})
   403  	})
   404  
   405  	Describe("UpdateApplication", func() {
   406  		var (
   407  			submitApp, resultApp Application
   408  			warnings             Warnings
   409  			err                  error
   410  		)
   411  
   412  		JustBeforeEach(func() {
   413  			submitApp = Application{
   414  				GUID:                "some-app-guid",
   415  				StackName:           "some-stack-name",
   416  				LifecycleType:       constant.AppLifecycleTypeBuildpack,
   417  				LifecycleBuildpacks: []string{"buildpack-1", "buildpack-2"},
   418  			}
   419  			resultApp, warnings, err = actor.UpdateApplication(submitApp)
   420  		})
   421  
   422  		When("the app successfully gets updated", func() {
   423  			var apiResponseApp ccv3.Application
   424  
   425  			BeforeEach(func() {
   426  				apiResponseApp = ccv3.Application{
   427  					GUID:                "response-app-guid",
   428  					StackName:           "response-stack-name",
   429  					LifecycleType:       constant.AppLifecycleTypeBuildpack,
   430  					LifecycleBuildpacks: []string{"response-buildpack-1", "response-buildpack-2"},
   431  					Relationships: ccv3.Relationships{
   432  						constant.RelationshipTypeSpace: ccv3.Relationship{GUID: "some-space-guid"},
   433  					},
   434  				}
   435  				fakeCloudControllerClient.UpdateApplicationReturns(
   436  					apiResponseApp,
   437  					ccv3.Warnings{"some-warning"},
   438  					nil,
   439  				)
   440  			})
   441  
   442  			It("creates and returns the application and warnings", func() {
   443  				Expect(err).ToNot(HaveOccurred())
   444  				Expect(resultApp).To(Equal(Application{
   445  					GUID:                apiResponseApp.GUID,
   446  					StackName:           apiResponseApp.StackName,
   447  					LifecycleType:       apiResponseApp.LifecycleType,
   448  					LifecycleBuildpacks: apiResponseApp.LifecycleBuildpacks,
   449  					SpaceGUID:           "some-space-guid",
   450  				}))
   451  				Expect(warnings).To(ConsistOf("some-warning"))
   452  
   453  				Expect(fakeCloudControllerClient.UpdateApplicationCallCount()).To(Equal(1))
   454  				Expect(fakeCloudControllerClient.UpdateApplicationArgsForCall(0)).To(Equal(ccv3.Application{
   455  					GUID:                submitApp.GUID,
   456  					StackName:           submitApp.StackName,
   457  					LifecycleType:       submitApp.LifecycleType,
   458  					LifecycleBuildpacks: submitApp.LifecycleBuildpacks,
   459  				}))
   460  			})
   461  		})
   462  
   463  		When("the cc client returns an error", func() {
   464  			var expectedError error
   465  
   466  			BeforeEach(func() {
   467  				expectedError = errors.New("I am a CloudControllerClient Error")
   468  				fakeCloudControllerClient.UpdateApplicationReturns(
   469  					ccv3.Application{},
   470  					ccv3.Warnings{"some-warning"},
   471  					expectedError,
   472  				)
   473  			})
   474  
   475  			It("raises the error and warnings", func() {
   476  				Expect(err).To(MatchError(expectedError))
   477  				Expect(warnings).To(ConsistOf("some-warning"))
   478  			})
   479  		})
   480  	})
   481  
   482  	Describe("PollStart", func() {
   483  		var warningsChannel chan Warnings
   484  		var allWarnings Warnings
   485  		var funcDone chan interface{}
   486  
   487  		BeforeEach(func() {
   488  			warningsChannel = make(chan Warnings)
   489  			funcDone = make(chan interface{})
   490  			allWarnings = Warnings{}
   491  			go func() {
   492  				for {
   493  					select {
   494  					case warnings := <-warningsChannel:
   495  						allWarnings = append(allWarnings, warnings...)
   496  					case <-funcDone:
   497  						return
   498  					}
   499  				}
   500  			}()
   501  		})
   502  
   503  		When("getting the application processes fails", func() {
   504  			BeforeEach(func() {
   505  				fakeCloudControllerClient.GetApplicationProcessesReturns(nil, ccv3.Warnings{"get-app-warning-1", "get-app-warning-2"}, errors.New("some-error"))
   506  			})
   507  
   508  			It("returns the error and all warnings", func() {
   509  				err := actor.PollStart("some-guid", warningsChannel)
   510  				funcDone <- nil
   511  				Expect(allWarnings).To(ConsistOf("get-app-warning-1", "get-app-warning-2"))
   512  				Expect(err).To(MatchError(errors.New("some-error")))
   513  			})
   514  		})
   515  
   516  		When("getting the application processes succeeds", func() {
   517  			var processes []ccv3.Process
   518  
   519  			BeforeEach(func() {
   520  				fakeConfig.StartupTimeoutReturns(time.Second)
   521  				fakeConfig.PollingIntervalReturns(0)
   522  			})
   523  
   524  			JustBeforeEach(func() {
   525  				fakeCloudControllerClient.GetApplicationProcessesReturns(
   526  					processes,
   527  					ccv3.Warnings{"get-app-warning-1"}, nil)
   528  			})
   529  
   530  			When("there is a single process", func() {
   531  				BeforeEach(func() {
   532  					processes = []ccv3.Process{{GUID: "abc123"}}
   533  				})
   534  
   535  				When("the polling times out", func() {
   536  					BeforeEach(func() {
   537  						fakeConfig.StartupTimeoutReturns(time.Millisecond)
   538  						fakeConfig.PollingIntervalReturns(time.Millisecond * 2)
   539  						fakeCloudControllerClient.GetProcessInstancesReturns(
   540  							[]ccv3.ProcessInstance{{State: constant.ProcessInstanceStarting}},
   541  							ccv3.Warnings{"get-process-warning-1", "get-process-warning-2"},
   542  							nil,
   543  						)
   544  					})
   545  
   546  					It("returns the timeout error", func() {
   547  						err := actor.PollStart("some-guid", warningsChannel)
   548  						funcDone <- nil
   549  
   550  						Expect(allWarnings).To(ConsistOf("get-app-warning-1", "get-process-warning-1", "get-process-warning-2"))
   551  						Expect(err).To(MatchError(actionerror.StartupTimeoutError{}))
   552  					})
   553  
   554  					It("gets polling and timeout values from the config", func() {
   555  						actor.PollStart("some-guid", warningsChannel)
   556  						funcDone <- nil
   557  
   558  						Expect(fakeConfig.StartupTimeoutCallCount()).To(Equal(1))
   559  						Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(1))
   560  					})
   561  				})
   562  
   563  				When("getting the process instances errors", func() {
   564  					BeforeEach(func() {
   565  						fakeCloudControllerClient.GetProcessInstancesReturns(
   566  							nil,
   567  							ccv3.Warnings{"get-process-warning-1", "get-process-warning-2"},
   568  							errors.New("some-error"),
   569  						)
   570  					})
   571  
   572  					It("returns the error", func() {
   573  						err := actor.PollStart("some-guid", warningsChannel)
   574  						funcDone <- nil
   575  
   576  						Expect(allWarnings).To(ConsistOf("get-app-warning-1", "get-process-warning-1", "get-process-warning-2"))
   577  						Expect(err).To(MatchError("some-error"))
   578  					})
   579  				})
   580  
   581  				When("getting the process instances succeeds", func() {
   582  					var (
   583  						initialInstanceStates    []ccv3.ProcessInstance
   584  						eventualInstanceStates   []ccv3.ProcessInstance
   585  						pollStartErr             error
   586  						processInstanceCallCount int
   587  					)
   588  
   589  					BeforeEach(func() {
   590  						processInstanceCallCount = 0
   591  					})
   592  
   593  					JustBeforeEach(func() {
   594  						fakeCloudControllerClient.GetProcessInstancesStub = func(processGuid string) ([]ccv3.ProcessInstance, ccv3.Warnings, error) {
   595  							defer func() { processInstanceCallCount++ }()
   596  							if processInstanceCallCount == 0 {
   597  								return initialInstanceStates,
   598  									ccv3.Warnings{"get-process-warning-1", "get-process-warning-2"},
   599  									nil
   600  							} else {
   601  								return eventualInstanceStates,
   602  									ccv3.Warnings{fmt.Sprintf("get-process-warning-%d", processInstanceCallCount+2)},
   603  									nil
   604  							}
   605  						}
   606  
   607  						pollStartErr = actor.PollStart("some-guid", warningsChannel)
   608  						funcDone <- nil
   609  					})
   610  
   611  					When("there are no process instances", func() {
   612  						BeforeEach(func() {
   613  							initialInstanceStates = []ccv3.ProcessInstance{}
   614  							eventualInstanceStates = []ccv3.ProcessInstance{}
   615  						})
   616  
   617  						It("should not return an error", func() {
   618  							Expect(pollStartErr).NotTo(HaveOccurred())
   619  						})
   620  
   621  						It("should only call GetProcessInstances once before exiting", func() {
   622  							Expect(processInstanceCallCount).To(Equal(1))
   623  						})
   624  
   625  						It("should return correct warnings", func() {
   626  							Expect(allWarnings).To(ConsistOf("get-app-warning-1", "get-process-warning-1", "get-process-warning-2"))
   627  						})
   628  					})
   629  
   630  					When("all instances become running by the second call", func() {
   631  						BeforeEach(func() {
   632  							initialInstanceStates = []ccv3.ProcessInstance{{State: constant.ProcessInstanceStarting}, {State: constant.ProcessInstanceStarting}}
   633  							eventualInstanceStates = []ccv3.ProcessInstance{{State: constant.ProcessInstanceRunning}, {State: constant.ProcessInstanceRunning}}
   634  						})
   635  
   636  						It("should not return an error", func() {
   637  							Expect(pollStartErr).NotTo(HaveOccurred())
   638  						})
   639  
   640  						It("should call GetProcessInstances twice", func() {
   641  							Expect(processInstanceCallCount).To(Equal(2))
   642  						})
   643  
   644  						It("should return correct warnings", func() {
   645  							Expect(allWarnings).To(ConsistOf("get-app-warning-1", "get-process-warning-1", "get-process-warning-2", "get-process-warning-3"))
   646  						})
   647  					})
   648  
   649  					When("at least one instance has become running by the second call", func() {
   650  						BeforeEach(func() {
   651  							initialInstanceStates = []ccv3.ProcessInstance{{State: constant.ProcessInstanceStarting}, {State: constant.ProcessInstanceStarting}, {State: constant.ProcessInstanceStarting}}
   652  							eventualInstanceStates = []ccv3.ProcessInstance{{State: constant.ProcessInstanceStarting}, {State: constant.ProcessInstanceStarting}, {State: constant.ProcessInstanceRunning}}
   653  						})
   654  
   655  						It("should not return an error", func() {
   656  							Expect(pollStartErr).NotTo(HaveOccurred())
   657  						})
   658  
   659  						It("should call GetProcessInstances twice", func() {
   660  							Expect(processInstanceCallCount).To(Equal(2))
   661  						})
   662  
   663  						It("should return correct warnings", func() {
   664  							Expect(len(allWarnings)).To(Equal(4))
   665  							Expect(allWarnings).To(ConsistOf("get-app-warning-1", "get-process-warning-1", "get-process-warning-2", "get-process-warning-3"))
   666  						})
   667  					})
   668  
   669  					When("all of the instances have crashed by the second call", func() {
   670  						BeforeEach(func() {
   671  							initialInstanceStates = []ccv3.ProcessInstance{{State: constant.ProcessInstanceStarting}, {State: constant.ProcessInstanceStarting}, {State: constant.ProcessInstanceStarting}}
   672  							eventualInstanceStates = []ccv3.ProcessInstance{{State: constant.ProcessInstanceCrashed}, {State: constant.ProcessInstanceCrashed}, {State: constant.ProcessInstanceCrashed}}
   673  						})
   674  
   675  						It("should not return an error", func() {
   676  							Expect(pollStartErr).NotTo(HaveOccurred())
   677  						})
   678  
   679  						It("should call GetProcessInstances twice", func() {
   680  							Expect(processInstanceCallCount).To(Equal(2))
   681  						})
   682  
   683  						It("should return correct warnings", func() {
   684  							Expect(len(allWarnings)).To(Equal(4))
   685  							Expect(allWarnings).To(ConsistOf("get-app-warning-1", "get-process-warning-1", "get-process-warning-2", "get-process-warning-3"))
   686  						})
   687  					})
   688  				})
   689  			})
   690  
   691  			Context("where there are multiple processes", func() {
   692  				var (
   693  					pollStartErr             error
   694  					processInstanceCallCount int
   695  				)
   696  
   697  				BeforeEach(func() {
   698  					processInstanceCallCount = 0
   699  					fakeConfig.StartupTimeoutReturns(time.Millisecond)
   700  					fakeConfig.PollingIntervalReturns(time.Millisecond * 2)
   701  				})
   702  
   703  				JustBeforeEach(func() {
   704  					fakeCloudControllerClient.GetProcessInstancesStub = func(processGuid string) ([]ccv3.ProcessInstance, ccv3.Warnings, error) {
   705  						defer func() { processInstanceCallCount++ }()
   706  						if strings.HasPrefix(processGuid, "good") {
   707  							return []ccv3.ProcessInstance{{State: constant.ProcessInstanceRunning}}, nil, nil
   708  						} else {
   709  							return []ccv3.ProcessInstance{{State: constant.ProcessInstanceStarting}}, nil, nil
   710  						}
   711  					}
   712  
   713  					pollStartErr = actor.PollStart("some-guid", warningsChannel)
   714  					funcDone <- nil
   715  				})
   716  
   717  				When("none of the processes are ready", func() {
   718  					BeforeEach(func() {
   719  						processes = []ccv3.Process{{GUID: "bad-1"}, {GUID: "bad-2"}}
   720  					})
   721  
   722  					It("returns the timeout error", func() {
   723  						Expect(pollStartErr).To(MatchError(actionerror.StartupTimeoutError{}))
   724  					})
   725  
   726  				})
   727  
   728  				When("some of the processes are ready", func() {
   729  					BeforeEach(func() {
   730  						processes = []ccv3.Process{{GUID: "bad-1"}, {GUID: "good-1"}}
   731  					})
   732  
   733  					It("returns the timeout error", func() {
   734  						Expect(pollStartErr).To(MatchError(actionerror.StartupTimeoutError{}))
   735  					})
   736  				})
   737  
   738  				When("all of the processes are ready", func() {
   739  					BeforeEach(func() {
   740  						processes = []ccv3.Process{{GUID: "good-1"}, {GUID: "good-2"}}
   741  					})
   742  
   743  					It("returns nil", func() {
   744  						Expect(pollStartErr).ToNot(HaveOccurred())
   745  					})
   746  				})
   747  			})
   748  		})
   749  	})
   750  
   751  	Describe("StopApplication", func() {
   752  		var (
   753  			warnings   Warnings
   754  			executeErr error
   755  		)
   756  
   757  		JustBeforeEach(func() {
   758  			warnings, executeErr = actor.StopApplication("some-app-guid")
   759  		})
   760  
   761  		When("there are no client errors", func() {
   762  			BeforeEach(func() {
   763  				fakeCloudControllerClient.UpdateApplicationStopReturns(
   764  					ccv3.Application{GUID: "some-app-guid"},
   765  					ccv3.Warnings{"stop-application-warning"},
   766  					nil,
   767  				)
   768  			})
   769  
   770  			It("stops the application", func() {
   771  				Expect(executeErr).ToNot(HaveOccurred())
   772  				Expect(warnings).To(ConsistOf("stop-application-warning"))
   773  
   774  				Expect(fakeCloudControllerClient.UpdateApplicationStopCallCount()).To(Equal(1))
   775  				Expect(fakeCloudControllerClient.UpdateApplicationStopArgsForCall(0)).To(Equal("some-app-guid"))
   776  			})
   777  		})
   778  
   779  		When("stopping the application fails", func() {
   780  			var expectedErr error
   781  			BeforeEach(func() {
   782  				expectedErr = errors.New("some set stop-application error")
   783  				fakeCloudControllerClient.UpdateApplicationStopReturns(
   784  					ccv3.Application{},
   785  					ccv3.Warnings{"stop-application-warning"},
   786  					expectedErr,
   787  				)
   788  			})
   789  
   790  			It("returns the error", func() {
   791  				Expect(executeErr).To(Equal(expectedErr))
   792  				Expect(warnings).To(ConsistOf("stop-application-warning"))
   793  			})
   794  		})
   795  	})
   796  
   797  	Describe("StartApplication", func() {
   798  		When("there are no client errors", func() {
   799  			BeforeEach(func() {
   800  				fakeCloudControllerClient.UpdateApplicationStartReturns(
   801  					ccv3.Application{GUID: "some-app-guid"},
   802  					ccv3.Warnings{"start-application-warning"},
   803  					nil,
   804  				)
   805  			})
   806  
   807  			It("starts the application", func() {
   808  				warnings, err := actor.StartApplication("some-app-guid")
   809  
   810  				Expect(err).ToNot(HaveOccurred())
   811  				Expect(warnings).To(ConsistOf("start-application-warning"))
   812  
   813  				Expect(fakeCloudControllerClient.UpdateApplicationStartCallCount()).To(Equal(1))
   814  				Expect(fakeCloudControllerClient.UpdateApplicationStartArgsForCall(0)).To(Equal("some-app-guid"))
   815  			})
   816  		})
   817  
   818  		When("starting the application fails", func() {
   819  			var expectedErr error
   820  			BeforeEach(func() {
   821  				expectedErr = errors.New("some set start-application error")
   822  				fakeCloudControllerClient.UpdateApplicationStartReturns(
   823  					ccv3.Application{},
   824  					ccv3.Warnings{"start-application-warning"},
   825  					expectedErr,
   826  				)
   827  			})
   828  
   829  			It("returns the error", func() {
   830  				warnings, err := actor.StartApplication("some-app-guid")
   831  
   832  				Expect(err).To(Equal(expectedErr))
   833  				Expect(warnings).To(ConsistOf("start-application-warning"))
   834  			})
   835  		})
   836  	})
   837  
   838  	Describe("RestartApplication", func() {
   839  		var (
   840  			warnings   Warnings
   841  			executeErr error
   842  		)
   843  
   844  		JustBeforeEach(func() {
   845  			warnings, executeErr = actor.RestartApplication("some-app-guid")
   846  		})
   847  
   848  		When("there are no client errors", func() {
   849  			BeforeEach(func() {
   850  				fakeCloudControllerClient.UpdateApplicationRestartReturns(
   851  					ccv3.Application{GUID: "some-app-guid"},
   852  					ccv3.Warnings{"restart-application-warning"},
   853  					nil,
   854  				)
   855  			})
   856  
   857  			It("stops the application", func() {
   858  				Expect(executeErr).ToNot(HaveOccurred())
   859  				Expect(warnings).To(ConsistOf("restart-application-warning"))
   860  
   861  				Expect(fakeCloudControllerClient.UpdateApplicationRestartCallCount()).To(Equal(1))
   862  				Expect(fakeCloudControllerClient.UpdateApplicationRestartArgsForCall(0)).To(Equal("some-app-guid"))
   863  			})
   864  		})
   865  
   866  		When("restarting the application fails", func() {
   867  			var expectedErr error
   868  			BeforeEach(func() {
   869  				expectedErr = errors.New("some set restart-application error")
   870  				fakeCloudControllerClient.UpdateApplicationRestartReturns(
   871  					ccv3.Application{},
   872  					ccv3.Warnings{"restart-application-warning"},
   873  					expectedErr,
   874  				)
   875  			})
   876  
   877  			It("returns the error", func() {
   878  				Expect(executeErr).To(Equal(expectedErr))
   879  				Expect(warnings).To(ConsistOf("restart-application-warning"))
   880  			})
   881  		})
   882  	})
   883  
   884  })