github.com/cloudfoundry-community/cloudfoundry-cli@v6.44.1-0.20240130060226-cda5ed8e89a5+incompatible/actor/v7action/application_test.go (about)

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