github.com/jenspinney/cli@v6.42.1-0.20190207184520-7450c600020e+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: constant.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: constant.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: constant.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(constant.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(constant.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(constant.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(types.FilteredString{IsSet: true, Value: "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(constant.HTTP), 374 "HealthCheckEndpoint": Equal("/health"), 375 "HealthCheckInvocationTimeout": BeEquivalentTo(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: types.FilteredString{IsSet: true, Value: "[PRIVATE DATA HIDDEN IN LISTS]"}, 533 MemoryInMB: types.NullUint64{Value: 32, IsSet: true}, 534 HealthCheckType: constant.Port, 535 }, 536 Process{ 537 GUID: "process-2-guid", 538 Type: "worker", 539 Command: types.FilteredString{IsSet: true, Value: "[PRIVATE DATA HIDDEN IN LISTS]"}, 540 MemoryInMB: types.NullUint64{Value: 64, IsSet: true}, 541 HealthCheckType: constant.HTTP, 542 HealthCheckEndpoint: "/health", 543 }, 544 Process{ 545 GUID: "process-3-guid", 546 Type: "console", 547 Command: types.FilteredString{IsSet: true, Value: "[PRIVATE DATA HIDDEN IN LISTS]"}, 548 MemoryInMB: types.NullUint64{Value: 128, IsSet: true}, 549 HealthCheckType: constant.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("UpdateProcess", func() { 583 var ( 584 inputProcess Process 585 586 process Process 587 warnings []string 588 err error 589 ) 590 591 BeforeEach(func() { 592 inputProcess = Process{ 593 GUID: "some-process-guid", 594 } 595 }) 596 597 JustBeforeEach(func() { 598 process, warnings, err = client.UpdateProcess(inputProcess) 599 }) 600 601 When("patching the process succeeds", func() { 602 When("the command is set", func() { 603 When("the start command is an arbitrary command", func() { 604 BeforeEach(func() { 605 inputProcess.Command = types.FilteredString{IsSet: true, Value: "some-command"} 606 607 expectedBody := `{ 608 "command": "some-command" 609 }` 610 611 expectedResponse := `{ 612 "command": "some-command" 613 }` 614 615 server.AppendHandlers( 616 CombineHandlers( 617 VerifyRequest(http.MethodPatch, "/v3/processes/some-process-guid"), 618 VerifyJSON(expectedBody), 619 RespondWith(http.StatusOK, expectedResponse, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 620 ), 621 ) 622 }) 623 624 It("patches this process's command with the provided command", func() { 625 Expect(err).ToNot(HaveOccurred()) 626 Expect(warnings).To(ConsistOf("this is a warning")) 627 Expect(process).To(MatchFields(IgnoreExtras, Fields{ 628 "Command": Equal(types.FilteredString{IsSet: true, Value: "some-command"}), 629 })) 630 }) 631 }) 632 633 When("the start command reset", func() { 634 BeforeEach(func() { 635 inputProcess.Command = types.FilteredString{IsSet: true} 636 637 expectedBody := `{ 638 "command": null 639 }` 640 641 expectedResponse := `{ 642 "command": "some-default-command" 643 }` 644 645 server.AppendHandlers( 646 CombineHandlers( 647 VerifyRequest(http.MethodPatch, "/v3/processes/some-process-guid"), 648 VerifyJSON(expectedBody), 649 RespondWith(http.StatusOK, expectedResponse, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 650 ), 651 ) 652 }) 653 654 It("patches this process's command with 'null' and returns the default command", func() { 655 Expect(err).ToNot(HaveOccurred()) 656 Expect(warnings).To(ConsistOf("this is a warning")) 657 Expect(process).To(MatchFields(IgnoreExtras, Fields{ 658 "Command": Equal(types.FilteredString{IsSet: true, Value: "some-default-command"}), 659 })) 660 }) 661 }) 662 }) 663 664 When("the endpoint is set", func() { 665 BeforeEach(func() { 666 inputProcess.HealthCheckEndpoint = "some-endpoint" 667 inputProcess.HealthCheckType = "some-type" 668 669 expectedBody := `{ 670 "health_check": { 671 "type": "some-type", 672 "data": { 673 "endpoint": "some-endpoint" 674 } 675 } 676 }` 677 expectedResponse := `{ 678 "health_check": { 679 "type": "some-type", 680 "data": { 681 "endpoint": "some-endpoint", 682 "invocation_timeout": null 683 } 684 } 685 }` 686 server.AppendHandlers( 687 CombineHandlers( 688 VerifyRequest(http.MethodPatch, "/v3/processes/some-process-guid"), 689 VerifyJSON(expectedBody), 690 RespondWith(http.StatusOK, expectedResponse, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 691 ), 692 ) 693 }) 694 695 It("patches this process's health check", func() { 696 Expect(err).ToNot(HaveOccurred()) 697 Expect(warnings).To(ConsistOf("this is a warning")) 698 Expect(process).To(MatchFields(IgnoreExtras, Fields{ 699 "HealthCheckType": Equal(constant.HealthCheckType("some-type")), 700 "HealthCheckEndpoint": Equal("some-endpoint"), 701 })) 702 }) 703 }) 704 705 When("the invocation timeout is set", func() { 706 BeforeEach(func() { 707 inputProcess.HealthCheckInvocationTimeout = 42 708 inputProcess.HealthCheckType = "some-type" 709 710 expectedBody := `{ 711 "health_check": { 712 "type": "some-type", 713 "data": { 714 "endpoint": null, 715 "invocation_timeout": 42 716 } 717 } 718 }` 719 expectedResponse := `{ 720 "health_check": { 721 "type": "some-type", 722 "data": { 723 "endpoint": null, 724 "invocation_timeout": 42 725 } 726 } 727 }` 728 server.AppendHandlers( 729 CombineHandlers( 730 VerifyRequest(http.MethodPatch, "/v3/processes/some-process-guid"), 731 VerifyJSON(expectedBody), 732 RespondWith(http.StatusOK, expectedResponse, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 733 ), 734 ) 735 }) 736 737 It("patches this process's health check", func() { 738 Expect(err).ToNot(HaveOccurred()) 739 Expect(warnings).To(ConsistOf("this is a warning")) 740 Expect(process).To(Equal(Process{ 741 HealthCheckType: "some-type", 742 HealthCheckEndpoint: "", 743 HealthCheckInvocationTimeout: 42, 744 })) 745 }) 746 }) 747 748 When("the endpoint and timeout are not set", func() { 749 BeforeEach(func() { 750 inputProcess.HealthCheckType = "some-type" 751 752 expectedBody := `{ 753 "health_check": { 754 "type": "some-type", 755 "data": { 756 "endpoint": null 757 } 758 } 759 }` 760 responseBody := `{ 761 "health_check": { 762 "type": "some-type", 763 "data": { 764 "endpoint": null 765 } 766 } 767 }` 768 server.AppendHandlers( 769 CombineHandlers( 770 VerifyRequest(http.MethodPatch, "/v3/processes/some-process-guid"), 771 VerifyJSON(expectedBody), 772 RespondWith(http.StatusOK, responseBody, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 773 ), 774 ) 775 }) 776 777 It("patches this process's health check", func() { 778 Expect(err).ToNot(HaveOccurred()) 779 Expect(warnings).To(ConsistOf("this is a warning")) 780 Expect(process).To(MatchFields(IgnoreExtras, Fields{ 781 "HealthCheckType": Equal(constant.HealthCheckType("some-type")), 782 "HealthCheckEndpoint": BeEmpty(), 783 })) 784 }) 785 }) 786 }) 787 788 When("the process does not exist", func() { 789 BeforeEach(func() { 790 response := `{ 791 "errors": [ 792 { 793 "detail": "Process not found", 794 "title": "CF-ResourceNotFound", 795 "code": 10010 796 } 797 ] 798 }` 799 800 server.AppendHandlers( 801 CombineHandlers( 802 VerifyRequest(http.MethodPatch, "/v3/processes/some-process-guid"), 803 RespondWith(http.StatusNotFound, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 804 ), 805 ) 806 }) 807 808 It("returns an error and warnings", func() { 809 Expect(err).To(MatchError(ccerror.ProcessNotFoundError{})) 810 Expect(warnings).To(ConsistOf("this is a warning")) 811 }) 812 }) 813 814 When("the cloud controller returns errors and warnings", func() { 815 BeforeEach(func() { 816 response := `{ 817 "errors": [ 818 { 819 "code": 10008, 820 "detail": "The request is semantically invalid: command presence", 821 "title": "CF-UnprocessableEntity" 822 }, 823 { 824 "code": 10009, 825 "detail": "Some CC Error", 826 "title": "CF-SomeNewError" 827 } 828 ] 829 }` 830 server.AppendHandlers( 831 CombineHandlers( 832 VerifyRequest(http.MethodPatch, "/v3/processes/some-process-guid"), 833 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 834 ), 835 ) 836 }) 837 838 It("returns the error and all warnings", func() { 839 Expect(err).To(MatchError(ccerror.MultiError{ 840 ResponseCode: http.StatusTeapot, 841 Errors: []ccerror.V3Error{ 842 { 843 Code: 10008, 844 Detail: "The request is semantically invalid: command presence", 845 Title: "CF-UnprocessableEntity", 846 }, 847 { 848 Code: 10009, 849 Detail: "Some CC Error", 850 Title: "CF-SomeNewError", 851 }, 852 }, 853 })) 854 Expect(warnings).To(ConsistOf("this is a warning")) 855 }) 856 }) 857 }) 858 })