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