github.com/DaAlbrecht/cf-cli@v0.0.0-20231128151943-1fe19bb400b9/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("UpdateApplicationName", func() { 618 var ( 619 newAppName string 620 appGUID string 621 622 updatedApp resources.Application 623 warnings Warnings 624 executeErr error 625 ) 626 627 JustBeforeEach(func() { 628 newAppName = "some-new-app-name" 629 appGUID = "some-app-guid" 630 631 updatedApp, warnings, executeErr = client.UpdateApplicationName(newAppName, appGUID) 632 }) 633 634 When("the application successfully is updated", func() { 635 BeforeEach(func() { 636 requester.MakeRequestCalls(func(requestParams RequestParams) (JobURL, Warnings, error) { 637 requestParams.ResponseBody.(*resources.Application).GUID = appGUID 638 requestParams.ResponseBody.(*resources.Application).Name = requestParams.RequestBody.(resources.ApplicationNameOnly).Name 639 requestParams.ResponseBody.(*resources.Application).StackName = "some-stack-name" 640 requestParams.ResponseBody.(*resources.Application).LifecycleType = constant.AppLifecycleTypeBuildpack 641 requestParams.ResponseBody.(*resources.Application).LifecycleBuildpacks = []string{"some-buildpack"} 642 requestParams.ResponseBody.(*resources.Application).SpaceGUID = "some-space-guid" 643 return "", Warnings{"this is a warning"}, nil 644 }) 645 }) 646 647 It("makes the correct request", func() { 648 Expect(requester.MakeRequestCallCount()).To(Equal(1)) 649 actualParams := requester.MakeRequestArgsForCall(0) 650 Expect(actualParams.RequestName).To(Equal(internal.PatchApplicationRequest)) 651 Expect(actualParams.URIParams).To(Equal(internal.Params{"app_guid": "some-app-guid"})) 652 Expect(actualParams.RequestBody).To(Equal(resources.ApplicationNameOnly{Name: newAppName})) 653 _, ok := actualParams.ResponseBody.(*resources.Application) 654 Expect(ok).To(BeTrue()) 655 }) 656 657 It("returns the updated app and warnings", func() { 658 Expect(executeErr).NotTo(HaveOccurred()) 659 Expect(warnings).To(ConsistOf("this is a warning")) 660 661 Expect(updatedApp).To(Equal(resources.Application{ 662 GUID: "some-app-guid", 663 StackName: "some-stack-name", 664 LifecycleBuildpacks: []string{"some-buildpack"}, 665 LifecycleType: constant.AppLifecycleTypeBuildpack, 666 Name: "some-new-app-name", 667 SpaceGUID: "some-space-guid", 668 })) 669 }) 670 }) 671 672 When("cc returns back an error or warnings", func() { 673 BeforeEach(func() { 674 errors := []ccerror.V3Error{ 675 { 676 Code: 10008, 677 Detail: "The request is semantically invalid: command presence", 678 Title: "CF-UnprocessableEntity", 679 }, 680 { 681 Code: 10010, 682 Detail: "App not found", 683 Title: "CF-ResourceNotFound", 684 }, 685 } 686 687 requester.MakeRequestReturns( 688 "", 689 Warnings{"this is a warning"}, 690 ccerror.MultiError{ResponseCode: http.StatusTeapot, Errors: errors}, 691 ) 692 }) 693 694 It("returns the error and all warnings", func() { 695 Expect(executeErr).To(MatchError(ccerror.MultiError{ 696 ResponseCode: http.StatusTeapot, 697 Errors: []ccerror.V3Error{ 698 { 699 Code: 10008, 700 Detail: "The request is semantically invalid: command presence", 701 Title: "CF-UnprocessableEntity", 702 }, 703 { 704 Code: 10010, 705 Detail: "App not found", 706 Title: "CF-ResourceNotFound", 707 }, 708 }, 709 })) 710 Expect(warnings).To(ConsistOf("this is a warning")) 711 }) 712 }) 713 }) 714 715 Describe("UpdateApplicationStop", func() { 716 var ( 717 responseApp resources.Application 718 warnings Warnings 719 executeErr error 720 ) 721 722 JustBeforeEach(func() { 723 responseApp, warnings, executeErr = client.UpdateApplicationStop("some-app-guid") 724 }) 725 726 When("the response succeeds", func() { 727 BeforeEach(func() { 728 requester.MakeRequestCalls(func(requestParams RequestParams) (JobURL, Warnings, error) { 729 requestParams.ResponseBody.(*resources.Application).GUID = "some-app-guid" 730 requestParams.ResponseBody.(*resources.Application).Name = "some-app" 731 requestParams.ResponseBody.(*resources.Application).State = constant.ApplicationStopped 732 return "", Warnings{"this is a warning"}, nil 733 }) 734 }) 735 736 It("makes the correct request", func() { 737 Expect(requester.MakeRequestCallCount()).To(Equal(1)) 738 actualParams := requester.MakeRequestArgsForCall(0) 739 Expect(actualParams.RequestName).To(Equal(internal.PostApplicationActionStopRequest)) 740 Expect(actualParams.URIParams).To(Equal(internal.Params{"app_guid": "some-app-guid"})) 741 _, ok := actualParams.ResponseBody.(*resources.Application) 742 Expect(ok).To(BeTrue()) 743 }) 744 745 It("returns the application, warnings, and no error", func() { 746 Expect(responseApp).To(Equal(resources.Application{ 747 GUID: "some-app-guid", 748 Name: "some-app", 749 State: constant.ApplicationStopped, 750 })) 751 Expect(executeErr).ToNot(HaveOccurred()) 752 Expect(warnings).To(ConsistOf("this is a warning")) 753 }) 754 }) 755 756 When("the CC returns an error", func() { 757 BeforeEach(func() { 758 errors := []ccerror.V3Error{ 759 { 760 Code: 10008, 761 Detail: "The request is semantically invalid: command presence", 762 Title: "CF-UnprocessableEntity", 763 }, 764 { 765 Code: 10010, 766 Detail: "App not found", 767 Title: "CF-ResourceNotFound", 768 }, 769 } 770 771 requester.MakeRequestReturns( 772 "", 773 Warnings{"this is a warning"}, 774 ccerror.MultiError{ResponseCode: http.StatusTeapot, Errors: errors}, 775 ) 776 }) 777 778 It("returns no app, the error and all warnings", func() { 779 Expect(executeErr).To(MatchError(ccerror.MultiError{ 780 ResponseCode: http.StatusTeapot, 781 Errors: []ccerror.V3Error{ 782 { 783 Code: 10008, 784 Detail: "The request is semantically invalid: command presence", 785 Title: "CF-UnprocessableEntity", 786 }, 787 { 788 Code: 10010, 789 Detail: "App not found", 790 Title: "CF-ResourceNotFound", 791 }, 792 }, 793 })) 794 Expect(warnings).To(ConsistOf("this is a warning")) 795 }) 796 }) 797 }) 798 799 Describe("UpdateApplicationStart", func() { 800 var ( 801 responseApp resources.Application 802 warnings Warnings 803 executeErr error 804 ) 805 806 JustBeforeEach(func() { 807 responseApp, warnings, executeErr = client.UpdateApplicationStart("some-app-guid") 808 }) 809 810 When("the response succeeds", func() { 811 BeforeEach(func() { 812 requester.MakeRequestCalls(func(requestParams RequestParams) (JobURL, Warnings, error) { 813 requestParams.ResponseBody.(*resources.Application).GUID = "some-app-guid" 814 requestParams.ResponseBody.(*resources.Application).Name = "some-app" 815 return "", Warnings{"this is a warning"}, nil 816 }) 817 }) 818 819 It("makes the correct request", func() { 820 Expect(requester.MakeRequestCallCount()).To(Equal(1)) 821 actualParams := requester.MakeRequestArgsForCall(0) 822 Expect(actualParams.RequestName).To(Equal(internal.PostApplicationActionStartRequest)) 823 Expect(actualParams.URIParams).To(Equal(internal.Params{"app_guid": "some-app-guid"})) 824 _, ok := actualParams.ResponseBody.(*resources.Application) 825 Expect(ok).To(BeTrue()) 826 }) 827 828 It("returns warnings and no error", func() { 829 Expect(executeErr).ToNot(HaveOccurred()) 830 Expect(warnings).To(ConsistOf("this is a warning")) 831 Expect(responseApp).To(Equal(resources.Application{ 832 GUID: "some-app-guid", 833 Name: "some-app", 834 })) 835 }) 836 }) 837 838 When("cc returns back an error or warnings", func() { 839 BeforeEach(func() { 840 errors := []ccerror.V3Error{ 841 { 842 Code: 10008, 843 Detail: "The request is semantically invalid: command presence", 844 Title: "CF-UnprocessableEntity", 845 }, 846 { 847 Code: 10010, 848 Detail: "App not found", 849 Title: "CF-ResourceNotFound", 850 }, 851 } 852 853 requester.MakeRequestReturns( 854 "", 855 Warnings{"this is a warning"}, 856 ccerror.MultiError{ResponseCode: http.StatusTeapot, Errors: errors}, 857 ) 858 }) 859 860 It("returns the error and all warnings", func() { 861 Expect(executeErr).To(MatchError(ccerror.MultiError{ 862 ResponseCode: http.StatusTeapot, 863 Errors: []ccerror.V3Error{ 864 { 865 Code: 10008, 866 Detail: "The request is semantically invalid: command presence", 867 Title: "CF-UnprocessableEntity", 868 }, 869 { 870 Code: 10010, 871 Detail: "App not found", 872 Title: "CF-ResourceNotFound", 873 }, 874 }, 875 })) 876 Expect(warnings).To(ConsistOf("this is a warning")) 877 }) 878 }) 879 }) 880 881 Describe("UpdateApplicationRestart", func() { 882 var ( 883 responseApp resources.Application 884 warnings Warnings 885 executeErr error 886 ) 887 888 JustBeforeEach(func() { 889 responseApp, warnings, executeErr = client.UpdateApplicationRestart("some-app-guid") 890 }) 891 892 When("the response succeeds", func() { 893 BeforeEach(func() { 894 requester.MakeRequestCalls(func(requestParams RequestParams) (JobURL, Warnings, error) { 895 requestParams.ResponseBody.(*resources.Application).GUID = "some-app-guid" 896 requestParams.ResponseBody.(*resources.Application).Name = "some-app" 897 requestParams.ResponseBody.(*resources.Application).State = constant.ApplicationStarted 898 return "", Warnings{"this is a warning"}, nil 899 }) 900 }) 901 902 It("makes the correct request", func() { 903 Expect(requester.MakeRequestCallCount()).To(Equal(1)) 904 actualParams := requester.MakeRequestArgsForCall(0) 905 Expect(actualParams.RequestName).To(Equal(internal.PostApplicationActionRestartRequest)) 906 Expect(actualParams.URIParams).To(Equal(internal.Params{"app_guid": "some-app-guid"})) 907 _, ok := actualParams.ResponseBody.(*resources.Application) 908 Expect(ok).To(BeTrue()) 909 }) 910 911 It("returns the application, warnings, and no error", func() { 912 Expect(responseApp).To(Equal(resources.Application{ 913 GUID: "some-app-guid", 914 Name: "some-app", 915 State: constant.ApplicationStarted, 916 })) 917 Expect(executeErr).ToNot(HaveOccurred()) 918 Expect(warnings).To(ConsistOf("this is a warning")) 919 }) 920 }) 921 922 When("the CC returns an error", func() { 923 BeforeEach(func() { 924 errors := []ccerror.V3Error{ 925 { 926 Code: 10008, 927 Detail: "The request is semantically invalid: command presence", 928 Title: "CF-UnprocessableEntity", 929 }, 930 { 931 Code: 10010, 932 Detail: "App not found", 933 Title: "CF-ResourceNotFound", 934 }, 935 } 936 937 requester.MakeRequestReturns( 938 "", 939 Warnings{"this is a warning"}, 940 ccerror.MultiError{ResponseCode: http.StatusTeapot, Errors: errors}, 941 ) 942 }) 943 944 It("returns no app, the error and all warnings", func() { 945 Expect(executeErr).To(MatchError(ccerror.MultiError{ 946 ResponseCode: http.StatusTeapot, 947 Errors: []ccerror.V3Error{ 948 { 949 Code: 10008, 950 Detail: "The request is semantically invalid: command presence", 951 Title: "CF-UnprocessableEntity", 952 }, 953 { 954 Code: 10010, 955 Detail: "App not found", 956 Title: "CF-ResourceNotFound", 957 }, 958 }, 959 })) 960 Expect(warnings).To(ConsistOf("this is a warning")) 961 }) 962 }) 963 }) 964 })