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 })