github.com/liamawhite/cli-with-i18n@v6.32.1-0.20171122084555-dede0a5c3448+incompatible/actor/v3action/application_test.go (about)

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