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