github.com/wanddynosios/cli/v8@v8.7.9-0.20240221182337-1a92e3a7017f/api/cloudcontroller/ccv3/route_test.go (about) 1 package ccv3_test 2 3 import ( 4 "fmt" 5 "net/http" 6 7 "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" 8 . "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" 9 "code.cloudfoundry.org/cli/resources" 10 "code.cloudfoundry.org/cli/types" 11 . "github.com/onsi/ginkgo" 12 . "github.com/onsi/gomega" 13 . "github.com/onsi/gomega/ghttp" 14 ) 15 16 var _ = Describe("Route", func() { 17 var client *Client 18 19 BeforeEach(func() { 20 client, _ = NewTestClient() 21 }) 22 23 Describe("CreateRoute", func() { 24 var ( 25 route resources.Route 26 warnings Warnings 27 executeErr error 28 spaceGUID string 29 domainGUID string 30 host string 31 path string 32 port int 33 ccv3Route resources.Route 34 ) 35 36 BeforeEach(func() { 37 host = "" 38 path = "" 39 port = 0 40 }) 41 42 JustBeforeEach(func() { 43 spaceGUID = "space-guid" 44 domainGUID = "domain-guid" 45 ccv3Route = resources.Route{SpaceGUID: spaceGUID, DomainGUID: domainGUID, Host: host, Path: path, Port: port} 46 route, warnings, executeErr = client.CreateRoute(ccv3Route) 47 }) 48 49 When("the request succeeds", func() { 50 When("no additional flags", func() { 51 BeforeEach(func() { 52 host = "" 53 response := `{ 54 "guid": "some-route-guid", 55 "relationships": { 56 "space": { 57 "data": { "guid": "space-guid" } 58 }, 59 "domain": { 60 "data": { "guid": "domain-guid" } 61 } 62 }, 63 "host": "" 64 }` 65 66 expectedBody := `{ 67 "relationships": { 68 "space": { 69 "data": { "guid": "space-guid" } 70 }, 71 "domain": { 72 "data": { "guid": "domain-guid" } 73 } 74 } 75 }` 76 77 server.AppendHandlers( 78 CombineHandlers( 79 VerifyRequest(http.MethodPost, "/v3/routes"), 80 VerifyJSON(expectedBody), 81 RespondWith(http.StatusCreated, response, http.Header{"X-Cf-Warnings": {"warning-1"}}), 82 ), 83 ) 84 }) 85 86 It("returns the given route and all warnings", func() { 87 Expect(executeErr).ToNot(HaveOccurred()) 88 Expect(warnings).To(ConsistOf("warning-1")) 89 90 Expect(route).To(Equal(resources.Route{ 91 GUID: "some-route-guid", 92 SpaceGUID: "space-guid", 93 DomainGUID: "domain-guid", 94 })) 95 }) 96 }) 97 98 When("hostname is passed in", func() { 99 100 BeforeEach(func() { 101 host = "cheesecake" 102 response := `{ 103 "guid": "some-route-guid", 104 "relationships": { 105 "space": { 106 "data": { "guid": "space-guid" } 107 }, 108 "domain": { 109 "data": { "guid": "domain-guid" } 110 } 111 }, 112 "host": "cheesecake" 113 }` 114 115 expectedBody := `{ 116 "relationships": { 117 "space": { 118 "data": { "guid": "space-guid" } 119 }, 120 "domain": { 121 "data": { "guid": "domain-guid" } 122 } 123 }, 124 "host": "cheesecake" 125 }` 126 127 server.AppendHandlers( 128 CombineHandlers( 129 VerifyRequest(http.MethodPost, "/v3/routes"), 130 VerifyJSON(expectedBody), 131 RespondWith(http.StatusCreated, response, http.Header{"X-Cf-Warnings": {"warning-1"}}), 132 ), 133 ) 134 }) 135 136 It("returns the given route and all warnings", func() { 137 Expect(executeErr).ToNot(HaveOccurred()) 138 Expect(warnings).To(ConsistOf("warning-1")) 139 140 Expect(route).To(Equal(resources.Route{ 141 GUID: "some-route-guid", 142 SpaceGUID: "space-guid", 143 DomainGUID: "domain-guid", 144 Host: "cheesecake", 145 })) 146 }) 147 }) 148 149 When("path is passed in", func() { 150 BeforeEach(func() { 151 path = "lion" 152 153 response := `{ 154 "guid": "this-route-guid", 155 "relationships": { 156 "space": { 157 "data": { 158 "guid": "space-guid" 159 } 160 }, 161 "domain": { 162 "data": { 163 "guid": "domain-guid" 164 } 165 } 166 }, 167 "path": "lion" 168 }` 169 expectedRequestBody := `{ 170 "relationships": { 171 "space": { 172 "data": { 173 "guid": "space-guid" 174 } 175 }, 176 "domain": { 177 "data": { 178 "guid": "domain-guid" 179 } 180 } 181 }, 182 "path": "lion" 183 }` 184 185 server.AppendHandlers( 186 CombineHandlers( 187 VerifyRequest(http.MethodPost, "/v3/routes"), 188 VerifyJSON(expectedRequestBody), 189 RespondWith(http.StatusCreated, response, http.Header{"X-Cf-Warnings": {"warning-1"}}), 190 ), 191 ) 192 }) 193 When("the request succeeds", func() { 194 It("returns the given route and all warnings", func() { 195 Expect(executeErr).ToNot(HaveOccurred()) 196 Expect(warnings).To(ConsistOf("warning-1")) 197 198 Expect(route).To(Equal(resources.Route{ 199 GUID: "this-route-guid", 200 SpaceGUID: "space-guid", 201 DomainGUID: "domain-guid", 202 Path: "lion", 203 })) 204 }) 205 }) 206 }) 207 208 When("port is passed in", func() { 209 BeforeEach(func() { 210 port = 1234 211 212 response := `{ 213 "guid": "this-route-guid", 214 "relationships": { 215 "space": { 216 "data": { 217 "guid": "space-guid" 218 } 219 }, 220 "domain": { 221 "data": { 222 "guid": "domain-guid" 223 } 224 } 225 }, 226 "port": 1234 227 }` 228 expectedRequestBody := `{ 229 "relationships": { 230 "space": { 231 "data": { 232 "guid": "space-guid" 233 } 234 }, 235 "domain": { 236 "data": { 237 "guid": "domain-guid" 238 } 239 } 240 }, 241 "port": 1234 242 }` 243 244 server.AppendHandlers( 245 CombineHandlers( 246 VerifyRequest(http.MethodPost, "/v3/routes"), 247 VerifyJSON(expectedRequestBody), 248 RespondWith(http.StatusCreated, response, http.Header{"X-Cf-Warnings": {"warning-1"}}), 249 ), 250 ) 251 }) 252 When("the request succeeds", func() { 253 It("returns the given route and all warnings", func() { 254 Expect(executeErr).ToNot(HaveOccurred()) 255 Expect(warnings).To(ConsistOf("warning-1")) 256 257 Expect(route).To(Equal(resources.Route{ 258 GUID: "this-route-guid", 259 SpaceGUID: "space-guid", 260 DomainGUID: "domain-guid", 261 Port: 1234, 262 })) 263 }) 264 }) 265 }) 266 }) 267 268 When("the cloud controller returns errors and warnings", func() { 269 BeforeEach(func() { 270 response := `{ 271 "errors": [ 272 { 273 "code": 10008, 274 "detail": "The request is semantically invalid: command presence", 275 "title": "CF-UnprocessableEntity" 276 }, 277 { 278 "code": 10010, 279 "detail": "Isolation segment not found", 280 "title": "CF-ResourceNotFound" 281 } 282 ] 283 }` 284 server.AppendHandlers( 285 CombineHandlers( 286 VerifyRequest(http.MethodPost, "/v3/routes"), 287 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 288 ), 289 ) 290 }) 291 292 It("returns the error and all warnings", func() { 293 Expect(executeErr).To(MatchError(ccerror.MultiError{ 294 ResponseCode: http.StatusTeapot, 295 Errors: []ccerror.V3Error{ 296 { 297 Code: 10008, 298 Detail: "The request is semantically invalid: command presence", 299 Title: "CF-UnprocessableEntity", 300 }, 301 { 302 Code: 10010, 303 Detail: "Isolation segment not found", 304 Title: "CF-ResourceNotFound", 305 }, 306 }, 307 })) 308 Expect(warnings).To(ConsistOf("this is a warning")) 309 }) 310 }) 311 }) 312 313 Describe("GetRoutes", func() { 314 var ( 315 query Query 316 routes []resources.Route 317 warnings Warnings 318 executeErr error 319 ) 320 321 JustBeforeEach(func() { 322 routes, warnings, executeErr = client.GetRoutes(query) 323 }) 324 325 When("the request succeeds", func() { 326 var ( 327 response1 string 328 response2 string 329 ) 330 331 BeforeEach(func() { 332 response1 = fmt.Sprintf(` 333 { 334 "pagination": { 335 "next": { 336 "href": "%s/v3/routes?page=2" 337 } 338 }, 339 "resources": [ 340 { 341 "guid": "route-1-guid", 342 "url": "hello", 343 "metadata": { 344 "labels": { 345 "key1": "value1" 346 } 347 } 348 }, 349 { 350 "guid": "route-2-guid", 351 "url": "bye" 352 } 353 ] 354 }`, server.URL()) 355 356 response2 = ` 357 { 358 "pagination": { 359 "next": null 360 }, 361 "resources": [ 362 { 363 "guid": "route-3-guid" 364 } 365 ] 366 }` 367 }) 368 369 When("not passing any filters", func() { 370 BeforeEach(func() { 371 query = Query{} 372 373 server.AppendHandlers( 374 CombineHandlers( 375 VerifyRequest(http.MethodGet, "/v3/routes"), 376 RespondWith(http.StatusOK, response1, http.Header{"X-Cf-Warnings": {"warning-1"}}), 377 ), 378 ) 379 server.AppendHandlers( 380 CombineHandlers( 381 VerifyRequest(http.MethodGet, "/v3/routes", "page=2"), 382 RespondWith(http.StatusOK, response2, http.Header{"X-Cf-Warnings": {"warning-2"}}), 383 ), 384 ) 385 }) 386 387 It("returns the given route and all warnings", func() { 388 Expect(executeErr).ToNot(HaveOccurred()) 389 Expect(warnings).To(ConsistOf("warning-1", "warning-2")) 390 391 Expect(routes).To(Equal([]resources.Route{ 392 { 393 GUID: "route-1-guid", 394 URL: "hello", 395 Metadata: &resources.Metadata{ 396 Labels: map[string]types.NullString{ 397 "key1": types.NewNullString("value1"), 398 }, 399 }, 400 }, 401 { 402 GUID: "route-2-guid", 403 URL: "bye", 404 }, 405 { 406 GUID: "route-3-guid", 407 }, 408 })) 409 }) 410 }) 411 412 When("passing in a query", func() { 413 BeforeEach(func() { 414 query = Query{Key: "space_guids", Values: []string{"guid1", "guid2"}} 415 416 server.AppendHandlers( 417 CombineHandlers( 418 VerifyRequest(http.MethodGet, "/v3/routes", "space_guids=guid1,guid2"), 419 RespondWith(http.StatusOK, response1, http.Header{"X-Cf-Warnings": {"warning-1"}}), 420 ), 421 ) 422 server.AppendHandlers( 423 CombineHandlers( 424 VerifyRequest(http.MethodGet, "/v3/routes", "page=2", "space_guids=guid1,guid2"), 425 RespondWith(http.StatusOK, response2, http.Header{"X-Cf-Warnings": {"warning-2"}}), 426 ), 427 ) 428 }) 429 430 It("passes query params", func() { 431 Expect(executeErr).ToNot(HaveOccurred()) 432 Expect(warnings).To(ConsistOf("warning-1", "warning-2")) 433 434 Expect(routes).To(Equal([]resources.Route{ 435 { 436 GUID: "route-1-guid", 437 URL: "hello", 438 Metadata: &resources.Metadata{ 439 Labels: map[string]types.NullString{ 440 "key1": types.NewNullString("value1"), 441 }, 442 }, 443 }, 444 { 445 GUID: "route-2-guid", 446 URL: "bye", 447 }, 448 { 449 GUID: "route-3-guid", 450 }, 451 })) 452 }) 453 }) 454 }) 455 }) 456 457 Describe("DeleteRoute", func() { 458 var ( 459 routeGUID string 460 jobURLString string 461 jobURL JobURL 462 warnings Warnings 463 executeErr error 464 ) 465 466 JustBeforeEach(func() { 467 jobURL, warnings, executeErr = client.DeleteRoute(routeGUID) 468 }) 469 470 When("route exists", func() { 471 routeGUID = "route-guid" 472 jobURLString = "https://api.test.com/v3/jobs/job-guid" 473 474 BeforeEach(func() { 475 server.AppendHandlers( 476 CombineHandlers( 477 VerifyRequest(http.MethodDelete, "/v3/routes/route-guid"), 478 RespondWith(http.StatusAccepted, nil, http.Header{ 479 "X-Cf-Warnings": {"this is a warning"}, 480 "Location": {jobURLString}, 481 }), 482 ), 483 ) 484 }) 485 486 It("returns all warnings", func() { 487 Expect(executeErr).NotTo(HaveOccurred()) 488 Expect(jobURL).To(Equal(JobURL(jobURLString))) 489 Expect(warnings).To(ConsistOf("this is a warning")) 490 }) 491 }) 492 493 When("the cloud controller returns errors and warnings", func() { 494 BeforeEach(func() { 495 response := `{ 496 "errors": [ 497 { 498 "code": 10008, 499 "detail": "The request is semantically invalid: command presence", 500 "title": "CF-UnprocessableEntity" 501 }, 502 { 503 "code": 10010, 504 "detail": "Isolation segment not found", 505 "title": "CF-ResourceNotFound" 506 } 507 ] 508 }` 509 server.AppendHandlers( 510 CombineHandlers( 511 VerifyRequest(http.MethodDelete, "/v3/routes/route-guid"), 512 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 513 ), 514 ) 515 }) 516 517 It("returns the error and all warnings", func() { 518 Expect(executeErr).To(MatchError(ccerror.MultiError{ 519 ResponseCode: http.StatusTeapot, 520 Errors: []ccerror.V3Error{ 521 { 522 Code: 10008, 523 Detail: "The request is semantically invalid: command presence", 524 Title: "CF-UnprocessableEntity", 525 }, 526 { 527 Code: 10010, 528 Detail: "Isolation segment not found", 529 Title: "CF-ResourceNotFound", 530 }, 531 }, 532 })) 533 Expect(warnings).To(ConsistOf("this is a warning")) 534 }) 535 }) 536 }) 537 538 Describe("MapRoute", func() { 539 var ( 540 routeGUID = "route-guid" 541 appGUID = "app-guid" 542 destinationProtocol string 543 expectedBody string 544 warnings Warnings 545 executeErr error 546 ) 547 548 JustBeforeEach(func() { 549 response := `{}` 550 server.AppendHandlers( 551 CombineHandlers( 552 VerifyRequest(http.MethodPost, "/v3/routes/route-guid/destinations"), 553 VerifyJSON(expectedBody), 554 RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"warning-1"}}), 555 ), 556 ) 557 warnings, executeErr = client.MapRoute(routeGUID, appGUID, destinationProtocol) 558 }) 559 560 When("the request is successful", func() { 561 BeforeEach(func() { 562 destinationProtocol = "http2" 563 expectedBody = fmt.Sprintf(` 564 { 565 "destinations": [ 566 { 567 "app": { 568 "guid": "%s" 569 }, 570 "protocol": "%s" 571 } 572 ] 573 } 574 `, appGUID, destinationProtocol) 575 }) 576 577 It("returns the warnings and no error", func() { 578 Expect(executeErr).ToNot(HaveOccurred()) 579 Expect(warnings).To(ConsistOf("warning-1")) 580 }) 581 }) 582 583 Context("when destination protocol is not provided", func() { 584 BeforeEach(func() { 585 destinationProtocol = "" 586 expectedBody = fmt.Sprintf(` 587 { 588 "destinations": [ 589 { 590 "app": { 591 "guid": "%s" 592 } 593 } 594 ] 595 } 596 `, appGUID) 597 }) 598 599 It("does not include it in the request", func() { 600 Expect(executeErr).ToNot(HaveOccurred()) 601 }) 602 }) 603 604 When("the cloud controller returns errors and warnings", func() { 605 BeforeEach(func() { 606 response := `{ 607 "errors": [ 608 { 609 "code": 10008, 610 "detail": "The request is semantically invalid: command presence", 611 "title": "CF-UnprocessableEntity" 612 }, 613 { 614 "code": 10010, 615 "detail": "Isolation segment not found", 616 "title": "CF-ResourceNotFound" 617 } 618 ] 619 }` 620 server.AppendHandlers( 621 CombineHandlers( 622 VerifyRequest(http.MethodPost, "/v3/routes/route-guid/destinations"), 623 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 624 ), 625 ) 626 }) 627 628 It("returns the error and all warnings", func() { 629 Expect(executeErr).To(MatchError(ccerror.MultiError{ 630 ResponseCode: http.StatusTeapot, 631 Errors: []ccerror.V3Error{ 632 { 633 Code: 10008, 634 Detail: "The request is semantically invalid: command presence", 635 Title: "CF-UnprocessableEntity", 636 }, 637 { 638 Code: 10010, 639 Detail: "Isolation segment not found", 640 Title: "CF-ResourceNotFound", 641 }, 642 }, 643 })) 644 Expect(warnings).To(ConsistOf("this is a warning")) 645 }) 646 }) 647 }) 648 649 Describe("GetRouteDestinations", func() { 650 var ( 651 routeGUID = "some-route-guid" 652 destinations []resources.RouteDestination 653 warnings Warnings 654 executeErr error 655 ) 656 657 JustBeforeEach(func() { 658 destinations, warnings, executeErr = client.GetRouteDestinations(routeGUID) 659 }) 660 661 When("the request succeeds", func() { 662 var ( 663 response string 664 ) 665 666 BeforeEach(func() { 667 response = ` 668 { 669 "destinations": [ 670 { 671 "guid": "destination-1-guid", 672 "app": { 673 "guid": "app-1-guid", 674 "process": { 675 "type": "web" 676 } 677 } 678 }, 679 { 680 "guid": "destination-2-guid", 681 "app": { 682 "guid": "app-2-guid", 683 "process": { 684 "type": "worker" 685 } 686 } 687 } 688 ] 689 }` 690 }) 691 692 When("the request succeeds", func() { 693 BeforeEach(func() { 694 server.AppendHandlers( 695 CombineHandlers( 696 VerifyRequest(http.MethodGet, "/v3/routes/some-route-guid/destinations"), 697 RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"warning-1"}}), 698 ), 699 ) 700 }) 701 702 It("returns destinations and all warnings", func() { 703 Expect(executeErr).ToNot(HaveOccurred()) 704 Expect(warnings).To(ConsistOf("warning-1")) 705 706 Expect(destinations).To(Equal([]resources.RouteDestination{ 707 { 708 GUID: "destination-1-guid", 709 App: resources.RouteDestinationApp{GUID: "app-1-guid", Process: struct{ Type string }{Type: "web"}}, 710 }, 711 { 712 GUID: "destination-2-guid", 713 App: resources.RouteDestinationApp{GUID: "app-2-guid", Process: struct{ Type string }{Type: "worker"}}, 714 }, 715 })) 716 }) 717 }) 718 }) 719 }) 720 721 Describe("UnmapRoute", func() { 722 var ( 723 routeGUID string 724 destinationGUID string 725 warnings Warnings 726 executeErr error 727 ) 728 729 JustBeforeEach(func() { 730 warnings, executeErr = client.UnmapRoute(routeGUID, destinationGUID) 731 }) 732 733 When("route exists", func() { 734 routeGUID = "route-guid" 735 destinationGUID = "destination-guid" 736 737 BeforeEach(func() { 738 server.AppendHandlers( 739 CombineHandlers( 740 VerifyRequest(http.MethodDelete, "/v3/routes/route-guid/destinations/destination-guid"), 741 RespondWith(http.StatusNoContent, nil, http.Header{ 742 "X-Cf-Warnings": {"this is a warning"}, 743 }), 744 ), 745 ) 746 }) 747 748 It("returns all warnings", func() { 749 Expect(executeErr).NotTo(HaveOccurred()) 750 Expect(warnings).To(ConsistOf("this is a warning")) 751 }) 752 }) 753 754 When("the cloud controller returns errors and warnings", func() { 755 BeforeEach(func() { 756 response := `{ 757 "errors": [ 758 { 759 "code": 10008, 760 "detail": "The request is semantically invalid: command presence", 761 "title": "CF-UnprocessableEntity" 762 }, 763 { 764 "code": 10010, 765 "detail": "Isolation segment not found", 766 "title": "CF-ResourceNotFound" 767 } 768 ] 769 }` 770 server.AppendHandlers( 771 CombineHandlers( 772 VerifyRequest(http.MethodDelete, "/v3/routes/route-guid/destinations/destination-guid"), 773 RespondWith(http.StatusTeapot, response, http.Header{ 774 "X-Cf-Warnings": {"this is a warning"}, 775 }), 776 ), 777 ) 778 }) 779 780 It("returns the error and all warnings", func() { 781 Expect(executeErr).To(MatchError(ccerror.MultiError{ 782 ResponseCode: http.StatusTeapot, 783 Errors: []ccerror.V3Error{ 784 { 785 Code: 10008, 786 Detail: "The request is semantically invalid: command presence", 787 Title: "CF-UnprocessableEntity", 788 }, 789 { 790 Code: 10010, 791 Detail: "Isolation segment not found", 792 Title: "CF-ResourceNotFound", 793 }, 794 }, 795 })) 796 Expect(warnings).To(ConsistOf("this is a warning")) 797 }) 798 }) 799 }) 800 801 Describe("UnshareRoute", func() { 802 var ( 803 routeGUID string 804 spaceGUID string 805 warnings Warnings 806 executeErr error 807 ) 808 809 JustBeforeEach(func() { 810 warnings, executeErr = client.UnshareRoute(routeGUID, spaceGUID) 811 }) 812 813 When("aroute exists and is shared with the space", func() { 814 routeGUID = "route-guid" 815 spaceGUID = "space-guid" 816 817 BeforeEach(func() { 818 server.AppendHandlers( 819 CombineHandlers( 820 VerifyRequest(http.MethodDelete, "/v3/routes/route-guid/relationships/shared_spaces/space-guid"), 821 RespondWith(http.StatusNoContent, nil, http.Header{ 822 "X-Cf-Warnings": {"this is a warning"}, 823 }), 824 ), 825 ) 826 }) 827 828 It("returns warnings", func() { 829 Expect(executeErr).NotTo(HaveOccurred()) 830 Expect(warnings).To(ConsistOf("this is a warning")) 831 }) 832 }) 833 834 When("the cloud controller returns errors and warnings", func() { 835 BeforeEach(func() { 836 response := `{ 837 "errors": [ 838 { 839 "code": 10008, 840 "detail": "The request is semantically invalid: command presence", 841 "title": "CF-UnprocessableEntity" 842 }, 843 { 844 "code": 10010, 845 "detail": "Route not found", 846 "title": "CF-ResourceNotFound" 847 } 848 ] 849 }` 850 server.AppendHandlers( 851 CombineHandlers( 852 VerifyRequest(http.MethodDelete, "/v3/routes/route-guid/relationships/shared_spaces/space-guid"), 853 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 854 ), 855 ) 856 }) 857 858 It("returns the error and all warnings", func() { 859 Expect(executeErr).To(MatchError(ccerror.MultiError{ 860 ResponseCode: http.StatusTeapot, 861 Errors: []ccerror.V3Error{ 862 { 863 Code: 10008, 864 Detail: "The request is semantically invalid: command presence", 865 Title: "CF-UnprocessableEntity", 866 }, 867 { 868 Code: 10010, 869 Detail: "Route not found", 870 Title: "CF-ResourceNotFound", 871 }, 872 }, 873 })) 874 Expect(warnings).To(ConsistOf("this is a warning")) 875 }) 876 }) 877 }) 878 879 Describe("UpdateDestination", func() { 880 var ( 881 routeGUID string 882 destinationGUID string 883 protocol string 884 warnings Warnings 885 executeErr error 886 ) 887 888 JustBeforeEach(func() { 889 warnings, executeErr = client.UpdateDestination(routeGUID, destinationGUID, protocol) 890 }) 891 892 When("the request succeeds", func() { 893 routeGUID = "route-guid" 894 destinationGUID = "destination-guid" 895 protocol = "http2" 896 897 expectedBody := fmt.Sprintf(`{ 898 "protocol": "%s" 899 }`, protocol) 900 901 BeforeEach(func() { 902 server.AppendHandlers( 903 CombineHandlers( 904 VerifyRequest(http.MethodPatch, "/v3/routes/route-guid/destinations/destination-guid"), 905 VerifyJSON(expectedBody), 906 RespondWith(http.StatusNoContent, nil, http.Header{ 907 "X-Cf-Warnings": {"this is a warning"}, 908 }), 909 ), 910 ) 911 }) 912 913 It("returns all warnings", func() { 914 Expect(executeErr).NotTo(HaveOccurred()) 915 Expect(warnings).To(ConsistOf("this is a warning")) 916 }) 917 }) 918 919 When("the cloud controller returns errors and warnings", func() { 920 BeforeEach(func() { 921 response := `{ 922 "errors": [ 923 { 924 "code": 10008, 925 "detail": "The request is semantically invalid: command presence", 926 "title": "CF-UnprocessableEntity" 927 }, 928 { 929 "code": 10010, 930 "detail": "Isolation segment not found", 931 "title": "CF-ResourceNotFound" 932 } 933 ] 934 }` 935 server.AppendHandlers( 936 CombineHandlers( 937 VerifyRequest(http.MethodPatch, "/v3/routes/route-guid/destinations/destination-guid"), 938 RespondWith(http.StatusTeapot, response, http.Header{ 939 "X-Cf-Warnings": {"this is a warning"}, 940 }), 941 ), 942 ) 943 }) 944 945 It("returns the error and all warnings", func() { 946 Expect(executeErr).To(MatchError(ccerror.MultiError{ 947 ResponseCode: http.StatusTeapot, 948 Errors: []ccerror.V3Error{ 949 { 950 Code: 10008, 951 Detail: "The request is semantically invalid: command presence", 952 Title: "CF-UnprocessableEntity", 953 }, 954 { 955 Code: 10010, 956 Detail: "Isolation segment not found", 957 Title: "CF-ResourceNotFound", 958 }, 959 }, 960 })) 961 Expect(warnings).To(ConsistOf("this is a warning")) 962 }) 963 }) 964 }) 965 966 Describe("DeleteOrphanedRoutes", func() { 967 var ( 968 spaceGUID string 969 warnings Warnings 970 executeErr error 971 jobURL JobURL 972 ) 973 JustBeforeEach(func() { 974 jobURL, warnings, executeErr = client.DeleteOrphanedRoutes(spaceGUID) 975 }) 976 977 When("the API succeeds", func() { 978 BeforeEach(func() { 979 spaceGUID = "space-guid" 980 server.AppendHandlers( 981 CombineHandlers( 982 VerifyRequest(http.MethodDelete, "/v3/spaces/space-guid/routes", "unmapped=true"), 983 RespondWith( 984 http.StatusAccepted, 985 nil, 986 http.Header{"X-Cf-Warnings": {"orphaned-warning"}, "Location": {"job-url"}}, 987 ), 988 ), 989 ) 990 }) 991 992 It("returns the warnings and a job", func() { 993 Expect(executeErr).ToNot(HaveOccurred()) 994 Expect(warnings).To(ConsistOf("orphaned-warning")) 995 Expect(jobURL).To(Equal(JobURL("job-url"))) 996 }) 997 }) 998 999 When("the API fails", func() { 1000 BeforeEach(func() { 1001 spaceGUID = "space-guid" 1002 response := `{ 1003 "errors": [ 1004 { 1005 "code": 10008, 1006 "detail": "The request is semantically invalid: command presence", 1007 "title": "CF-UnprocessableEntity" 1008 }, 1009 { 1010 "code": 10010, 1011 "detail": "Isolation segment not found", 1012 "title": "CF-ResourceNotFound" 1013 } 1014 ] 1015 }` 1016 server.AppendHandlers( 1017 CombineHandlers( 1018 VerifyRequest(http.MethodDelete, "/v3/spaces/space-guid/routes", "unmapped=true"), 1019 RespondWith( 1020 http.StatusTeapot, 1021 response, 1022 http.Header{"X-Cf-Warnings": {"orphaned-warning"}}, 1023 ), 1024 ), 1025 ) 1026 }) 1027 1028 It("returns the warnings and a job", func() { 1029 1030 Expect(executeErr).To(MatchError(ccerror.MultiError{ 1031 ResponseCode: http.StatusTeapot, 1032 Errors: []ccerror.V3Error{ 1033 { 1034 Code: 10008, 1035 Detail: "The request is semantically invalid: command presence", 1036 Title: "CF-UnprocessableEntity", 1037 }, 1038 { 1039 Code: 10010, 1040 Detail: "Isolation segment not found", 1041 Title: "CF-ResourceNotFound", 1042 }, 1043 }, 1044 })) 1045 Expect(warnings).To(ConsistOf("orphaned-warning")) 1046 }) 1047 }) 1048 }) 1049 1050 Describe("GetApplicationRoutes", func() { 1051 var ( 1052 appGUID string 1053 1054 routes []resources.Route 1055 warnings Warnings 1056 executeErr error 1057 ) 1058 1059 BeforeEach(func() { 1060 appGUID = "some-app-guid" 1061 }) 1062 1063 JustBeforeEach(func() { 1064 routes, warnings, executeErr = client.GetApplicationRoutes(appGUID) 1065 }) 1066 1067 When("the request succeeds", func() { 1068 BeforeEach(func() { 1069 body := `{ 1070 "resources": [ 1071 { 1072 "guid": "route-guid", 1073 "host": "host", 1074 "path": "/path", 1075 "url": "host.domain.com/path", 1076 "relationships": { 1077 "space": { 1078 "data": { 1079 "guid": "space-guid" 1080 } 1081 }, 1082 "domain": { 1083 "data": { 1084 "guid": "domain-guid" 1085 } 1086 } 1087 } 1088 }, { 1089 "guid": "route2-guid", 1090 "host": "", 1091 "path": "", 1092 "url": "domain.com", 1093 "relationships": { 1094 "space": { 1095 "data": { 1096 "guid": "space-guid" 1097 } 1098 }, 1099 "domain": { 1100 "data": { 1101 "guid": "domain2-guid" 1102 } 1103 } 1104 } 1105 } 1106 ] 1107 }` 1108 server.AppendHandlers( 1109 CombineHandlers( 1110 VerifyRequest(http.MethodGet, "/v3/apps/some-app-guid/routes"), 1111 RespondWith( 1112 http.StatusOK, 1113 body, 1114 http.Header{"X-Cf-Warnings": {"get-app-routes-warning"}, "Location": {"job-url"}}, 1115 ), 1116 ), 1117 ) 1118 }) 1119 1120 It("returns an array of routes", func() { 1121 Expect(executeErr).NotTo(HaveOccurred()) 1122 Expect(warnings).To(ConsistOf("get-app-routes-warning")) 1123 1124 Expect(routes).To(ConsistOf( 1125 resources.Route{ 1126 GUID: "route-guid", 1127 DomainGUID: "domain-guid", 1128 SpaceGUID: "space-guid", 1129 Host: "host", 1130 Path: "/path", 1131 URL: "host.domain.com/path", 1132 }, 1133 resources.Route{ 1134 GUID: "route2-guid", 1135 DomainGUID: "domain2-guid", 1136 SpaceGUID: "space-guid", 1137 Host: "", 1138 Path: "", 1139 URL: "domain.com", 1140 }, 1141 )) 1142 }) 1143 }) 1144 1145 When("there is a cc error", func() { 1146 BeforeEach(func() { 1147 response := `{ 1148 "errors": [ 1149 { 1150 "code": 10008, 1151 "detail": "The request is semantically invalid: command presence", 1152 "title": "CF-UnprocessableEntity" 1153 }, 1154 { 1155 "code": 10010, 1156 "detail": "Isolation segment not found", 1157 "title": "CF-ResourceNotFound" 1158 } 1159 ] 1160 }` 1161 server.AppendHandlers( 1162 CombineHandlers( 1163 VerifyRequest(http.MethodGet, "/v3/apps/some-app-guid/routes"), 1164 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"get-app-routes-warning"}, "Location": {"job-url"}}), 1165 ), 1166 ) 1167 }) 1168 1169 It("returns the error", func() { 1170 Expect(executeErr).To(MatchError(ccerror.MultiError{ 1171 ResponseCode: http.StatusTeapot, 1172 Errors: []ccerror.V3Error{ 1173 { 1174 Code: 10008, 1175 Detail: "The request is semantically invalid: command presence", 1176 Title: "CF-UnprocessableEntity", 1177 }, 1178 { 1179 Code: 10010, 1180 Detail: "Isolation segment not found", 1181 Title: "CF-ResourceNotFound", 1182 }, 1183 }, 1184 })) 1185 Expect(warnings).To(ConsistOf("get-app-routes-warning")) 1186 }) 1187 }) 1188 }) 1189 })