github.com/jghiloni/cli@v6.28.1-0.20170628223758-0ce05fe032a2+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  	. "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  
    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(fakeCloudControllerClient, fakeConfig)
    30  	})
    31  
    32  	Describe("GetApplicationByNameAndSpace", func() {
    33  		Context("when the app exists", func() {
    34  			BeforeEach(func() {
    35  				fakeCloudControllerClient.GetApplicationsReturns(
    36  					[]ccv3.Application{
    37  						{
    38  							Name: "some-app-name",
    39  							GUID: "some-app-guid",
    40  						},
    41  					},
    42  					ccv3.Warnings{"some-warning"},
    43  					nil,
    44  				)
    45  			})
    46  
    47  			It("returns the application and warnings", func() {
    48  				app, warnings, err := actor.GetApplicationByNameAndSpace("some-app-name", "some-space-guid")
    49  				Expect(err).ToNot(HaveOccurred())
    50  				Expect(app).To(Equal(Application{
    51  					Name: "some-app-name",
    52  					GUID: "some-app-guid",
    53  				}))
    54  				Expect(warnings).To(Equal(Warnings{"some-warning"}))
    55  
    56  				Expect(fakeCloudControllerClient.GetApplicationsCallCount()).To(Equal(1))
    57  				expectedQuery := url.Values{
    58  					"names":       []string{"some-app-name"},
    59  					"space_guids": []string{"some-space-guid"},
    60  				}
    61  				query := fakeCloudControllerClient.GetApplicationsArgsForCall(0)
    62  				Expect(query).To(Equal(expectedQuery))
    63  			})
    64  		})
    65  
    66  		Context("when the cloud controller client returns an error", func() {
    67  			var expectedError error
    68  
    69  			BeforeEach(func() {
    70  				expectedError = errors.New("I am a CloudControllerClient Error")
    71  				fakeCloudControllerClient.GetApplicationsReturns(
    72  					[]ccv3.Application{},
    73  					ccv3.Warnings{"some-warning"},
    74  					expectedError)
    75  			})
    76  
    77  			It("returns the warnings and the error", func() {
    78  				_, warnings, err := actor.GetApplicationByNameAndSpace("some-app-name", "some-space-guid")
    79  				Expect(warnings).To(ConsistOf("some-warning"))
    80  				Expect(err).To(MatchError(expectedError))
    81  				Expect(fakeCloudControllerClient.GetApplicationsCallCount()).To(Equal(1))
    82  				expectedQuery := url.Values{
    83  					"names":       []string{"some-app-name"},
    84  					"space_guids": []string{"some-space-guid"},
    85  				}
    86  				query := fakeCloudControllerClient.GetApplicationsArgsForCall(0)
    87  				Expect(query).To(Equal(expectedQuery))
    88  			})
    89  		})
    90  
    91  		Context("when the app does not exist", func() {
    92  			BeforeEach(func() {
    93  				fakeCloudControllerClient.GetApplicationsReturns(
    94  					[]ccv3.Application{},
    95  					ccv3.Warnings{"some-warning"},
    96  					nil,
    97  				)
    98  			})
    99  
   100  			It("returns an ApplicationNotFoundError and the warnings", func() {
   101  				_, warnings, err := actor.GetApplicationByNameAndSpace("some-app-name", "some-space-guid")
   102  				Expect(warnings).To(ConsistOf("some-warning"))
   103  				Expect(err).To(MatchError(
   104  					ApplicationNotFoundError{Name: "some-app-name"}))
   105  				Expect(fakeCloudControllerClient.GetApplicationsCallCount()).To(Equal(1))
   106  				expectedQuery := url.Values{
   107  					"names":       []string{"some-app-name"},
   108  					"space_guids": []string{"some-space-guid"},
   109  				}
   110  				query := fakeCloudControllerClient.GetApplicationsArgsForCall(0)
   111  				Expect(query).To(Equal(expectedQuery))
   112  			})
   113  		})
   114  	})
   115  
   116  	Describe("CreateApplicationByNameAndSpace", func() {
   117  		Context("when the app successfully gets created", func() {
   118  			BeforeEach(func() {
   119  				fakeCloudControllerClient.CreateApplicationReturns(
   120  					ccv3.Application{
   121  						Name: "some-app-name",
   122  						GUID: "some-app-guid",
   123  					},
   124  					ccv3.Warnings{"some-warning"},
   125  					nil,
   126  				)
   127  			})
   128  
   129  			It("creates and returns the application and warnings", func() {
   130  				app, warnings, err := actor.CreateApplicationByNameAndSpace("some-app-name", "some-space-guid")
   131  
   132  				Expect(err).ToNot(HaveOccurred())
   133  				Expect(app).To(Equal(Application{
   134  					Name: "some-app-name",
   135  					GUID: "some-app-guid",
   136  				}))
   137  				Expect(warnings).To(ConsistOf("some-warning"))
   138  
   139  				Expect(fakeCloudControllerClient.CreateApplicationCallCount()).To(Equal(1))
   140  				expectedApp := ccv3.Application{
   141  					Name: "some-app-name",
   142  					Relationships: ccv3.Relationships{
   143  						ccv3.SpaceRelationship: ccv3.Relationship{GUID: "some-space-guid"},
   144  					},
   145  				}
   146  				Expect(fakeCloudControllerClient.CreateApplicationArgsForCall(0)).To(Equal(expectedApp))
   147  			})
   148  		})
   149  
   150  		Context("when the cc client returns an error", func() {
   151  			var expectedError error
   152  
   153  			BeforeEach(func() {
   154  				expectedError = errors.New("I am a CloudControllerClient Error")
   155  				fakeCloudControllerClient.CreateApplicationReturns(
   156  					ccv3.Application{},
   157  					ccv3.Warnings{"some-warning"},
   158  					expectedError,
   159  				)
   160  			})
   161  
   162  			It("raises the error and warnings", func() {
   163  				_, warnings, err := actor.CreateApplicationByNameAndSpace("some-app-name", "some-space-guid")
   164  
   165  				Expect(err).To(MatchError(expectedError))
   166  				Expect(warnings).To(ConsistOf("some-warning"))
   167  			})
   168  		})
   169  
   170  		Context("when the cc client response contains an UnprocessableEntityError", func() {
   171  			BeforeEach(func() {
   172  				fakeCloudControllerClient.CreateApplicationReturns(
   173  					ccv3.Application{},
   174  					ccv3.Warnings{"some-warning"},
   175  					ccerror.UnprocessableEntityError{},
   176  				)
   177  			})
   178  
   179  			It("raises the error as ApplicationAlreadyExistsError and warnings", func() {
   180  				_, warnings, err := actor.CreateApplicationByNameAndSpace("some-app-name", "some-space-guid")
   181  
   182  				Expect(err).To(MatchError(ApplicationAlreadyExistsError{Name: "some-app-name"}))
   183  				Expect(warnings).To(ConsistOf("some-warning"))
   184  			})
   185  		})
   186  	})
   187  
   188  	Describe("PollStart", func() {
   189  		var warningsChannel chan Warnings
   190  		var allWarnings Warnings
   191  		var funcDone chan interface{}
   192  
   193  		BeforeEach(func() {
   194  			warningsChannel = make(chan Warnings)
   195  			funcDone = make(chan interface{})
   196  			allWarnings = Warnings{}
   197  			go func() {
   198  				for {
   199  					select {
   200  					case warnings := <-warningsChannel:
   201  						allWarnings = append(allWarnings, warnings...)
   202  					case <-funcDone:
   203  						return
   204  					}
   205  				}
   206  			}()
   207  		})
   208  
   209  		Context("when getting the application processes fails", func() {
   210  			BeforeEach(func() {
   211  				fakeCloudControllerClient.GetApplicationProcessesReturns(nil, ccv3.Warnings{"get-app-warning-1", "get-app-warning-2"}, errors.New("some-error"))
   212  			})
   213  
   214  			It("returns the error and all warnings", func() {
   215  				err := actor.PollStart("some-guid", warningsChannel)
   216  				funcDone <- nil
   217  				Expect(allWarnings).To(ConsistOf("get-app-warning-1", "get-app-warning-2"))
   218  				Expect(err).To(MatchError(errors.New("some-error")))
   219  			})
   220  		})
   221  
   222  		Context("when getting the application processes succeeds", func() {
   223  			var processes []ccv3.Process
   224  
   225  			BeforeEach(func() {
   226  				fakeConfig.StartupTimeoutReturns(time.Second)
   227  				fakeConfig.PollingIntervalReturns(0)
   228  			})
   229  
   230  			JustBeforeEach(func() {
   231  				fakeCloudControllerClient.GetApplicationProcessesReturns(
   232  					processes,
   233  					ccv3.Warnings{"get-app-warning-1"}, nil)
   234  			})
   235  
   236  			Context("when there is a single process", func() {
   237  				BeforeEach(func() {
   238  					processes = []ccv3.Process{{GUID: "abc123"}}
   239  				})
   240  
   241  				Context("when the polling times out", func() {
   242  					BeforeEach(func() {
   243  						fakeConfig.StartupTimeoutReturns(time.Millisecond)
   244  						fakeConfig.PollingIntervalReturns(time.Millisecond * 2)
   245  						fakeCloudControllerClient.GetProcessInstancesReturns(
   246  							[]ccv3.Instance{{State: "STARTING"}},
   247  							ccv3.Warnings{"get-process-warning-1", "get-process-warning-2"},
   248  							nil,
   249  						)
   250  					})
   251  
   252  					It("returns the timeout error", func() {
   253  						err := actor.PollStart("some-guid", warningsChannel)
   254  						funcDone <- nil
   255  
   256  						Expect(allWarnings).To(ConsistOf("get-app-warning-1", "get-process-warning-1", "get-process-warning-2"))
   257  						Expect(err).To(MatchError(StartupTimeoutError{}))
   258  					})
   259  
   260  					It("gets polling and timeout values from the config", func() {
   261  						actor.PollStart("some-guid", warningsChannel)
   262  						funcDone <- nil
   263  
   264  						Expect(fakeConfig.StartupTimeoutCallCount()).To(Equal(1))
   265  						Expect(fakeConfig.PollingIntervalCallCount()).To(Equal(1))
   266  					})
   267  				})
   268  
   269  				Context("when getting the process instances errors", func() {
   270  					BeforeEach(func() {
   271  						fakeCloudControllerClient.GetProcessInstancesReturns(
   272  							nil,
   273  							ccv3.Warnings{"get-process-warning-1", "get-process-warning-2"},
   274  							errors.New("some-error"),
   275  						)
   276  					})
   277  
   278  					It("returns the error", func() {
   279  						err := actor.PollStart("some-guid", warningsChannel)
   280  						funcDone <- nil
   281  
   282  						Expect(allWarnings).To(ConsistOf("get-app-warning-1", "get-process-warning-1", "get-process-warning-2"))
   283  						Expect(err).To(MatchError("some-error"))
   284  					})
   285  				})
   286  
   287  				Context("when getting the process instances succeeds", func() {
   288  					var (
   289  						initialInstanceStates    []ccv3.Instance
   290  						eventualInstanceStates   []ccv3.Instance
   291  						pollStartErr             error
   292  						processInstanceCallCount int
   293  					)
   294  
   295  					BeforeEach(func() {
   296  						processInstanceCallCount = 0
   297  					})
   298  
   299  					JustBeforeEach(func() {
   300  						fakeCloudControllerClient.GetProcessInstancesStub = func(processGuid string) ([]ccv3.Instance, ccv3.Warnings, error) {
   301  							defer func() { processInstanceCallCount++ }()
   302  							if processInstanceCallCount == 0 {
   303  								return initialInstanceStates,
   304  									ccv3.Warnings{"get-process-warning-1", "get-process-warning-2"},
   305  									nil
   306  							} else {
   307  								return eventualInstanceStates,
   308  									ccv3.Warnings{fmt.Sprintf("get-process-warning-%d", processInstanceCallCount+2)},
   309  									nil
   310  							}
   311  						}
   312  
   313  						pollStartErr = actor.PollStart("some-guid", warningsChannel)
   314  						funcDone <- nil
   315  					})
   316  
   317  					Context("when there are no process instances", func() {
   318  						BeforeEach(func() {
   319  							initialInstanceStates = []ccv3.Instance{}
   320  							eventualInstanceStates = []ccv3.Instance{}
   321  						})
   322  
   323  						It("should not return an error", func() {
   324  							Expect(pollStartErr).NotTo(HaveOccurred())
   325  						})
   326  
   327  						It("should only call GetProcessInstances once before exiting", func() {
   328  							Expect(processInstanceCallCount).To(Equal(1))
   329  						})
   330  
   331  						It("should return correct warnings", func() {
   332  							Expect(allWarnings).To(ConsistOf("get-app-warning-1", "get-process-warning-1", "get-process-warning-2"))
   333  						})
   334  					})
   335  
   336  					Context("when all instances become running by the second call", func() {
   337  						BeforeEach(func() {
   338  							initialInstanceStates = []ccv3.Instance{{State: "STARTING"}, {State: "STARTING"}}
   339  							eventualInstanceStates = []ccv3.Instance{{State: "RUNNING"}, {State: "RUNNING"}}
   340  						})
   341  
   342  						It("should not return an error", func() {
   343  							Expect(pollStartErr).NotTo(HaveOccurred())
   344  						})
   345  
   346  						It("should call GetProcessInstances twice", func() {
   347  							Expect(processInstanceCallCount).To(Equal(2))
   348  						})
   349  
   350  						It("should return correct warnings", func() {
   351  							Expect(allWarnings).To(ConsistOf("get-app-warning-1", "get-process-warning-1", "get-process-warning-2", "get-process-warning-3"))
   352  						})
   353  					})
   354  
   355  					Context("when at least one instance has become running by the second call", func() {
   356  						BeforeEach(func() {
   357  							initialInstanceStates = []ccv3.Instance{{State: "STARTING"}, {State: "STARTING"}, {State: "STARTING"}}
   358  							eventualInstanceStates = []ccv3.Instance{{State: "STARTING"}, {State: "STARTING"}, {State: "RUNNING"}}
   359  						})
   360  
   361  						It("should not return an error", func() {
   362  							Expect(pollStartErr).NotTo(HaveOccurred())
   363  						})
   364  
   365  						It("should call GetProcessInstances twice", func() {
   366  							Expect(processInstanceCallCount).To(Equal(2))
   367  						})
   368  
   369  						It("should return correct warnings", func() {
   370  							Expect(len(allWarnings)).To(Equal(4))
   371  							Expect(allWarnings).To(ConsistOf("get-app-warning-1", "get-process-warning-1", "get-process-warning-2", "get-process-warning-3"))
   372  						})
   373  					})
   374  				})
   375  			})
   376  
   377  			Context("where there are multiple processes", func() {
   378  				var (
   379  					pollStartErr             error
   380  					processInstanceCallCount int
   381  				)
   382  
   383  				BeforeEach(func() {
   384  					processInstanceCallCount = 0
   385  					fakeConfig.StartupTimeoutReturns(time.Millisecond)
   386  					fakeConfig.PollingIntervalReturns(time.Millisecond * 2)
   387  				})
   388  
   389  				JustBeforeEach(func() {
   390  					fakeCloudControllerClient.GetProcessInstancesStub = func(processGuid string) ([]ccv3.Instance, ccv3.Warnings, error) {
   391  						defer func() { processInstanceCallCount++ }()
   392  						if strings.HasPrefix(processGuid, "good") {
   393  							return []ccv3.Instance{ccv3.Instance{State: "RUNNING"}}, nil, nil
   394  						} else {
   395  							return []ccv3.Instance{ccv3.Instance{State: "STARTING"}}, nil, nil
   396  						}
   397  					}
   398  
   399  					pollStartErr = actor.PollStart("some-guid", warningsChannel)
   400  					funcDone <- nil
   401  				})
   402  
   403  				Context("when none of the processes are ready", func() {
   404  					BeforeEach(func() {
   405  						processes = []ccv3.Process{{GUID: "bad-1"}, {GUID: "bad-2"}}
   406  					})
   407  
   408  					It("returns the timeout error", func() {
   409  						Expect(pollStartErr).To(MatchError(StartupTimeoutError{}))
   410  					})
   411  
   412  				})
   413  
   414  				Context("when some of the processes are ready", func() {
   415  					BeforeEach(func() {
   416  						processes = []ccv3.Process{{GUID: "bad-1"}, {GUID: "good-1"}}
   417  					})
   418  
   419  					It("returns the timeout error", func() {
   420  						Expect(pollStartErr).To(MatchError(StartupTimeoutError{}))
   421  					})
   422  				})
   423  
   424  				Context("when all of the processes are ready", func() {
   425  					BeforeEach(func() {
   426  						processes = []ccv3.Process{{GUID: "good-1"}, {GUID: "good-2"}}
   427  					})
   428  
   429  					It("returns nil", func() {
   430  						Expect(pollStartErr).ToNot(HaveOccurred())
   431  					})
   432  				})
   433  			})
   434  		})
   435  	})
   436  
   437  	Describe("StopApplication", func() {
   438  		Context("when there are no client errors", func() {
   439  			BeforeEach(func() {
   440  				fakeCloudControllerClient.GetApplicationsReturns(
   441  					[]ccv3.Application{
   442  						{GUID: "some-app-guid"},
   443  					},
   444  					ccv3.Warnings{"get-applications-warning"},
   445  					nil,
   446  				)
   447  
   448  				fakeCloudControllerClient.StopApplicationReturns(
   449  					ccv3.Warnings{"stop-application-warning"},
   450  					nil,
   451  				)
   452  			})
   453  
   454  			It("stops the application", func() {
   455  				warnings, err := actor.StopApplication("some-app-name", "some-space-guid")
   456  
   457  				Expect(err).ToNot(HaveOccurred())
   458  				Expect(warnings).To(ConsistOf("get-applications-warning", "stop-application-warning"))
   459  
   460  				Expect(fakeCloudControllerClient.GetApplicationsCallCount()).To(Equal(1))
   461  				queryURL := fakeCloudControllerClient.GetApplicationsArgsForCall(0)
   462  				query := url.Values{"names": []string{"some-app-name"}, "space_guids": []string{"some-space-guid"}}
   463  				Expect(queryURL).To(Equal(query))
   464  
   465  				Expect(fakeCloudControllerClient.StopApplicationCallCount()).To(Equal(1))
   466  				appGUID := fakeCloudControllerClient.StopApplicationArgsForCall(0)
   467  				Expect(appGUID).To(Equal("some-app-guid"))
   468  			})
   469  		})
   470  
   471  		Context("when getting the application fails", func() {
   472  			var expectedErr error
   473  
   474  			BeforeEach(func() {
   475  				expectedErr = errors.New("some get application error")
   476  
   477  				fakeCloudControllerClient.GetApplicationsReturns(
   478  					[]ccv3.Application{},
   479  					ccv3.Warnings{"get-applications-warning"},
   480  					expectedErr,
   481  				)
   482  			})
   483  
   484  			It("returns the error", func() {
   485  				warnings, err := actor.StopApplication("some-app-name", "some-space-guid")
   486  
   487  				Expect(err).To(Equal(expectedErr))
   488  				Expect(warnings).To(ConsistOf("get-applications-warning"))
   489  			})
   490  		})
   491  
   492  		Context("when stopping the application fails", func() {
   493  			var expectedErr error
   494  			BeforeEach(func() {
   495  				expectedErr = errors.New("some set stop-application error")
   496  				fakeCloudControllerClient.GetApplicationsReturns(
   497  					[]ccv3.Application{
   498  						{GUID: "some-app-guid"},
   499  					},
   500  					ccv3.Warnings{"get-applications-warning"},
   501  					nil,
   502  				)
   503  
   504  				fakeCloudControllerClient.StopApplicationReturns(
   505  					ccv3.Warnings{"stop-application-warning"},
   506  					expectedErr,
   507  				)
   508  			})
   509  
   510  			It("returns the error", func() {
   511  				warnings, err := actor.StopApplication("some-app-name", "some-space-guid")
   512  
   513  				Expect(err).To(Equal(expectedErr))
   514  				Expect(warnings).To(ConsistOf("get-applications-warning", "stop-application-warning"))
   515  			})
   516  		})
   517  	})
   518  
   519  	Describe("StartApplication", func() {
   520  		Context("when there are no client errors", func() {
   521  			BeforeEach(func() {
   522  				fakeCloudControllerClient.GetApplicationsReturns(
   523  					[]ccv3.Application{
   524  						{GUID: "some-app-guid"},
   525  					},
   526  					ccv3.Warnings{"get-applications-warning"},
   527  					nil,
   528  				)
   529  
   530  				fakeCloudControllerClient.StartApplicationReturns(
   531  					ccv3.Application{GUID: "some-app-guid"},
   532  					ccv3.Warnings{"start-application-warning"},
   533  					nil,
   534  				)
   535  			})
   536  
   537  			It("starts the application", func() {
   538  				app, warnings, err := actor.StartApplication("some-app-name", "some-space-guid")
   539  
   540  				Expect(err).ToNot(HaveOccurred())
   541  				Expect(warnings).To(ConsistOf("get-applications-warning", "start-application-warning"))
   542  				Expect(app).To(Equal(Application{GUID: "some-app-guid"}))
   543  
   544  				Expect(fakeCloudControllerClient.GetApplicationsCallCount()).To(Equal(1))
   545  				queryURL := fakeCloudControllerClient.GetApplicationsArgsForCall(0)
   546  				query := url.Values{"names": []string{"some-app-name"}, "space_guids": []string{"some-space-guid"}}
   547  				Expect(queryURL).To(Equal(query))
   548  
   549  				Expect(fakeCloudControllerClient.StartApplicationCallCount()).To(Equal(1))
   550  				appGUID := fakeCloudControllerClient.StartApplicationArgsForCall(0)
   551  				Expect(appGUID).To(Equal("some-app-guid"))
   552  			})
   553  		})
   554  
   555  		Context("when getting the application fails", func() {
   556  			var expectedErr error
   557  
   558  			BeforeEach(func() {
   559  				expectedErr = errors.New("some get application error")
   560  
   561  				fakeCloudControllerClient.GetApplicationsReturns(
   562  					[]ccv3.Application{},
   563  					ccv3.Warnings{"get-applications-warning"},
   564  					expectedErr,
   565  				)
   566  			})
   567  
   568  			It("returns the error", func() {
   569  				_, warnings, err := actor.StartApplication("some-app-name", "some-space-guid")
   570  
   571  				Expect(err).To(Equal(expectedErr))
   572  				Expect(warnings).To(ConsistOf("get-applications-warning"))
   573  			})
   574  		})
   575  
   576  		Context("when starting the application fails", func() {
   577  			var expectedErr error
   578  			BeforeEach(func() {
   579  				expectedErr = errors.New("some set start-application error")
   580  				fakeCloudControllerClient.GetApplicationsReturns(
   581  					[]ccv3.Application{
   582  						{GUID: "some-app-guid"},
   583  					},
   584  					ccv3.Warnings{"get-applications-warning"},
   585  					nil,
   586  				)
   587  
   588  				fakeCloudControllerClient.StartApplicationReturns(
   589  					ccv3.Application{},
   590  					ccv3.Warnings{"start-application-warning"},
   591  					expectedErr,
   592  				)
   593  			})
   594  
   595  			It("returns the error", func() {
   596  				_, warnings, err := actor.StartApplication("some-app-name", "some-space-guid")
   597  
   598  				Expect(err).To(Equal(expectedErr))
   599  				Expect(warnings).To(ConsistOf("get-applications-warning", "start-application-warning"))
   600  			})
   601  		})
   602  	})
   603  })