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