github.com/sleungcy-sap/cli@v7.1.0+incompatible/api/cloudcontroller/ccv3/application_test.go (about) 1 package ccv3_test 2 3 import ( 4 "encoding/json" 5 "net/http" 6 7 "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" 8 . "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" 9 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/ccv3fakes" 10 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant" 11 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/internal" 12 "code.cloudfoundry.org/cli/resources" 13 "code.cloudfoundry.org/cli/types" 14 . "github.com/onsi/ginkgo" 15 . "github.com/onsi/gomega" 16 ) 17 18 var _ = Describe("Application", func() { 19 var ( 20 client *Client 21 requester *ccv3fakes.FakeRequester 22 ) 23 24 BeforeEach(func() { 25 requester = new(ccv3fakes.FakeRequester) 26 client, _ = NewFakeRequesterTestClient(requester) 27 }) 28 29 Describe("Application", func() { 30 Describe("MarshalJSON", func() { 31 var ( 32 app resources.Application 33 appBytes []byte 34 err error 35 ) 36 37 BeforeEach(func() { 38 app = resources.Application{} 39 }) 40 41 JustBeforeEach(func() { 42 appBytes, err = app.MarshalJSON() 43 Expect(err).ToNot(HaveOccurred()) 44 }) 45 46 When("no lifecycle is provided", func() { 47 BeforeEach(func() { 48 app = resources.Application{} 49 }) 50 51 It("omits the lifecycle from the JSON", func() { 52 Expect(string(appBytes)).To(Equal("{}")) 53 }) 54 }) 55 56 When("lifecycle type docker is provided", func() { 57 BeforeEach(func() { 58 app = resources.Application{ 59 LifecycleType: constant.AppLifecycleTypeDocker, 60 } 61 }) 62 63 It("sets lifecycle type to docker with empty data", func() { 64 Expect(string(appBytes)).To(MatchJSON(`{"lifecycle":{"type":"docker","data":{}}}`)) 65 }) 66 }) 67 68 When("lifecycle type buildpack is provided", func() { 69 BeforeEach(func() { 70 app.LifecycleType = constant.AppLifecycleTypeBuildpack 71 }) 72 73 When("no buildpacks are provided", func() { 74 It("omits the lifecycle from the JSON", func() { 75 Expect(string(appBytes)).To(MatchJSON("{}")) 76 }) 77 78 When("but you do specify a stack", func() { 79 BeforeEach(func() { 80 app.StackName = "cflinuxfs9000" 81 }) 82 83 It("does, in fact, send the stack in the json", func() { 84 Expect(string(appBytes)).To(MatchJSON(`{"lifecycle":{"data":{"stack":"cflinuxfs9000"},"type":"buildpack"}}`)) 85 }) 86 }) 87 }) 88 89 When("default buildpack is provided", func() { 90 BeforeEach(func() { 91 app.LifecycleBuildpacks = []string{"default"} 92 }) 93 94 It("sets the lifecycle buildpack to be empty in the JSON", func() { 95 Expect(string(appBytes)).To(MatchJSON(`{"lifecycle":{"data":{"buildpacks":null},"type":"buildpack"}}`)) 96 }) 97 }) 98 99 When("null buildpack is provided", func() { 100 BeforeEach(func() { 101 app.LifecycleBuildpacks = []string{"null"} 102 }) 103 104 It("sets the Lifecycle buildpack to be empty in the JSON", func() { 105 Expect(string(appBytes)).To(MatchJSON(`{"lifecycle":{"data":{"buildpacks":null},"type":"buildpack"}}`)) 106 }) 107 }) 108 109 When("other buildpacks are provided", func() { 110 BeforeEach(func() { 111 app.LifecycleBuildpacks = []string{"some-buildpack"} 112 }) 113 114 It("sets them in the JSON", func() { 115 Expect(string(appBytes)).To(MatchJSON(`{"lifecycle":{"data":{"buildpacks":["some-buildpack"]},"type":"buildpack"}}`)) 116 }) 117 }) 118 }) 119 120 When("metadata is provided", func() { 121 BeforeEach(func() { 122 app = resources.Application{ 123 Metadata: &resources.Metadata{ 124 Labels: map[string]types.NullString{ 125 "some-key": types.NewNullString("some-value"), 126 "other-key": types.NewNullString("other-value\nwith a newline & a \" quote")}, 127 }, 128 } 129 }) 130 131 It("should include the labels in the JSON", func() { 132 Expect(string(appBytes)).To(MatchJSON(`{ 133 "metadata": { 134 "labels": { 135 "some-key":"some-value", 136 "other-key":"other-value\nwith a newline & a \" quote" 137 } 138 } 139 }`)) 140 }) 141 142 When("labels need to be removed", func() { 143 BeforeEach(func() { 144 app = resources.Application{ 145 Metadata: &resources.Metadata{ 146 Labels: map[string]types.NullString{ 147 "some-key": types.NewNullString("some-value"), 148 "other-key": types.NewNullString("other-value\nwith a newline & a \" quote"), 149 "key-to-delete": types.NewNullString(), 150 }, 151 }, 152 } 153 }) 154 155 It("should send nulls for those labels", func() { 156 Expect(string(appBytes)).To(MatchJSON(`{ 157 "metadata": { 158 "labels": { 159 "some-key":"some-value", 160 "other-key":"other-value\nwith a newline & a \" quote", 161 "key-to-delete":null 162 } 163 } 164 }`)) 165 }) 166 }) 167 }) 168 }) 169 170 Describe("UnmarshalJSON", func() { 171 var ( 172 app resources.Application 173 appBytes []byte 174 err error 175 ) 176 177 BeforeEach(func() { 178 appBytes = []byte("{}") 179 app = resources.Application{} 180 }) 181 182 JustBeforeEach(func() { 183 err = json.Unmarshal(appBytes, &app) 184 Expect(err).ToNot(HaveOccurred()) 185 }) 186 187 When("no lifecycle is provided", func() { 188 BeforeEach(func() { 189 appBytes = []byte("{}") 190 }) 191 192 It("omits the lifecycle from the JSON", func() { 193 Expect(app).To(Equal(resources.Application{})) 194 }) 195 }) 196 197 When("lifecycle type docker is provided", func() { 198 BeforeEach(func() { 199 appBytes = []byte(`{"lifecycle":{"type":"docker","data":{}}}`) 200 }) 201 It("sets the lifecycle type to docker with empty data", func() { 202 Expect(app).To(Equal(resources.Application{ 203 LifecycleType: constant.AppLifecycleTypeDocker, 204 })) 205 }) 206 }) 207 208 When("lifecycle type buildpack is provided", func() { 209 210 When("other buildpacks are provided", func() { 211 BeforeEach(func() { 212 appBytes = []byte(`{"lifecycle":{"data":{"buildpacks":["some-buildpack"]},"type":"buildpack"}}`) 213 }) 214 215 It("sets them in the JSON", func() { 216 Expect(app).To(Equal(resources.Application{ 217 LifecycleType: constant.AppLifecycleTypeBuildpack, 218 LifecycleBuildpacks: []string{"some-buildpack"}, 219 })) 220 }) 221 }) 222 }) 223 224 When("labels are provided", func() { 225 BeforeEach(func() { 226 appBytes = []byte(`{"metadata":{"labels":{"some-key":"some-value"}}}`) 227 }) 228 229 It("sets the labels", func() { 230 Expect(app).To(Equal(resources.Application{ 231 Metadata: &resources.Metadata{ 232 Labels: map[string]types.NullString{ 233 "some-key": types.NewNullString("some-value"), 234 }, 235 }, 236 })) 237 }) 238 }) 239 }) 240 }) 241 242 Describe("CreateApplication", func() { 243 var ( 244 appToCreate resources.Application 245 246 createdApp resources.Application 247 warnings Warnings 248 executeErr error 249 ) 250 251 JustBeforeEach(func() { 252 createdApp, warnings, executeErr = client.CreateApplication(appToCreate) 253 }) 254 255 When("the application successfully is created", func() { 256 BeforeEach(func() { 257 requester.MakeRequestCalls(func(requestParams RequestParams) (JobURL, Warnings, error) { 258 requestParams.ResponseBody.(*resources.Application).GUID = "some-app-guid" 259 requestParams.ResponseBody.(*resources.Application).Name = requestParams.RequestBody.(resources.Application).Name 260 return "", Warnings{"this is a warning"}, nil 261 }) 262 appToCreate = resources.Application{ 263 Name: "some-app-name", 264 SpaceGUID: "some-space-guid", 265 } 266 }) 267 268 It("makes the correct request", func() { 269 Expect(requester.MakeRequestCallCount()).To(Equal(1)) 270 actualParams := requester.MakeRequestArgsForCall(0) 271 Expect(actualParams.RequestName).To(Equal(internal.PostApplicationRequest)) 272 Expect(actualParams.RequestBody).To(Equal(appToCreate)) 273 _, ok := actualParams.ResponseBody.(*resources.Application) 274 Expect(ok).To(BeTrue()) 275 }) 276 277 It("returns the created app and warnings", func() { 278 Expect(executeErr).NotTo(HaveOccurred()) 279 Expect(warnings).To(ConsistOf("this is a warning")) 280 281 Expect(createdApp).To(Equal(resources.Application{ 282 Name: "some-app-name", 283 GUID: "some-app-guid", 284 })) 285 }) 286 }) 287 288 When("cc returns back an error or warnings", func() { 289 BeforeEach(func() { 290 errors := []ccerror.V3Error{ 291 { 292 Code: 10008, 293 Detail: "The request is semantically invalid: command presence", 294 Title: "CF-UnprocessableEntity", 295 }, 296 { 297 Code: 10010, 298 Detail: "App not found", 299 Title: "CF-ResourceNotFound", 300 }, 301 } 302 303 requester.MakeRequestReturns( 304 "", 305 Warnings{"this is a warning"}, 306 ccerror.MultiError{ResponseCode: http.StatusTeapot, Errors: errors}, 307 ) 308 }) 309 310 It("returns the error and all warnings", func() { 311 Expect(executeErr).To(MatchError(ccerror.MultiError{ 312 ResponseCode: http.StatusTeapot, 313 Errors: []ccerror.V3Error{ 314 { 315 Code: 10008, 316 Detail: "The request is semantically invalid: command presence", 317 Title: "CF-UnprocessableEntity", 318 }, 319 { 320 Code: 10010, 321 Detail: "App not found", 322 Title: "CF-ResourceNotFound", 323 }, 324 }, 325 })) 326 Expect(warnings).To(ConsistOf("this is a warning")) 327 }) 328 }) 329 }) 330 331 Describe("GetApplicationByNameAndSpace", func() { 332 var ( 333 appName = "some-app-name" 334 spaceGUID = "some-space-guid" 335 336 app resources.Application 337 warnings Warnings 338 executeErr error 339 ) 340 341 JustBeforeEach(func() { 342 app, warnings, executeErr = client.GetApplicationByNameAndSpace(appName, spaceGUID) 343 }) 344 345 When("the application exists", func() { 346 BeforeEach(func() { 347 requester.MakeListRequestCalls(func(requestParams RequestParams) (IncludedResources, Warnings, error) { 348 err := requestParams.AppendToList(resources.Application{GUID: "app-guid-2"}) 349 Expect(err).NotTo(HaveOccurred()) 350 return IncludedResources{}, Warnings{"this is a warning"}, nil 351 }) 352 }) 353 354 It("makes the correct request", func() { 355 Expect(requester.MakeListRequestCallCount()).To(Equal(1)) 356 actualParams := requester.MakeListRequestArgsForCall(0) 357 Expect(actualParams.RequestName).To(Equal(internal.GetApplicationsRequest)) 358 Expect(actualParams.Query).To(Equal([]Query{ 359 {Key: NameFilter, Values: []string{"some-app-name"}}, 360 {Key: SpaceGUIDFilter, Values: []string{"some-space-guid"}}, 361 })) 362 _, ok := actualParams.ResponseBody.(resources.Application) 363 Expect(ok).To(BeTrue()) 364 }) 365 366 It("returns the queried application and all warnings", func() { 367 Expect(executeErr).NotTo(HaveOccurred()) 368 Expect(warnings).To(ConsistOf("this is a warning")) 369 Expect(app).To(Equal(resources.Application{GUID: "app-guid-2"})) 370 }) 371 }) 372 373 When("the application does not exist", func() { 374 BeforeEach(func() { 375 requester.MakeListRequestReturns(IncludedResources{}, Warnings{"this is a warning"}, nil) 376 }) 377 378 It("returns an error and warnings", func() { 379 Expect(executeErr).To(MatchError(ccerror.ApplicationNotFoundError{Name: appName})) 380 Expect(warnings).To(ConsistOf("this is a warning")) 381 }) 382 }) 383 384 When("the cloud controller returns errors and warnings", func() { 385 BeforeEach(func() { 386 errors := []ccerror.V3Error{ 387 { 388 Code: 10008, 389 Detail: "The request is semantically invalid: command presence", 390 Title: "CF-UnprocessableEntity", 391 }, 392 { 393 Code: 10010, 394 Detail: "App not found", 395 Title: "CF-ResourceNotFound", 396 }, 397 } 398 399 requester.MakeListRequestReturns( 400 IncludedResources{}, 401 Warnings{"this is a warning"}, 402 ccerror.MultiError{ResponseCode: http.StatusTeapot, Errors: errors}, 403 ) 404 }) 405 406 It("returns the error and all warnings", func() { 407 Expect(executeErr).To(MatchError(ccerror.MultiError{ 408 ResponseCode: http.StatusTeapot, 409 Errors: []ccerror.V3Error{ 410 { 411 Code: 10008, 412 Detail: "The request is semantically invalid: command presence", 413 Title: "CF-UnprocessableEntity", 414 }, 415 { 416 Code: 10010, 417 Detail: "App not found", 418 Title: "CF-ResourceNotFound", 419 }, 420 }, 421 })) 422 Expect(warnings).To(ConsistOf("this is a warning")) 423 }) 424 }) 425 }) 426 427 Describe("GetApplications", func() { 428 var ( 429 filters []Query 430 431 apps []resources.Application 432 warnings Warnings 433 executeErr error 434 ) 435 436 JustBeforeEach(func() { 437 apps, warnings, executeErr = client.GetApplications(filters...) 438 }) 439 440 When("applications exist", func() { 441 BeforeEach(func() { 442 requester.MakeListRequestCalls(func(requestParams RequestParams) (IncludedResources, Warnings, error) { 443 err := requestParams.AppendToList(resources.Application{GUID: "app-guid-1"}) 444 Expect(err).NotTo(HaveOccurred()) 445 return IncludedResources{}, Warnings{"this is a warning", "this is another warning"}, nil 446 }) 447 448 filters = []Query{ 449 {Key: SpaceGUIDFilter, Values: []string{"some-space-guid"}}, 450 {Key: NameFilter, Values: []string{"some-app-name"}}, 451 } 452 }) 453 454 It("makes the correct request", func() { 455 Expect(requester.MakeListRequestCallCount()).To(Equal(1)) 456 actualParams := requester.MakeListRequestArgsForCall(0) 457 Expect(actualParams.RequestName).To(Equal(internal.GetApplicationsRequest)) 458 Expect(actualParams.Query).To(Equal(filters)) 459 _, ok := actualParams.ResponseBody.(resources.Application) 460 Expect(ok).To(BeTrue()) 461 }) 462 463 It("returns the queried applications and all warnings", func() { 464 Expect(executeErr).NotTo(HaveOccurred()) 465 Expect(warnings).To(ConsistOf("this is a warning", "this is another warning")) 466 467 Expect(apps).To(ConsistOf(resources.Application{GUID: "app-guid-1"})) 468 }) 469 }) 470 471 When("the cloud controller returns errors and warnings", func() { 472 BeforeEach(func() { 473 errors := []ccerror.V3Error{ 474 { 475 Code: 10008, 476 Detail: "The request is semantically invalid: command presence", 477 Title: "CF-UnprocessableEntity", 478 }, 479 { 480 Code: 10010, 481 Detail: "App not found", 482 Title: "CF-ResourceNotFound", 483 }, 484 } 485 486 requester.MakeListRequestReturns( 487 IncludedResources{}, 488 Warnings{"this is a warning"}, 489 ccerror.MultiError{ResponseCode: http.StatusTeapot, Errors: errors}, 490 ) 491 }) 492 493 It("returns the error and all warnings", func() { 494 Expect(executeErr).To(MatchError(ccerror.MultiError{ 495 ResponseCode: http.StatusTeapot, 496 Errors: []ccerror.V3Error{ 497 { 498 Code: 10008, 499 Detail: "The request is semantically invalid: command presence", 500 Title: "CF-UnprocessableEntity", 501 }, 502 { 503 Code: 10010, 504 Detail: "App not found", 505 Title: "CF-ResourceNotFound", 506 }, 507 }, 508 })) 509 Expect(warnings).To(ConsistOf("this is a warning")) 510 }) 511 }) 512 }) 513 514 Describe("UpdateApplication", func() { 515 var ( 516 appToUpdate resources.Application 517 518 updatedApp resources.Application 519 warnings Warnings 520 executeErr error 521 ) 522 523 JustBeforeEach(func() { 524 updatedApp, warnings, executeErr = client.UpdateApplication(appToUpdate) 525 }) 526 527 When("the application successfully is updated", func() { 528 BeforeEach(func() { 529 requester.MakeRequestCalls(func(requestParams RequestParams) (JobURL, Warnings, error) { 530 requestParams.ResponseBody.(*resources.Application).GUID = "some-app-guid" 531 requestParams.ResponseBody.(*resources.Application).Name = requestParams.RequestBody.(resources.Application).Name 532 requestParams.ResponseBody.(*resources.Application).StackName = requestParams.RequestBody.(resources.Application).StackName 533 requestParams.ResponseBody.(*resources.Application).LifecycleType = requestParams.RequestBody.(resources.Application).LifecycleType 534 requestParams.ResponseBody.(*resources.Application).LifecycleBuildpacks = requestParams.RequestBody.(resources.Application).LifecycleBuildpacks 535 requestParams.ResponseBody.(*resources.Application).SpaceGUID = requestParams.RequestBody.(resources.Application).SpaceGUID 536 return "", Warnings{"this is a warning"}, nil 537 }) 538 539 appToUpdate = resources.Application{ 540 GUID: "some-app-guid", 541 Name: "some-app-name", 542 StackName: "some-stack-name", 543 LifecycleType: constant.AppLifecycleTypeBuildpack, 544 LifecycleBuildpacks: []string{"some-buildpack"}, 545 SpaceGUID: "some-space-guid", 546 } 547 }) 548 549 It("makes the correct request", func() { 550 Expect(requester.MakeRequestCallCount()).To(Equal(1)) 551 actualParams := requester.MakeRequestArgsForCall(0) 552 Expect(actualParams.RequestName).To(Equal(internal.PatchApplicationRequest)) 553 Expect(actualParams.URIParams).To(Equal(internal.Params{"app_guid": "some-app-guid"})) 554 Expect(actualParams.RequestBody).To(Equal(appToUpdate)) 555 _, ok := actualParams.ResponseBody.(*resources.Application) 556 Expect(ok).To(BeTrue()) 557 }) 558 559 It("returns the updated app and warnings", func() { 560 Expect(executeErr).NotTo(HaveOccurred()) 561 Expect(warnings).To(ConsistOf("this is a warning")) 562 563 Expect(updatedApp).To(Equal(resources.Application{ 564 GUID: "some-app-guid", 565 StackName: "some-stack-name", 566 LifecycleBuildpacks: []string{"some-buildpack"}, 567 LifecycleType: constant.AppLifecycleTypeBuildpack, 568 Name: "some-app-name", 569 SpaceGUID: "some-space-guid", 570 })) 571 }) 572 }) 573 574 When("cc returns back an error or warnings", func() { 575 BeforeEach(func() { 576 errors := []ccerror.V3Error{ 577 { 578 Code: 10008, 579 Detail: "The request is semantically invalid: command presence", 580 Title: "CF-UnprocessableEntity", 581 }, 582 { 583 Code: 10010, 584 Detail: "App not found", 585 Title: "CF-ResourceNotFound", 586 }, 587 } 588 589 requester.MakeRequestReturns( 590 "", 591 Warnings{"this is a warning"}, 592 ccerror.MultiError{ResponseCode: http.StatusTeapot, Errors: errors}, 593 ) 594 }) 595 596 It("returns the error and all warnings", func() { 597 Expect(executeErr).To(MatchError(ccerror.MultiError{ 598 ResponseCode: http.StatusTeapot, 599 Errors: []ccerror.V3Error{ 600 { 601 Code: 10008, 602 Detail: "The request is semantically invalid: command presence", 603 Title: "CF-UnprocessableEntity", 604 }, 605 { 606 Code: 10010, 607 Detail: "App not found", 608 Title: "CF-ResourceNotFound", 609 }, 610 }, 611 })) 612 Expect(warnings).To(ConsistOf("this is a warning")) 613 }) 614 }) 615 }) 616 617 Describe("UpdateApplicationStop", func() { 618 var ( 619 responseApp resources.Application 620 warnings Warnings 621 executeErr error 622 ) 623 624 JustBeforeEach(func() { 625 responseApp, warnings, executeErr = client.UpdateApplicationStop("some-app-guid") 626 }) 627 628 When("the response succeeds", func() { 629 BeforeEach(func() { 630 requester.MakeRequestCalls(func(requestParams RequestParams) (JobURL, Warnings, error) { 631 requestParams.ResponseBody.(*resources.Application).GUID = "some-app-guid" 632 requestParams.ResponseBody.(*resources.Application).Name = "some-app" 633 requestParams.ResponseBody.(*resources.Application).State = constant.ApplicationStopped 634 return "", Warnings{"this is a warning"}, nil 635 }) 636 }) 637 638 It("makes the correct request", func() { 639 Expect(requester.MakeRequestCallCount()).To(Equal(1)) 640 actualParams := requester.MakeRequestArgsForCall(0) 641 Expect(actualParams.RequestName).To(Equal(internal.PostApplicationActionStopRequest)) 642 Expect(actualParams.URIParams).To(Equal(internal.Params{"app_guid": "some-app-guid"})) 643 _, ok := actualParams.ResponseBody.(*resources.Application) 644 Expect(ok).To(BeTrue()) 645 }) 646 647 It("returns the application, warnings, and no error", func() { 648 Expect(responseApp).To(Equal(resources.Application{ 649 GUID: "some-app-guid", 650 Name: "some-app", 651 State: constant.ApplicationStopped, 652 })) 653 Expect(executeErr).ToNot(HaveOccurred()) 654 Expect(warnings).To(ConsistOf("this is a warning")) 655 }) 656 }) 657 658 When("the CC returns an error", func() { 659 BeforeEach(func() { 660 errors := []ccerror.V3Error{ 661 { 662 Code: 10008, 663 Detail: "The request is semantically invalid: command presence", 664 Title: "CF-UnprocessableEntity", 665 }, 666 { 667 Code: 10010, 668 Detail: "App not found", 669 Title: "CF-ResourceNotFound", 670 }, 671 } 672 673 requester.MakeRequestReturns( 674 "", 675 Warnings{"this is a warning"}, 676 ccerror.MultiError{ResponseCode: http.StatusTeapot, Errors: errors}, 677 ) 678 }) 679 680 It("returns no app, the error and all warnings", func() { 681 Expect(executeErr).To(MatchError(ccerror.MultiError{ 682 ResponseCode: http.StatusTeapot, 683 Errors: []ccerror.V3Error{ 684 { 685 Code: 10008, 686 Detail: "The request is semantically invalid: command presence", 687 Title: "CF-UnprocessableEntity", 688 }, 689 { 690 Code: 10010, 691 Detail: "App not found", 692 Title: "CF-ResourceNotFound", 693 }, 694 }, 695 })) 696 Expect(warnings).To(ConsistOf("this is a warning")) 697 }) 698 }) 699 }) 700 701 Describe("UpdateApplicationStart", func() { 702 var ( 703 responseApp resources.Application 704 warnings Warnings 705 executeErr error 706 ) 707 708 JustBeforeEach(func() { 709 responseApp, warnings, executeErr = client.UpdateApplicationStart("some-app-guid") 710 }) 711 712 When("the response succeeds", func() { 713 BeforeEach(func() { 714 requester.MakeRequestCalls(func(requestParams RequestParams) (JobURL, Warnings, error) { 715 requestParams.ResponseBody.(*resources.Application).GUID = "some-app-guid" 716 requestParams.ResponseBody.(*resources.Application).Name = "some-app" 717 return "", Warnings{"this is a warning"}, nil 718 }) 719 }) 720 721 It("makes the correct request", func() { 722 Expect(requester.MakeRequestCallCount()).To(Equal(1)) 723 actualParams := requester.MakeRequestArgsForCall(0) 724 Expect(actualParams.RequestName).To(Equal(internal.PostApplicationActionStartRequest)) 725 Expect(actualParams.URIParams).To(Equal(internal.Params{"app_guid": "some-app-guid"})) 726 _, ok := actualParams.ResponseBody.(*resources.Application) 727 Expect(ok).To(BeTrue()) 728 }) 729 730 It("returns warnings and no error", func() { 731 Expect(executeErr).ToNot(HaveOccurred()) 732 Expect(warnings).To(ConsistOf("this is a warning")) 733 Expect(responseApp).To(Equal(resources.Application{ 734 GUID: "some-app-guid", 735 Name: "some-app", 736 })) 737 }) 738 }) 739 740 When("cc returns back an error or warnings", func() { 741 BeforeEach(func() { 742 errors := []ccerror.V3Error{ 743 { 744 Code: 10008, 745 Detail: "The request is semantically invalid: command presence", 746 Title: "CF-UnprocessableEntity", 747 }, 748 { 749 Code: 10010, 750 Detail: "App not found", 751 Title: "CF-ResourceNotFound", 752 }, 753 } 754 755 requester.MakeRequestReturns( 756 "", 757 Warnings{"this is a warning"}, 758 ccerror.MultiError{ResponseCode: http.StatusTeapot, Errors: errors}, 759 ) 760 }) 761 762 It("returns the error and all warnings", func() { 763 Expect(executeErr).To(MatchError(ccerror.MultiError{ 764 ResponseCode: http.StatusTeapot, 765 Errors: []ccerror.V3Error{ 766 { 767 Code: 10008, 768 Detail: "The request is semantically invalid: command presence", 769 Title: "CF-UnprocessableEntity", 770 }, 771 { 772 Code: 10010, 773 Detail: "App not found", 774 Title: "CF-ResourceNotFound", 775 }, 776 }, 777 })) 778 Expect(warnings).To(ConsistOf("this is a warning")) 779 }) 780 }) 781 }) 782 783 Describe("UpdateApplicationRestart", func() { 784 var ( 785 responseApp resources.Application 786 warnings Warnings 787 executeErr error 788 ) 789 790 JustBeforeEach(func() { 791 responseApp, warnings, executeErr = client.UpdateApplicationRestart("some-app-guid") 792 }) 793 794 When("the response succeeds", func() { 795 BeforeEach(func() { 796 requester.MakeRequestCalls(func(requestParams RequestParams) (JobURL, Warnings, error) { 797 requestParams.ResponseBody.(*resources.Application).GUID = "some-app-guid" 798 requestParams.ResponseBody.(*resources.Application).Name = "some-app" 799 requestParams.ResponseBody.(*resources.Application).State = constant.ApplicationStarted 800 return "", Warnings{"this is a warning"}, nil 801 }) 802 }) 803 804 It("makes the correct request", func() { 805 Expect(requester.MakeRequestCallCount()).To(Equal(1)) 806 actualParams := requester.MakeRequestArgsForCall(0) 807 Expect(actualParams.RequestName).To(Equal(internal.PostApplicationActionRestartRequest)) 808 Expect(actualParams.URIParams).To(Equal(internal.Params{"app_guid": "some-app-guid"})) 809 _, ok := actualParams.ResponseBody.(*resources.Application) 810 Expect(ok).To(BeTrue()) 811 }) 812 813 It("returns the application, warnings, and no error", func() { 814 Expect(responseApp).To(Equal(resources.Application{ 815 GUID: "some-app-guid", 816 Name: "some-app", 817 State: constant.ApplicationStarted, 818 })) 819 Expect(executeErr).ToNot(HaveOccurred()) 820 Expect(warnings).To(ConsistOf("this is a warning")) 821 }) 822 }) 823 824 When("the CC returns an error", func() { 825 BeforeEach(func() { 826 errors := []ccerror.V3Error{ 827 { 828 Code: 10008, 829 Detail: "The request is semantically invalid: command presence", 830 Title: "CF-UnprocessableEntity", 831 }, 832 { 833 Code: 10010, 834 Detail: "App not found", 835 Title: "CF-ResourceNotFound", 836 }, 837 } 838 839 requester.MakeRequestReturns( 840 "", 841 Warnings{"this is a warning"}, 842 ccerror.MultiError{ResponseCode: http.StatusTeapot, Errors: errors}, 843 ) 844 }) 845 846 It("returns no app, the error and all warnings", func() { 847 Expect(executeErr).To(MatchError(ccerror.MultiError{ 848 ResponseCode: http.StatusTeapot, 849 Errors: []ccerror.V3Error{ 850 { 851 Code: 10008, 852 Detail: "The request is semantically invalid: command presence", 853 Title: "CF-UnprocessableEntity", 854 }, 855 { 856 Code: 10010, 857 Detail: "App not found", 858 Title: "CF-ResourceNotFound", 859 }, 860 }, 861 })) 862 Expect(warnings).To(ConsistOf("this is a warning")) 863 }) 864 }) 865 }) 866 })