github.com/arunkumar7540/cli@v6.45.0+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": {}}}`)) 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": {}}}`)) 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 "HealthCheckTimeout": BeEquivalentTo(90), 377 })) 378 }) 379 }) 380 381 When("the application does not exist", func() { 382 BeforeEach(func() { 383 response := `{ 384 "errors": [ 385 { 386 "detail": "Application not found", 387 "title": "CF-ResourceNotFound", 388 "code": 10010 389 } 390 ] 391 }` 392 server.AppendHandlers( 393 CombineHandlers( 394 VerifyRequest(http.MethodGet, "/v3/apps/some-app-guid/processes/some-type"), 395 RespondWith(http.StatusNotFound, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 396 ), 397 ) 398 }) 399 400 It("returns a ResourceNotFoundError", func() { 401 Expect(warnings).To(ConsistOf("this is a warning")) 402 Expect(err).To(MatchError(ccerror.ResourceNotFoundError{Message: "Application not found"})) 403 }) 404 }) 405 406 When("the cloud controller returns errors and warnings", func() { 407 BeforeEach(func() { 408 response := `{ 409 "errors": [ 410 { 411 "code": 10008, 412 "detail": "The request is semantically invalid: command presence", 413 "title": "CF-UnprocessableEntity" 414 }, 415 { 416 "code": 10009, 417 "detail": "Some CC Error", 418 "title": "CF-SomeNewError" 419 } 420 ] 421 }` 422 server.AppendHandlers( 423 CombineHandlers( 424 VerifyRequest(http.MethodGet, "/v3/apps/some-app-guid/processes/some-type"), 425 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 426 ), 427 ) 428 }) 429 430 It("returns the error and all warnings", func() { 431 Expect(err).To(MatchError(ccerror.MultiError{ 432 ResponseCode: http.StatusTeapot, 433 Errors: []ccerror.V3Error{ 434 { 435 Code: 10008, 436 Detail: "The request is semantically invalid: command presence", 437 Title: "CF-UnprocessableEntity", 438 }, 439 { 440 Code: 10009, 441 Detail: "Some CC Error", 442 Title: "CF-SomeNewError", 443 }, 444 }, 445 })) 446 Expect(warnings).To(ConsistOf("this is a warning")) 447 }) 448 }) 449 }) 450 451 Describe("GetApplicationProcesses", func() { 452 When("the application exists", func() { 453 BeforeEach(func() { 454 response1 := fmt.Sprintf(` 455 { 456 "pagination": { 457 "next": { 458 "href": "%s/v3/apps/some-app-guid/processes?page=2" 459 } 460 }, 461 "resources": [ 462 { 463 "guid": "process-1-guid", 464 "type": "web", 465 "command": "[PRIVATE DATA HIDDEN IN LISTS]", 466 "memory_in_mb": 32, 467 "health_check": { 468 "type": "port", 469 "data": { 470 "timeout": null, 471 "endpoint": null 472 } 473 } 474 }, 475 { 476 "guid": "process-2-guid", 477 "type": "worker", 478 "command": "[PRIVATE DATA HIDDEN IN LISTS]", 479 "memory_in_mb": 64, 480 "health_check": { 481 "type": "http", 482 "data": { 483 "timeout": 60, 484 "endpoint": "/health" 485 } 486 } 487 } 488 ] 489 }`, server.URL()) 490 response2 := ` 491 { 492 "pagination": { 493 "next": null 494 }, 495 "resources": [ 496 { 497 "guid": "process-3-guid", 498 "type": "console", 499 "command": "[PRIVATE DATA HIDDEN IN LISTS]", 500 "memory_in_mb": 128, 501 "health_check": { 502 "type": "process", 503 "data": { 504 "timeout": 90, 505 "endpoint": null 506 } 507 } 508 } 509 ] 510 }` 511 server.AppendHandlers( 512 CombineHandlers( 513 VerifyRequest(http.MethodGet, "/v3/apps/some-app-guid/processes"), 514 RespondWith(http.StatusOK, response1, http.Header{"X-Cf-Warnings": {"warning-1"}}), 515 ), 516 ) 517 server.AppendHandlers( 518 CombineHandlers( 519 VerifyRequest(http.MethodGet, "/v3/apps/some-app-guid/processes", "page=2"), 520 RespondWith(http.StatusOK, response2, http.Header{"X-Cf-Warnings": {"warning-2"}}), 521 ), 522 ) 523 }) 524 525 It("returns a list of processes associated with the application and all warnings", func() { 526 processes, warnings, err := client.GetApplicationProcesses("some-app-guid") 527 Expect(err).ToNot(HaveOccurred()) 528 529 Expect(processes).To(ConsistOf( 530 Process{ 531 GUID: "process-1-guid", 532 Type: constant.ProcessTypeWeb, 533 Command: types.FilteredString{IsSet: true, Value: "[PRIVATE DATA HIDDEN IN LISTS]"}, 534 MemoryInMB: types.NullUint64{Value: 32, IsSet: true}, 535 HealthCheckType: constant.Port, 536 HealthCheckTimeout: 0, 537 }, 538 Process{ 539 GUID: "process-2-guid", 540 Type: "worker", 541 Command: types.FilteredString{IsSet: true, Value: "[PRIVATE DATA HIDDEN IN LISTS]"}, 542 MemoryInMB: types.NullUint64{Value: 64, IsSet: true}, 543 HealthCheckType: constant.HTTP, 544 HealthCheckEndpoint: "/health", 545 HealthCheckTimeout: 60, 546 }, 547 Process{ 548 GUID: "process-3-guid", 549 Type: "console", 550 Command: types.FilteredString{IsSet: true, Value: "[PRIVATE DATA HIDDEN IN LISTS]"}, 551 MemoryInMB: types.NullUint64{Value: 128, IsSet: true}, 552 HealthCheckType: constant.Process, 553 HealthCheckTimeout: 90, 554 }, 555 )) 556 Expect(warnings).To(ConsistOf("warning-1", "warning-2")) 557 }) 558 }) 559 560 When("cloud controller returns an error", func() { 561 BeforeEach(func() { 562 response := `{ 563 "errors": [ 564 { 565 "code": 10010, 566 "detail": "App not found", 567 "title": "CF-ResourceNotFound" 568 } 569 ] 570 }` 571 server.AppendHandlers( 572 CombineHandlers( 573 VerifyRequest(http.MethodGet, "/v3/apps/some-app-guid/processes"), 574 RespondWith(http.StatusNotFound, response), 575 ), 576 ) 577 }) 578 579 It("returns the error", func() { 580 _, _, err := client.GetApplicationProcesses("some-app-guid") 581 Expect(err).To(MatchError(ccerror.ApplicationNotFoundError{})) 582 }) 583 }) 584 }) 585 586 Describe("UpdateProcess", func() { 587 var ( 588 inputProcess Process 589 590 process Process 591 warnings []string 592 err error 593 ) 594 595 BeforeEach(func() { 596 inputProcess = Process{ 597 GUID: "some-process-guid", 598 } 599 }) 600 601 JustBeforeEach(func() { 602 process, warnings, err = client.UpdateProcess(inputProcess) 603 }) 604 605 When("patching the process succeeds", func() { 606 When("the command is set", func() { 607 When("the start command is an arbitrary command", func() { 608 BeforeEach(func() { 609 inputProcess.Command = types.FilteredString{IsSet: true, Value: "some-command"} 610 611 expectedBody := `{ 612 "command": "some-command" 613 }` 614 615 expectedResponse := `{ 616 "command": "some-command" 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 command with the provided command", func() { 629 Expect(err).ToNot(HaveOccurred()) 630 Expect(warnings).To(ConsistOf("this is a warning")) 631 Expect(process).To(MatchFields(IgnoreExtras, Fields{ 632 "Command": Equal(types.FilteredString{IsSet: true, Value: "some-command"}), 633 })) 634 }) 635 }) 636 637 When("the start command reset", func() { 638 BeforeEach(func() { 639 inputProcess.Command = types.FilteredString{IsSet: true} 640 641 expectedBody := `{ 642 "command": null 643 }` 644 645 expectedResponse := `{ 646 "command": "some-default-command" 647 }` 648 649 server.AppendHandlers( 650 CombineHandlers( 651 VerifyRequest(http.MethodPatch, "/v3/processes/some-process-guid"), 652 VerifyJSON(expectedBody), 653 RespondWith(http.StatusOK, expectedResponse, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 654 ), 655 ) 656 }) 657 658 It("patches this process's command with 'null' and returns the default command", func() { 659 Expect(err).ToNot(HaveOccurred()) 660 Expect(warnings).To(ConsistOf("this is a warning")) 661 Expect(process).To(MatchFields(IgnoreExtras, Fields{ 662 "Command": Equal(types.FilteredString{IsSet: true, Value: "some-default-command"}), 663 })) 664 }) 665 }) 666 }) 667 668 When("the endpoint is set", func() { 669 BeforeEach(func() { 670 inputProcess.HealthCheckEndpoint = "some-endpoint" 671 inputProcess.HealthCheckType = "some-type" 672 673 expectedBody := `{ 674 "health_check": { 675 "type": "some-type", 676 "data": { 677 "endpoint": "some-endpoint" 678 } 679 } 680 }` 681 expectedResponse := `{ 682 "health_check": { 683 "type": "some-type", 684 "data": { 685 "endpoint": "some-endpoint", 686 "invocation_timeout": null 687 } 688 } 689 }` 690 server.AppendHandlers( 691 CombineHandlers( 692 VerifyRequest(http.MethodPatch, "/v3/processes/some-process-guid"), 693 VerifyJSON(expectedBody), 694 RespondWith(http.StatusOK, expectedResponse, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 695 ), 696 ) 697 }) 698 699 It("patches this process's health check", func() { 700 Expect(err).ToNot(HaveOccurred()) 701 Expect(warnings).To(ConsistOf("this is a warning")) 702 Expect(process).To(MatchFields(IgnoreExtras, Fields{ 703 "HealthCheckType": Equal(constant.HealthCheckType("some-type")), 704 "HealthCheckEndpoint": Equal("some-endpoint"), 705 })) 706 }) 707 }) 708 709 When("the invocation timeout is set", func() { 710 BeforeEach(func() { 711 inputProcess.HealthCheckInvocationTimeout = 42 712 inputProcess.HealthCheckType = "some-type" 713 714 expectedBody := `{ 715 "health_check": { 716 "type": "some-type", 717 "data": { 718 "invocation_timeout": 42 719 } 720 } 721 }` 722 expectedResponse := `{ 723 "health_check": { 724 "type": "some-type", 725 "data": { 726 "invocation_timeout": 42 727 } 728 } 729 }` 730 server.AppendHandlers( 731 CombineHandlers( 732 VerifyRequest(http.MethodPatch, "/v3/processes/some-process-guid"), 733 VerifyJSON(expectedBody), 734 RespondWith(http.StatusOK, expectedResponse, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 735 ), 736 ) 737 }) 738 739 It("patches this process's health check", func() { 740 Expect(err).ToNot(HaveOccurred()) 741 Expect(warnings).To(ConsistOf("this is a warning")) 742 Expect(process).To(Equal(Process{ 743 HealthCheckType: "some-type", 744 HealthCheckInvocationTimeout: 42, 745 })) 746 }) 747 }) 748 749 When("the health check timeout is set", func() { 750 BeforeEach(func() { 751 inputProcess.HealthCheckTimeout = 77 752 inputProcess.HealthCheckType = "some-type" 753 754 expectedBody := `{ 755 "health_check": { 756 "type": "some-type", 757 "data": { 758 "timeout": 77 759 } 760 } 761 }` 762 expectedResponse := `{ 763 "health_check": { 764 "type": "some-type", 765 "data": { 766 "timeout": 77 767 } 768 } 769 }` 770 server.AppendHandlers( 771 CombineHandlers( 772 VerifyRequest(http.MethodPatch, "/v3/processes/some-process-guid"), 773 VerifyJSON(expectedBody), 774 RespondWith(http.StatusOK, expectedResponse, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 775 ), 776 ) 777 }) 778 779 It("patches this process's health check", func() { 780 Expect(err).ToNot(HaveOccurred()) 781 Expect(warnings).To(ConsistOf("this is a warning")) 782 Expect(process).To(Equal(Process{ 783 HealthCheckType: "some-type", 784 HealthCheckEndpoint: "", 785 HealthCheckTimeout: 77, 786 })) 787 }) 788 }) 789 790 When("the endpoint and timeout are not set", func() { 791 BeforeEach(func() { 792 inputProcess.HealthCheckType = "some-type" 793 794 expectedBody := `{ 795 "health_check": { 796 "type": "some-type", 797 "data": {} 798 } 799 }` 800 responseBody := `{ 801 "health_check": { 802 "type": "some-type" 803 } 804 }` 805 server.AppendHandlers( 806 CombineHandlers( 807 VerifyRequest(http.MethodPatch, "/v3/processes/some-process-guid"), 808 VerifyJSON(expectedBody), 809 RespondWith(http.StatusOK, responseBody, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 810 ), 811 ) 812 }) 813 814 It("patches this process's health check", func() { 815 Expect(err).ToNot(HaveOccurred()) 816 Expect(warnings).To(ConsistOf("this is a warning")) 817 Expect(process).To(MatchFields(IgnoreExtras, Fields{ 818 "HealthCheckType": Equal(constant.HealthCheckType("some-type")), 819 })) 820 }) 821 }) 822 }) 823 824 When("the process does not exist", func() { 825 BeforeEach(func() { 826 response := `{ 827 "errors": [ 828 { 829 "detail": "Process not found", 830 "title": "CF-ResourceNotFound", 831 "code": 10010 832 } 833 ] 834 }` 835 836 server.AppendHandlers( 837 CombineHandlers( 838 VerifyRequest(http.MethodPatch, "/v3/processes/some-process-guid"), 839 RespondWith(http.StatusNotFound, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 840 ), 841 ) 842 }) 843 844 It("returns an error and warnings", func() { 845 Expect(err).To(MatchError(ccerror.ProcessNotFoundError{})) 846 Expect(warnings).To(ConsistOf("this is a warning")) 847 }) 848 }) 849 850 When("the cloud controller returns errors and warnings", func() { 851 BeforeEach(func() { 852 response := `{ 853 "errors": [ 854 { 855 "code": 10008, 856 "detail": "The request is semantically invalid: command presence", 857 "title": "CF-UnprocessableEntity" 858 }, 859 { 860 "code": 10009, 861 "detail": "Some CC Error", 862 "title": "CF-SomeNewError" 863 } 864 ] 865 }` 866 server.AppendHandlers( 867 CombineHandlers( 868 VerifyRequest(http.MethodPatch, "/v3/processes/some-process-guid"), 869 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 870 ), 871 ) 872 }) 873 874 It("returns the error and all warnings", func() { 875 Expect(err).To(MatchError(ccerror.MultiError{ 876 ResponseCode: http.StatusTeapot, 877 Errors: []ccerror.V3Error{ 878 { 879 Code: 10008, 880 Detail: "The request is semantically invalid: command presence", 881 Title: "CF-UnprocessableEntity", 882 }, 883 { 884 Code: 10009, 885 Detail: "Some CC Error", 886 Title: "CF-SomeNewError", 887 }, 888 }, 889 })) 890 Expect(warnings).To(ConsistOf("this is a warning")) 891 }) 892 }) 893 }) 894 })