github.com/wanddynosios/cli/v8@v8.7.9-0.20240221182337-1a92e3a7017f/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 {Key: PerPage, Values: []string{"1"}}, 362 {Key: Page, Values: []string{"1"}}, 363 })) 364 _, ok := actualParams.ResponseBody.(resources.Application) 365 Expect(ok).To(BeTrue()) 366 }) 367 368 It("returns the queried application and all warnings", func() { 369 Expect(executeErr).NotTo(HaveOccurred()) 370 Expect(warnings).To(ConsistOf("this is a warning")) 371 Expect(app).To(Equal(resources.Application{GUID: "app-guid-2"})) 372 }) 373 }) 374 375 When("the application does not exist", func() { 376 BeforeEach(func() { 377 requester.MakeListRequestReturns(IncludedResources{}, Warnings{"this is a warning"}, nil) 378 }) 379 380 It("returns an error and warnings", func() { 381 Expect(executeErr).To(MatchError(ccerror.ApplicationNotFoundError{Name: appName})) 382 Expect(warnings).To(ConsistOf("this is a warning")) 383 }) 384 }) 385 386 When("the cloud controller returns errors and warnings", func() { 387 BeforeEach(func() { 388 errors := []ccerror.V3Error{ 389 { 390 Code: 10008, 391 Detail: "The request is semantically invalid: command presence", 392 Title: "CF-UnprocessableEntity", 393 }, 394 { 395 Code: 10010, 396 Detail: "App not found", 397 Title: "CF-ResourceNotFound", 398 }, 399 } 400 401 requester.MakeListRequestReturns( 402 IncludedResources{}, 403 Warnings{"this is a warning"}, 404 ccerror.MultiError{ResponseCode: http.StatusTeapot, Errors: errors}, 405 ) 406 }) 407 408 It("returns the error and all warnings", func() { 409 Expect(executeErr).To(MatchError(ccerror.MultiError{ 410 ResponseCode: http.StatusTeapot, 411 Errors: []ccerror.V3Error{ 412 { 413 Code: 10008, 414 Detail: "The request is semantically invalid: command presence", 415 Title: "CF-UnprocessableEntity", 416 }, 417 { 418 Code: 10010, 419 Detail: "App not found", 420 Title: "CF-ResourceNotFound", 421 }, 422 }, 423 })) 424 Expect(warnings).To(ConsistOf("this is a warning")) 425 }) 426 }) 427 }) 428 429 Describe("GetApplications", func() { 430 var ( 431 filters []Query 432 433 apps []resources.Application 434 warnings Warnings 435 executeErr error 436 ) 437 438 JustBeforeEach(func() { 439 apps, warnings, executeErr = client.GetApplications(filters...) 440 }) 441 442 When("applications exist", func() { 443 BeforeEach(func() { 444 requester.MakeListRequestCalls(func(requestParams RequestParams) (IncludedResources, Warnings, error) { 445 err := requestParams.AppendToList(resources.Application{GUID: "app-guid-1"}) 446 Expect(err).NotTo(HaveOccurred()) 447 return IncludedResources{}, Warnings{"this is a warning", "this is another warning"}, nil 448 }) 449 450 filters = []Query{ 451 {Key: SpaceGUIDFilter, Values: []string{"some-space-guid"}}, 452 {Key: NameFilter, Values: []string{"some-app-name"}}, 453 } 454 }) 455 456 It("makes the correct request", func() { 457 Expect(requester.MakeListRequestCallCount()).To(Equal(1)) 458 actualParams := requester.MakeListRequestArgsForCall(0) 459 Expect(actualParams.RequestName).To(Equal(internal.GetApplicationsRequest)) 460 Expect(actualParams.Query).To(Equal(filters)) 461 _, ok := actualParams.ResponseBody.(resources.Application) 462 Expect(ok).To(BeTrue()) 463 }) 464 465 It("returns the queried applications and all warnings", func() { 466 Expect(executeErr).NotTo(HaveOccurred()) 467 Expect(warnings).To(ConsistOf("this is a warning", "this is another warning")) 468 469 Expect(apps).To(ConsistOf(resources.Application{GUID: "app-guid-1"})) 470 }) 471 }) 472 473 When("the cloud controller returns errors and warnings", func() { 474 BeforeEach(func() { 475 errors := []ccerror.V3Error{ 476 { 477 Code: 10008, 478 Detail: "The request is semantically invalid: command presence", 479 Title: "CF-UnprocessableEntity", 480 }, 481 { 482 Code: 10010, 483 Detail: "App not found", 484 Title: "CF-ResourceNotFound", 485 }, 486 } 487 488 requester.MakeListRequestReturns( 489 IncludedResources{}, 490 Warnings{"this is a warning"}, 491 ccerror.MultiError{ResponseCode: http.StatusTeapot, Errors: errors}, 492 ) 493 }) 494 495 It("returns the error and all warnings", func() { 496 Expect(executeErr).To(MatchError(ccerror.MultiError{ 497 ResponseCode: http.StatusTeapot, 498 Errors: []ccerror.V3Error{ 499 { 500 Code: 10008, 501 Detail: "The request is semantically invalid: command presence", 502 Title: "CF-UnprocessableEntity", 503 }, 504 { 505 Code: 10010, 506 Detail: "App not found", 507 Title: "CF-ResourceNotFound", 508 }, 509 }, 510 })) 511 Expect(warnings).To(ConsistOf("this is a warning")) 512 }) 513 }) 514 }) 515 516 Describe("UpdateApplication", func() { 517 var ( 518 appToUpdate resources.Application 519 520 updatedApp resources.Application 521 warnings Warnings 522 executeErr error 523 ) 524 525 JustBeforeEach(func() { 526 updatedApp, warnings, executeErr = client.UpdateApplication(appToUpdate) 527 }) 528 529 When("the application successfully is updated", func() { 530 BeforeEach(func() { 531 requester.MakeRequestCalls(func(requestParams RequestParams) (JobURL, Warnings, error) { 532 requestParams.ResponseBody.(*resources.Application).GUID = "some-app-guid" 533 requestParams.ResponseBody.(*resources.Application).Name = requestParams.RequestBody.(resources.Application).Name 534 requestParams.ResponseBody.(*resources.Application).StackName = requestParams.RequestBody.(resources.Application).StackName 535 requestParams.ResponseBody.(*resources.Application).LifecycleType = requestParams.RequestBody.(resources.Application).LifecycleType 536 requestParams.ResponseBody.(*resources.Application).LifecycleBuildpacks = requestParams.RequestBody.(resources.Application).LifecycleBuildpacks 537 requestParams.ResponseBody.(*resources.Application).SpaceGUID = requestParams.RequestBody.(resources.Application).SpaceGUID 538 return "", Warnings{"this is a warning"}, nil 539 }) 540 541 appToUpdate = resources.Application{ 542 GUID: "some-app-guid", 543 Name: "some-app-name", 544 StackName: "some-stack-name", 545 LifecycleType: constant.AppLifecycleTypeBuildpack, 546 LifecycleBuildpacks: []string{"some-buildpack"}, 547 SpaceGUID: "some-space-guid", 548 } 549 }) 550 551 It("makes the correct request", func() { 552 Expect(requester.MakeRequestCallCount()).To(Equal(1)) 553 actualParams := requester.MakeRequestArgsForCall(0) 554 Expect(actualParams.RequestName).To(Equal(internal.PatchApplicationRequest)) 555 Expect(actualParams.URIParams).To(Equal(internal.Params{"app_guid": "some-app-guid"})) 556 Expect(actualParams.RequestBody).To(Equal(appToUpdate)) 557 _, ok := actualParams.ResponseBody.(*resources.Application) 558 Expect(ok).To(BeTrue()) 559 }) 560 561 It("returns the updated app and warnings", func() { 562 Expect(executeErr).NotTo(HaveOccurred()) 563 Expect(warnings).To(ConsistOf("this is a warning")) 564 565 Expect(updatedApp).To(Equal(resources.Application{ 566 GUID: "some-app-guid", 567 StackName: "some-stack-name", 568 LifecycleBuildpacks: []string{"some-buildpack"}, 569 LifecycleType: constant.AppLifecycleTypeBuildpack, 570 Name: "some-app-name", 571 SpaceGUID: "some-space-guid", 572 })) 573 }) 574 }) 575 576 When("cc returns back an error or warnings", func() { 577 BeforeEach(func() { 578 errors := []ccerror.V3Error{ 579 { 580 Code: 10008, 581 Detail: "The request is semantically invalid: command presence", 582 Title: "CF-UnprocessableEntity", 583 }, 584 { 585 Code: 10010, 586 Detail: "App not found", 587 Title: "CF-ResourceNotFound", 588 }, 589 } 590 591 requester.MakeRequestReturns( 592 "", 593 Warnings{"this is a warning"}, 594 ccerror.MultiError{ResponseCode: http.StatusTeapot, Errors: errors}, 595 ) 596 }) 597 598 It("returns the error and all warnings", func() { 599 Expect(executeErr).To(MatchError(ccerror.MultiError{ 600 ResponseCode: http.StatusTeapot, 601 Errors: []ccerror.V3Error{ 602 { 603 Code: 10008, 604 Detail: "The request is semantically invalid: command presence", 605 Title: "CF-UnprocessableEntity", 606 }, 607 { 608 Code: 10010, 609 Detail: "App not found", 610 Title: "CF-ResourceNotFound", 611 }, 612 }, 613 })) 614 Expect(warnings).To(ConsistOf("this is a warning")) 615 }) 616 }) 617 }) 618 619 Describe("UpdateApplicationName", func() { 620 var ( 621 newAppName string 622 appGUID string 623 624 updatedApp resources.Application 625 warnings Warnings 626 executeErr error 627 ) 628 629 JustBeforeEach(func() { 630 newAppName = "some-new-app-name" 631 appGUID = "some-app-guid" 632 633 updatedApp, warnings, executeErr = client.UpdateApplicationName(newAppName, appGUID) 634 }) 635 636 When("the application successfully is updated", func() { 637 BeforeEach(func() { 638 requester.MakeRequestCalls(func(requestParams RequestParams) (JobURL, Warnings, error) { 639 requestParams.ResponseBody.(*resources.Application).GUID = appGUID 640 requestParams.ResponseBody.(*resources.Application).Name = requestParams.RequestBody.(resources.ApplicationNameOnly).Name 641 requestParams.ResponseBody.(*resources.Application).StackName = "some-stack-name" 642 requestParams.ResponseBody.(*resources.Application).LifecycleType = constant.AppLifecycleTypeBuildpack 643 requestParams.ResponseBody.(*resources.Application).LifecycleBuildpacks = []string{"some-buildpack"} 644 requestParams.ResponseBody.(*resources.Application).SpaceGUID = "some-space-guid" 645 return "", Warnings{"this is a warning"}, nil 646 }) 647 }) 648 649 It("makes the correct request", func() { 650 Expect(requester.MakeRequestCallCount()).To(Equal(1)) 651 actualParams := requester.MakeRequestArgsForCall(0) 652 Expect(actualParams.RequestName).To(Equal(internal.PatchApplicationRequest)) 653 Expect(actualParams.URIParams).To(Equal(internal.Params{"app_guid": "some-app-guid"})) 654 Expect(actualParams.RequestBody).To(Equal(resources.ApplicationNameOnly{Name: newAppName})) 655 _, ok := actualParams.ResponseBody.(*resources.Application) 656 Expect(ok).To(BeTrue()) 657 }) 658 659 It("returns the updated app and warnings", func() { 660 Expect(executeErr).NotTo(HaveOccurred()) 661 Expect(warnings).To(ConsistOf("this is a warning")) 662 663 Expect(updatedApp).To(Equal(resources.Application{ 664 GUID: "some-app-guid", 665 StackName: "some-stack-name", 666 LifecycleBuildpacks: []string{"some-buildpack"}, 667 LifecycleType: constant.AppLifecycleTypeBuildpack, 668 Name: "some-new-app-name", 669 SpaceGUID: "some-space-guid", 670 })) 671 }) 672 }) 673 674 When("cc returns back an error or warnings", func() { 675 BeforeEach(func() { 676 errors := []ccerror.V3Error{ 677 { 678 Code: 10008, 679 Detail: "The request is semantically invalid: command presence", 680 Title: "CF-UnprocessableEntity", 681 }, 682 { 683 Code: 10010, 684 Detail: "App not found", 685 Title: "CF-ResourceNotFound", 686 }, 687 } 688 689 requester.MakeRequestReturns( 690 "", 691 Warnings{"this is a warning"}, 692 ccerror.MultiError{ResponseCode: http.StatusTeapot, Errors: errors}, 693 ) 694 }) 695 696 It("returns the error and all warnings", func() { 697 Expect(executeErr).To(MatchError(ccerror.MultiError{ 698 ResponseCode: http.StatusTeapot, 699 Errors: []ccerror.V3Error{ 700 { 701 Code: 10008, 702 Detail: "The request is semantically invalid: command presence", 703 Title: "CF-UnprocessableEntity", 704 }, 705 { 706 Code: 10010, 707 Detail: "App not found", 708 Title: "CF-ResourceNotFound", 709 }, 710 }, 711 })) 712 Expect(warnings).To(ConsistOf("this is a warning")) 713 }) 714 }) 715 }) 716 717 Describe("UpdateApplicationStop", func() { 718 var ( 719 responseApp resources.Application 720 warnings Warnings 721 executeErr error 722 ) 723 724 JustBeforeEach(func() { 725 responseApp, warnings, executeErr = client.UpdateApplicationStop("some-app-guid") 726 }) 727 728 When("the response succeeds", func() { 729 BeforeEach(func() { 730 requester.MakeRequestCalls(func(requestParams RequestParams) (JobURL, Warnings, error) { 731 requestParams.ResponseBody.(*resources.Application).GUID = "some-app-guid" 732 requestParams.ResponseBody.(*resources.Application).Name = "some-app" 733 requestParams.ResponseBody.(*resources.Application).State = constant.ApplicationStopped 734 return "", Warnings{"this is a warning"}, nil 735 }) 736 }) 737 738 It("makes the correct request", func() { 739 Expect(requester.MakeRequestCallCount()).To(Equal(1)) 740 actualParams := requester.MakeRequestArgsForCall(0) 741 Expect(actualParams.RequestName).To(Equal(internal.PostApplicationActionStopRequest)) 742 Expect(actualParams.URIParams).To(Equal(internal.Params{"app_guid": "some-app-guid"})) 743 _, ok := actualParams.ResponseBody.(*resources.Application) 744 Expect(ok).To(BeTrue()) 745 }) 746 747 It("returns the application, warnings, and no error", func() { 748 Expect(responseApp).To(Equal(resources.Application{ 749 GUID: "some-app-guid", 750 Name: "some-app", 751 State: constant.ApplicationStopped, 752 })) 753 Expect(executeErr).ToNot(HaveOccurred()) 754 Expect(warnings).To(ConsistOf("this is a warning")) 755 }) 756 }) 757 758 When("the CC returns an error", func() { 759 BeforeEach(func() { 760 errors := []ccerror.V3Error{ 761 { 762 Code: 10008, 763 Detail: "The request is semantically invalid: command presence", 764 Title: "CF-UnprocessableEntity", 765 }, 766 { 767 Code: 10010, 768 Detail: "App not found", 769 Title: "CF-ResourceNotFound", 770 }, 771 } 772 773 requester.MakeRequestReturns( 774 "", 775 Warnings{"this is a warning"}, 776 ccerror.MultiError{ResponseCode: http.StatusTeapot, Errors: errors}, 777 ) 778 }) 779 780 It("returns no app, the error and all warnings", func() { 781 Expect(executeErr).To(MatchError(ccerror.MultiError{ 782 ResponseCode: http.StatusTeapot, 783 Errors: []ccerror.V3Error{ 784 { 785 Code: 10008, 786 Detail: "The request is semantically invalid: command presence", 787 Title: "CF-UnprocessableEntity", 788 }, 789 { 790 Code: 10010, 791 Detail: "App not found", 792 Title: "CF-ResourceNotFound", 793 }, 794 }, 795 })) 796 Expect(warnings).To(ConsistOf("this is a warning")) 797 }) 798 }) 799 }) 800 801 Describe("UpdateApplicationStart", func() { 802 var ( 803 responseApp resources.Application 804 warnings Warnings 805 executeErr error 806 ) 807 808 JustBeforeEach(func() { 809 responseApp, warnings, executeErr = client.UpdateApplicationStart("some-app-guid") 810 }) 811 812 When("the response succeeds", func() { 813 BeforeEach(func() { 814 requester.MakeRequestCalls(func(requestParams RequestParams) (JobURL, Warnings, error) { 815 requestParams.ResponseBody.(*resources.Application).GUID = "some-app-guid" 816 requestParams.ResponseBody.(*resources.Application).Name = "some-app" 817 return "", Warnings{"this is a warning"}, nil 818 }) 819 }) 820 821 It("makes the correct request", func() { 822 Expect(requester.MakeRequestCallCount()).To(Equal(1)) 823 actualParams := requester.MakeRequestArgsForCall(0) 824 Expect(actualParams.RequestName).To(Equal(internal.PostApplicationActionStartRequest)) 825 Expect(actualParams.URIParams).To(Equal(internal.Params{"app_guid": "some-app-guid"})) 826 _, ok := actualParams.ResponseBody.(*resources.Application) 827 Expect(ok).To(BeTrue()) 828 }) 829 830 It("returns warnings and no error", func() { 831 Expect(executeErr).ToNot(HaveOccurred()) 832 Expect(warnings).To(ConsistOf("this is a warning")) 833 Expect(responseApp).To(Equal(resources.Application{ 834 GUID: "some-app-guid", 835 Name: "some-app", 836 })) 837 }) 838 }) 839 840 When("cc returns back an error or warnings", func() { 841 BeforeEach(func() { 842 errors := []ccerror.V3Error{ 843 { 844 Code: 10008, 845 Detail: "The request is semantically invalid: command presence", 846 Title: "CF-UnprocessableEntity", 847 }, 848 { 849 Code: 10010, 850 Detail: "App not found", 851 Title: "CF-ResourceNotFound", 852 }, 853 } 854 855 requester.MakeRequestReturns( 856 "", 857 Warnings{"this is a warning"}, 858 ccerror.MultiError{ResponseCode: http.StatusTeapot, Errors: errors}, 859 ) 860 }) 861 862 It("returns the error and all warnings", func() { 863 Expect(executeErr).To(MatchError(ccerror.MultiError{ 864 ResponseCode: http.StatusTeapot, 865 Errors: []ccerror.V3Error{ 866 { 867 Code: 10008, 868 Detail: "The request is semantically invalid: command presence", 869 Title: "CF-UnprocessableEntity", 870 }, 871 { 872 Code: 10010, 873 Detail: "App not found", 874 Title: "CF-ResourceNotFound", 875 }, 876 }, 877 })) 878 Expect(warnings).To(ConsistOf("this is a warning")) 879 }) 880 }) 881 }) 882 883 Describe("UpdateApplicationRestart", func() { 884 var ( 885 responseApp resources.Application 886 warnings Warnings 887 executeErr error 888 ) 889 890 JustBeforeEach(func() { 891 responseApp, warnings, executeErr = client.UpdateApplicationRestart("some-app-guid") 892 }) 893 894 When("the response succeeds", func() { 895 BeforeEach(func() { 896 requester.MakeRequestCalls(func(requestParams RequestParams) (JobURL, Warnings, error) { 897 requestParams.ResponseBody.(*resources.Application).GUID = "some-app-guid" 898 requestParams.ResponseBody.(*resources.Application).Name = "some-app" 899 requestParams.ResponseBody.(*resources.Application).State = constant.ApplicationStarted 900 return "", Warnings{"this is a warning"}, nil 901 }) 902 }) 903 904 It("makes the correct request", func() { 905 Expect(requester.MakeRequestCallCount()).To(Equal(1)) 906 actualParams := requester.MakeRequestArgsForCall(0) 907 Expect(actualParams.RequestName).To(Equal(internal.PostApplicationActionRestartRequest)) 908 Expect(actualParams.URIParams).To(Equal(internal.Params{"app_guid": "some-app-guid"})) 909 _, ok := actualParams.ResponseBody.(*resources.Application) 910 Expect(ok).To(BeTrue()) 911 }) 912 913 It("returns the application, warnings, and no error", func() { 914 Expect(responseApp).To(Equal(resources.Application{ 915 GUID: "some-app-guid", 916 Name: "some-app", 917 State: constant.ApplicationStarted, 918 })) 919 Expect(executeErr).ToNot(HaveOccurred()) 920 Expect(warnings).To(ConsistOf("this is a warning")) 921 }) 922 }) 923 924 When("the CC returns an error", func() { 925 BeforeEach(func() { 926 errors := []ccerror.V3Error{ 927 { 928 Code: 10008, 929 Detail: "The request is semantically invalid: command presence", 930 Title: "CF-UnprocessableEntity", 931 }, 932 { 933 Code: 10010, 934 Detail: "App not found", 935 Title: "CF-ResourceNotFound", 936 }, 937 } 938 939 requester.MakeRequestReturns( 940 "", 941 Warnings{"this is a warning"}, 942 ccerror.MultiError{ResponseCode: http.StatusTeapot, Errors: errors}, 943 ) 944 }) 945 946 It("returns no app, the error and all warnings", func() { 947 Expect(executeErr).To(MatchError(ccerror.MultiError{ 948 ResponseCode: http.StatusTeapot, 949 Errors: []ccerror.V3Error{ 950 { 951 Code: 10008, 952 Detail: "The request is semantically invalid: command presence", 953 Title: "CF-UnprocessableEntity", 954 }, 955 { 956 Code: 10010, 957 Detail: "App not found", 958 Title: "CF-ResourceNotFound", 959 }, 960 }, 961 })) 962 Expect(warnings).To(ConsistOf("this is a warning")) 963 }) 964 }) 965 }) 966 })