github.com/cloudfoundry-attic/cli-with-i18n@v6.32.1-0.20171002233121-7401370d3b85+incompatible/api/cloudcontroller/ccv3/application_test.go (about) 1 package ccv3_test 2 3 import ( 4 "fmt" 5 "net/http" 6 "net/url" 7 8 "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" 9 . "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" 10 . "github.com/onsi/ginkgo" 11 . "github.com/onsi/gomega" 12 . "github.com/onsi/gomega/ghttp" 13 ) 14 15 var _ = Describe("Application", func() { 16 var client *Client 17 18 BeforeEach(func() { 19 client = NewTestClient() 20 }) 21 22 Describe("MarshalJSON", func() { 23 var ( 24 app Application 25 appBytes []byte 26 err error 27 ) 28 29 JustBeforeEach(func() { 30 appBytes, err = app.MarshalJSON() 31 Expect(err).ToNot(HaveOccurred()) 32 }) 33 34 Context("when no lifecycle is provided", func() { 35 BeforeEach(func() { 36 app = Application{} 37 }) 38 39 It("omits the lifecycle from the JSON", func() { 40 Expect(string(appBytes)).To(Equal("{}")) 41 }) 42 }) 43 44 Context("when lifecycle type docker is provided", func() { 45 BeforeEach(func() { 46 app = Application{ 47 Lifecycle: AppLifecycle{ 48 Type: DockerAppLifecycleType, 49 }, 50 } 51 }) 52 53 It("sets lifecycle type to docker with empty data", func() { 54 Expect(string(appBytes)).To(MatchJSON(`{"lifecycle":{"type":"docker","data":{}}}`)) 55 }) 56 }) 57 58 Context("when lifecycle type buildpack is provided", func() { 59 Context("when no buildpacks are provided", func() { 60 BeforeEach(func() { 61 app = Application{ 62 Lifecycle: AppLifecycle{ 63 Type: BuildpackAppLifecycleType, 64 }, 65 } 66 }) 67 68 It("omits the lifecycle from the JSON", func() { 69 Expect(string(appBytes)).To(Equal("{}")) 70 }) 71 }) 72 73 Context("when default buildpack is provided", func() { 74 BeforeEach(func() { 75 app = Application{ 76 Lifecycle: AppLifecycle{ 77 Type: BuildpackAppLifecycleType, 78 Data: AppLifecycleData{ 79 Buildpacks: []string{"default"}, 80 }, 81 }, 82 } 83 }) 84 85 It("sets the lifecycle buildpack to be empty in the JSON", func() { 86 Expect(string(appBytes)).To(Equal(`{"lifecycle":{"data":{"buildpacks":null},"type":"buildpack"}}`)) 87 }) 88 }) 89 90 Context("when null buildpack is provided", func() { 91 BeforeEach(func() { 92 app = Application{ 93 Lifecycle: AppLifecycle{ 94 Type: BuildpackAppLifecycleType, 95 Data: AppLifecycleData{ 96 Buildpacks: []string{"null"}, 97 }, 98 }, 99 } 100 }) 101 102 It("sets the Lifecycle buildpack to be empty in the JSON", func() { 103 Expect(string(appBytes)).To(Equal(`{"lifecycle":{"data":{"buildpacks":null},"type":"buildpack"}}`)) 104 }) 105 }) 106 107 Context("when other buildpacks are provided", func() { 108 BeforeEach(func() { 109 app = Application{ 110 Lifecycle: AppLifecycle{ 111 Type: BuildpackAppLifecycleType, 112 Data: AppLifecycleData{ 113 Buildpacks: []string{"some-buildpack"}, 114 }, 115 }, 116 } 117 }) 118 119 It("sets them in the JSON", func() { 120 Expect(string(appBytes)).To(Equal(`{"lifecycle":{"data":{"buildpacks":["some-buildpack"]},"type":"buildpack"}}`)) 121 }) 122 }) 123 }) 124 }) 125 126 Describe("GetApplications", func() { 127 Context("when applications exist", func() { 128 BeforeEach(func() { 129 response1 := fmt.Sprintf(`{ 130 "pagination": { 131 "next": { 132 "href": "%s/v3/apps?space_guids=some-space-guid&names=some-app-name&page=2&per_page=2" 133 } 134 }, 135 "resources": [ 136 { 137 "name": "app-name-1", 138 "guid": "app-guid-1", 139 "lifecycle": { 140 "type": "buildpack", 141 "data": { 142 "buildpacks": ["some-buildpack"], 143 "stack": "some-stack" 144 } 145 } 146 }, 147 { 148 "name": "app-name-2", 149 "guid": "app-guid-2" 150 } 151 ] 152 }`, server.URL()) 153 response2 := `{ 154 "pagination": { 155 "next": null 156 }, 157 "resources": [ 158 { 159 "name": "app-name-3", 160 "guid": "app-guid-3" 161 } 162 ] 163 }` 164 server.AppendHandlers( 165 CombineHandlers( 166 VerifyRequest(http.MethodGet, "/v3/apps", "space_guids=some-space-guid&names=some-app-name"), 167 RespondWith(http.StatusOK, response1, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 168 ), 169 ) 170 server.AppendHandlers( 171 CombineHandlers( 172 VerifyRequest(http.MethodGet, "/v3/apps", "space_guids=some-space-guid&names=some-app-name&page=2&per_page=2"), 173 RespondWith(http.StatusOK, response2, http.Header{"X-Cf-Warnings": {"this is another warning"}}), 174 ), 175 ) 176 }) 177 178 It("returns the queried applications and all warnings", func() { 179 apps, warnings, err := client.GetApplications(url.Values{ 180 SpaceGUIDFilter: []string{"some-space-guid"}, 181 NameFilter: []string{"some-app-name"}, 182 }) 183 Expect(err).NotTo(HaveOccurred()) 184 185 Expect(apps).To(ConsistOf( 186 Application{ 187 Name: "app-name-1", 188 GUID: "app-guid-1", 189 Lifecycle: AppLifecycle{ 190 Type: BuildpackAppLifecycleType, 191 Data: AppLifecycleData{ 192 Buildpacks: []string{"some-buildpack"}, 193 }, 194 }, 195 }, 196 Application{Name: "app-name-2", GUID: "app-guid-2"}, 197 Application{Name: "app-name-3", GUID: "app-guid-3"}, 198 )) 199 Expect(warnings).To(ConsistOf("this is a warning", "this is another warning")) 200 }) 201 }) 202 203 Context("when the cloud controller returns errors and warnings", func() { 204 BeforeEach(func() { 205 response := `{ 206 "errors": [ 207 { 208 "code": 10008, 209 "detail": "The request is semantically invalid: command presence", 210 "title": "CF-UnprocessableEntity" 211 }, 212 { 213 "code": 10010, 214 "detail": "App not found", 215 "title": "CF-ResourceNotFound" 216 } 217 ] 218 }` 219 server.AppendHandlers( 220 CombineHandlers( 221 VerifyRequest(http.MethodGet, "/v3/apps"), 222 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 223 ), 224 ) 225 }) 226 227 It("returns the error and all warnings", func() { 228 _, warnings, err := client.GetApplications(nil) 229 Expect(err).To(MatchError(ccerror.V3UnexpectedResponseError{ 230 ResponseCode: http.StatusTeapot, 231 V3ErrorResponse: ccerror.V3ErrorResponse{ 232 Errors: []ccerror.V3Error{ 233 { 234 Code: 10008, 235 Detail: "The request is semantically invalid: command presence", 236 Title: "CF-UnprocessableEntity", 237 }, 238 { 239 Code: 10010, 240 Detail: "App not found", 241 Title: "CF-ResourceNotFound", 242 }, 243 }, 244 }, 245 })) 246 Expect(warnings).To(ConsistOf("this is a warning")) 247 }) 248 }) 249 }) 250 251 Describe("UpdateApplication", func() { 252 Context("when the application successfully is updated", func() { 253 BeforeEach(func() { 254 response := `{ 255 "guid": "some-app-guid", 256 "name": "some-app-name" 257 }` 258 259 expectedBody := map[string]interface{}{ 260 "name": "some-app-name", 261 "lifecycle": map[string]interface{}{ 262 "type": "buildpack", 263 "data": map[string]interface{}{ 264 "buildpacks": []string{"some-buildpack"}, 265 }, 266 }, 267 "relationships": map[string]interface{}{ 268 "space": map[string]interface{}{ 269 "data": map[string]string{ 270 "guid": "some-space-guid", 271 }, 272 }, 273 }, 274 } 275 server.AppendHandlers( 276 CombineHandlers( 277 VerifyRequest(http.MethodPatch, "/v3/apps/some-app-guid"), 278 VerifyJSONRepresenting(expectedBody), 279 RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 280 ), 281 ) 282 }) 283 284 It("returns the updated app and warnings", func() { 285 app, warnings, err := client.UpdateApplication(Application{ 286 GUID: "some-app-guid", 287 Name: "some-app-name", 288 Lifecycle: AppLifecycle{ 289 Type: BuildpackAppLifecycleType, 290 Data: AppLifecycleData{ 291 Buildpacks: []string{"some-buildpack"}, 292 }, 293 }, 294 Relationships: Relationships{ 295 SpaceRelationship: Relationship{GUID: "some-space-guid"}, 296 }, 297 }) 298 299 Expect(err).NotTo(HaveOccurred()) 300 Expect(warnings).To(ConsistOf("this is a warning")) 301 302 Expect(app).To(Equal(Application{ 303 Name: "some-app-name", 304 GUID: "some-app-guid", 305 })) 306 }) 307 }) 308 309 Context("when cc returns back an error or warnings", func() { 310 BeforeEach(func() { 311 response := `{ 312 "errors": [ 313 { 314 "code": 10008, 315 "detail": "The request is semantically invalid: command presence", 316 "title": "CF-UnprocessableEntity" 317 }, 318 { 319 "code": 10010, 320 "detail": "App not found", 321 "title": "CF-ResourceNotFound" 322 } 323 ] 324 }` 325 server.AppendHandlers( 326 CombineHandlers( 327 VerifyRequest(http.MethodPatch, "/v3/apps/some-app-guid"), 328 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 329 ), 330 ) 331 }) 332 333 It("returns the error and all warnings", func() { 334 _, warnings, err := client.UpdateApplication(Application{GUID: "some-app-guid"}) 335 Expect(err).To(MatchError(ccerror.V3UnexpectedResponseError{ 336 ResponseCode: http.StatusTeapot, 337 V3ErrorResponse: ccerror.V3ErrorResponse{ 338 Errors: []ccerror.V3Error{ 339 { 340 Code: 10008, 341 Detail: "The request is semantically invalid: command presence", 342 Title: "CF-UnprocessableEntity", 343 }, 344 { 345 Code: 10010, 346 Detail: "App not found", 347 Title: "CF-ResourceNotFound", 348 }, 349 }, 350 }, 351 })) 352 Expect(warnings).To(ConsistOf("this is a warning")) 353 }) 354 }) 355 }) 356 357 Describe("CreateApplication", func() { 358 Context("when the application successfully is created", func() { 359 BeforeEach(func() { 360 response := `{ 361 "guid": "some-app-guid", 362 "name": "some-app-name" 363 }` 364 365 expectedBody := map[string]interface{}{ 366 "name": "some-app-name", 367 "relationships": map[string]interface{}{ 368 "space": map[string]interface{}{ 369 "data": map[string]string{ 370 "guid": "some-space-guid", 371 }, 372 }, 373 }, 374 } 375 server.AppendHandlers( 376 CombineHandlers( 377 VerifyRequest(http.MethodPost, "/v3/apps"), 378 VerifyJSONRepresenting(expectedBody), 379 RespondWith(http.StatusCreated, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 380 ), 381 ) 382 }) 383 384 It("returns the created app and warnings", func() { 385 app, warnings, err := client.CreateApplication(Application{ 386 Name: "some-app-name", 387 Relationships: Relationships{ 388 SpaceRelationship: Relationship{GUID: "some-space-guid"}, 389 }, 390 }) 391 392 Expect(err).NotTo(HaveOccurred()) 393 Expect(warnings).To(ConsistOf("this is a warning")) 394 395 Expect(app).To(Equal(Application{ 396 Name: "some-app-name", 397 GUID: "some-app-guid", 398 })) 399 }) 400 }) 401 402 Context("when the caller specifies a buildpack", func() { 403 BeforeEach(func() { 404 response := `{ 405 "guid": "some-app-guid", 406 "name": "some-app-name", 407 "lifecycle": { 408 "type": "buildpack", 409 "data": { 410 "buildpacks": ["some-buildpack"] 411 } 412 } 413 }` 414 415 expectedBody := map[string]interface{}{ 416 "name": "some-app-name", 417 "lifecycle": map[string]interface{}{ 418 "type": "buildpack", 419 "data": map[string]interface{}{ 420 "buildpacks": []string{"some-buildpack"}, 421 }, 422 }, 423 "relationships": map[string]interface{}{ 424 "space": map[string]interface{}{ 425 "data": map[string]string{ 426 "guid": "some-space-guid", 427 }, 428 }, 429 }, 430 } 431 server.AppendHandlers( 432 CombineHandlers( 433 VerifyRequest(http.MethodPost, "/v3/apps"), 434 VerifyJSONRepresenting(expectedBody), 435 RespondWith(http.StatusCreated, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 436 ), 437 ) 438 }) 439 440 It("returns the created app and warnings", func() { 441 app, warnings, err := client.CreateApplication(Application{ 442 Name: "some-app-name", 443 Lifecycle: AppLifecycle{ 444 Type: BuildpackAppLifecycleType, 445 Data: AppLifecycleData{ 446 Buildpacks: []string{"some-buildpack"}, 447 }, 448 }, 449 Relationships: Relationships{ 450 SpaceRelationship: Relationship{GUID: "some-space-guid"}, 451 }, 452 }) 453 454 Expect(err).NotTo(HaveOccurred()) 455 Expect(warnings).To(ConsistOf("this is a warning")) 456 457 Expect(app).To(Equal(Application{ 458 Name: "some-app-name", 459 GUID: "some-app-guid", 460 Lifecycle: AppLifecycle{ 461 Type: BuildpackAppLifecycleType, 462 Data: AppLifecycleData{ 463 Buildpacks: []string{"some-buildpack"}, 464 }, 465 }, 466 })) 467 }) 468 }) 469 470 Context("when cc returns back an error or warnings", func() { 471 BeforeEach(func() { 472 response := `{ 473 "errors": [ 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 server.AppendHandlers( 487 CombineHandlers( 488 VerifyRequest(http.MethodPost, "/v3/apps"), 489 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 490 ), 491 ) 492 }) 493 494 It("returns the error and all warnings", func() { 495 _, warnings, err := client.CreateApplication(Application{}) 496 Expect(err).To(MatchError(ccerror.V3UnexpectedResponseError{ 497 ResponseCode: http.StatusTeapot, 498 V3ErrorResponse: ccerror.V3ErrorResponse{ 499 Errors: []ccerror.V3Error{ 500 { 501 Code: 10008, 502 Detail: "The request is semantically invalid: command presence", 503 Title: "CF-UnprocessableEntity", 504 }, 505 { 506 Code: 10010, 507 Detail: "App not found", 508 Title: "CF-ResourceNotFound", 509 }, 510 }, 511 }, 512 })) 513 Expect(warnings).To(ConsistOf("this is a warning")) 514 }) 515 }) 516 }) 517 518 Describe("DeleteApplication", func() { 519 Context("when the application is deleted successfully", func() { 520 BeforeEach(func() { 521 server.AppendHandlers( 522 CombineHandlers( 523 VerifyRequest(http.MethodDelete, "/v3/apps/some-app-guid"), 524 RespondWith(http.StatusAccepted, ``, 525 http.Header{ 526 "X-Cf-Warnings": {"some-warning"}, 527 "Location": {"/v3/jobs/some-location"}, 528 }, 529 ), 530 ), 531 ) 532 }) 533 534 It("returns all warnings", func() { 535 jobLocation, warnings, err := client.DeleteApplication("some-app-guid") 536 Expect(err).ToNot(HaveOccurred()) 537 Expect(jobLocation).To(Equal("/v3/jobs/some-location")) 538 Expect(warnings).To(ConsistOf("some-warning")) 539 }) 540 }) 541 542 Context("when deleting the application returns an error", func() { 543 BeforeEach(func() { 544 server.AppendHandlers( 545 CombineHandlers( 546 VerifyRequest(http.MethodDelete, "/v3/apps/some-app-guid"), 547 RespondWith(http.StatusBadRequest, ``, 548 http.Header{ 549 "X-Cf-Warnings": {"some-warning"}, 550 }, 551 ), 552 ), 553 ) 554 }) 555 556 It("returns all warnings", func() { 557 _, warnings, err := client.DeleteApplication("some-app-guid") 558 Expect(err).To(MatchError(ccerror.RawHTTPStatusError{StatusCode: 400, RawResponse: []byte{}})) 559 Expect(warnings).To(ConsistOf("some-warning")) 560 }) 561 }) 562 }) 563 564 Describe("SetApplicationDroplet", func() { 565 Context("it sets the droplet", func() { 566 BeforeEach(func() { 567 response := ` 568 { 569 "data": { 570 "guid": "some-droplet-guid" 571 }, 572 "links": { 573 "self": { 574 "href": "https://api.example.org/v3/apps/some-app-guid/relationships/current_droplet" 575 }, 576 "related": { 577 "href": "https://api.example.org/v3/apps/some-app-guid/droplets/current" 578 } 579 } 580 }` 581 requestBody := map[string]interface{}{ 582 "data": map[string]string{ 583 "guid": "some-droplet-guid", 584 }, 585 } 586 587 server.AppendHandlers( 588 CombineHandlers( 589 VerifyRequest(http.MethodPatch, "/v3/apps/some-app-guid/relationships/current_droplet"), 590 VerifyJSONRepresenting(requestBody), 591 RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 592 ), 593 ) 594 }) 595 596 It("returns warnings and no error", func() { 597 relationship, warnings, err := client.SetApplicationDroplet("some-app-guid", "some-droplet-guid") 598 Expect(err).ToNot(HaveOccurred()) 599 Expect(warnings).To(ConsistOf("this is a warning")) 600 Expect(relationship.GUID).To(Equal("some-droplet-guid")) 601 }) 602 }) 603 }) 604 Context("when setting the app to the new droplet returns errors and warnings", func() { 605 BeforeEach(func() { 606 response := `{ 607 "errors": [ 608 { 609 "code": 10008, 610 "detail": "The request is semantically invalid: command presence", 611 "title": "CF-UnprocessableEntity" 612 }, 613 { 614 "code": 10010, 615 "detail": "App not found", 616 "title": "CF-ResourceNotFound" 617 } 618 ] 619 }` 620 requestBody := map[string]interface{}{ 621 "data": map[string]string{ 622 "guid": "some-droplet-guid", 623 }, 624 } 625 626 server.AppendHandlers( 627 CombineHandlers( 628 VerifyRequest(http.MethodPatch, "/v3/apps/no-such-app-guid/relationships/current_droplet"), 629 VerifyJSONRepresenting(requestBody), 630 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 631 ), 632 ) 633 634 }) 635 636 It("returns the error and all warnings", func() { 637 _, warnings, err := client.SetApplicationDroplet("no-such-app-guid", "some-droplet-guid") 638 Expect(err).To(MatchError(ccerror.V3UnexpectedResponseError{ 639 ResponseCode: http.StatusTeapot, 640 V3ErrorResponse: ccerror.V3ErrorResponse{ 641 Errors: []ccerror.V3Error{ 642 { 643 Code: 10008, 644 Detail: "The request is semantically invalid: command presence", 645 Title: "CF-UnprocessableEntity", 646 }, 647 { 648 Code: 10010, 649 Detail: "App not found", 650 Title: "CF-ResourceNotFound", 651 }, 652 }, 653 }, 654 })) 655 Expect(warnings).To(ConsistOf("this is a warning")) 656 }) 657 }) 658 659 Describe("StopApplication", func() { 660 Context("when the response succeeds", func() { 661 BeforeEach(func() { 662 response := ` 663 { 664 "guid": "some-app-guid", 665 "name": "some-app", 666 "state": "STOPPED" 667 }` 668 server.AppendHandlers( 669 CombineHandlers( 670 VerifyRequest(http.MethodPost, "/v3/apps/some-app-guid/actions/stop"), 671 RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 672 ), 673 ) 674 }) 675 676 It("returns warnings and no error", func() { 677 warnings, err := client.StopApplication("some-app-guid") 678 Expect(err).ToNot(HaveOccurred()) 679 Expect(warnings).To(ConsistOf("this is a warning")) 680 }) 681 }) 682 }) 683 684 Context("when stopping the app returns errors and warnings", func() { 685 BeforeEach(func() { 686 response := `{ 687 "errors": [ 688 { 689 "code": 10008, 690 "detail": "The request is semantically invalid: command presence", 691 "title": "CF-UnprocessableEntity" 692 }, 693 { 694 "code": 10010, 695 "detail": "App not found", 696 "title": "CF-ResourceNotFound" 697 } 698 ] 699 }` 700 server.AppendHandlers( 701 CombineHandlers( 702 VerifyRequest(http.MethodPost, "/v3/apps/no-such-app-guid/actions/stop"), 703 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 704 ), 705 ) 706 707 }) 708 709 It("returns the error and all warnings", func() { 710 warnings, err := client.StopApplication("no-such-app-guid") 711 Expect(err).To(MatchError(ccerror.V3UnexpectedResponseError{ 712 ResponseCode: http.StatusTeapot, 713 V3ErrorResponse: ccerror.V3ErrorResponse{ 714 Errors: []ccerror.V3Error{ 715 { 716 Code: 10008, 717 Detail: "The request is semantically invalid: command presence", 718 Title: "CF-UnprocessableEntity", 719 }, 720 { 721 Code: 10010, 722 Detail: "App not found", 723 Title: "CF-ResourceNotFound", 724 }, 725 }, 726 }, 727 })) 728 Expect(warnings).To(ConsistOf("this is a warning")) 729 }) 730 }) 731 732 Describe("StartApplication", func() { 733 Context("when the response succeeds", func() { 734 BeforeEach(func() { 735 response := ` 736 { 737 "guid": "some-app-guid", 738 "name": "some-app" 739 }` 740 server.AppendHandlers( 741 CombineHandlers( 742 VerifyRequest(http.MethodPost, "/v3/apps/some-app-guid/actions/start"), 743 RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 744 ), 745 ) 746 }) 747 748 It("returns warnings and no error", func() { 749 app, warnings, err := client.StartApplication("some-app-guid") 750 Expect(err).ToNot(HaveOccurred()) 751 Expect(warnings).To(ConsistOf("this is a warning")) 752 Expect(app.GUID).To(Equal("some-app-guid")) 753 }) 754 }) 755 }) 756 Context("when starting the app returns errors and warnings", func() { 757 BeforeEach(func() { 758 response := `{ 759 "errors": [ 760 { 761 "code": 10008, 762 "detail": "The request is semantically invalid: command presence", 763 "title": "CF-UnprocessableEntity" 764 }, 765 { 766 "code": 10010, 767 "detail": "App not found", 768 "title": "CF-ResourceNotFound" 769 } 770 ] 771 }` 772 server.AppendHandlers( 773 CombineHandlers( 774 VerifyRequest(http.MethodPost, "/v3/apps/no-such-app-guid/actions/start"), 775 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 776 ), 777 ) 778 779 }) 780 781 It("returns the error and all warnings", func() { 782 _, warnings, err := client.StartApplication("no-such-app-guid") 783 Expect(err).To(MatchError(ccerror.V3UnexpectedResponseError{ 784 ResponseCode: http.StatusTeapot, 785 V3ErrorResponse: ccerror.V3ErrorResponse{ 786 Errors: []ccerror.V3Error{ 787 { 788 Code: 10008, 789 Detail: "The request is semantically invalid: command presence", 790 Title: "CF-UnprocessableEntity", 791 }, 792 { 793 Code: 10010, 794 Detail: "App not found", 795 Title: "CF-ResourceNotFound", 796 }, 797 }, 798 }, 799 })) 800 Expect(warnings).To(ConsistOf("this is a warning")) 801 }) 802 }) 803 })