github.com/sleungcy/cli@v7.1.0+incompatible/api/cloudcontroller/ccv3/service_plan_test.go (about) 1 package ccv3_test 2 3 import ( 4 "fmt" 5 "net/http" 6 7 "code.cloudfoundry.org/cli/resources" 8 9 "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" 10 . "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" 11 12 . "github.com/onsi/ginkgo" 13 . "github.com/onsi/gomega" 14 . "github.com/onsi/gomega/ghttp" 15 ) 16 17 var _ = Describe("Service Plan", func() { 18 var client *Client 19 var query []Query 20 21 BeforeEach(func() { 22 client, _ = NewTestClient() 23 query = []Query{} 24 }) 25 26 Describe("GetServicePlans", func() { 27 var ( 28 plans []resources.ServicePlan 29 warnings Warnings 30 executeErr error 31 ) 32 33 JustBeforeEach(func() { 34 plans, warnings, executeErr = client.GetServicePlans(query...) 35 }) 36 37 When("service plans exist", func() { 38 BeforeEach(func() { 39 response1 := fmt.Sprintf(` 40 { 41 "pagination": { 42 "next": { 43 "href": "%s/v3/service_plans?names=myServicePlan&service_broker_names=myServiceBroker&service_offering_names=someOffering&page=2" 44 } 45 }, 46 "resources": [ 47 { 48 "guid": "service-plan-1-guid", 49 "name": "service-plan-1-name", 50 "description": "service-plan-1-description", 51 "available": true, 52 "free": true, 53 "visibility_type": "public", 54 "relationships": { 55 "service_offering": { 56 "data": { 57 "guid": "79d428b9-75b4-44db-addf-19c85c7f0f1e" 58 } 59 } 60 } 61 }, 62 { 63 "guid": "service-plan-2-guid", 64 "name": "service-plan-2-name", 65 "available": false, 66 "visibility_type": "admin", 67 "description": "service-plan-2-description", 68 "free": false, 69 "relationships": { 70 "service_offering": { 71 "data": { 72 "guid": "69d428b9-75b4-44db-addf-19c85c7f0f1e" 73 } 74 } 75 } 76 } 77 ] 78 }`, 79 server.URL()) 80 81 response2 := ` 82 { 83 "pagination": { 84 "next": { 85 "href": null 86 } 87 }, 88 "resources": [ 89 { 90 "guid": "service-plan-3-guid", 91 "name": "service-plan-3-name", 92 "visibility_type": "organization", 93 "description": "service-plan-3-description", 94 "available": true, 95 "free": true, 96 "relationships": { 97 "service_offering": { 98 "data": { 99 "guid": "59d428b9-75b4-44db-addf-19c85c7f0f1e" 100 } 101 } 102 } 103 } 104 ] 105 }` 106 107 server.AppendHandlers( 108 CombineHandlers( 109 VerifyRequest(http.MethodGet, "/v3/service_plans", "names=myServicePlan&service_broker_names=myServiceBroker&service_offering_names=someOffering"), 110 RespondWith(http.StatusOK, response1, http.Header{"X-Cf-Warnings": {"warning-1"}}), 111 ), 112 CombineHandlers( 113 VerifyRequest(http.MethodGet, "/v3/service_plans", "names=myServicePlan&service_broker_names=myServiceBroker&service_offering_names=someOffering&page=2"), 114 RespondWith(http.StatusOK, response2, http.Header{"X-Cf-Warnings": {"warning-2"}}), 115 ), 116 ) 117 118 query = []Query{ 119 { 120 Key: NameFilter, 121 Values: []string{"myServicePlan"}, 122 }, 123 { 124 Key: ServiceBrokerNamesFilter, 125 Values: []string{"myServiceBroker"}, 126 }, 127 { 128 Key: ServiceOfferingNamesFilter, 129 Values: []string{"someOffering"}, 130 }, 131 } 132 }) 133 134 It("returns a list of service plans with their associated warnings", func() { 135 Expect(executeErr).ToNot(HaveOccurred()) 136 137 Expect(plans).To(ConsistOf( 138 resources.ServicePlan{ 139 GUID: "service-plan-1-guid", 140 Name: "service-plan-1-name", 141 Description: "service-plan-1-description", 142 Available: true, 143 Free: true, 144 VisibilityType: "public", 145 ServiceOfferingGUID: "79d428b9-75b4-44db-addf-19c85c7f0f1e", 146 }, 147 resources.ServicePlan{ 148 GUID: "service-plan-2-guid", 149 Name: "service-plan-2-name", 150 Description: "service-plan-2-description", 151 Available: false, 152 Free: false, 153 VisibilityType: "admin", 154 ServiceOfferingGUID: "69d428b9-75b4-44db-addf-19c85c7f0f1e", 155 }, 156 resources.ServicePlan{ 157 GUID: "service-plan-3-guid", 158 Name: "service-plan-3-name", 159 Description: "service-plan-3-description", 160 Available: true, 161 Free: true, 162 VisibilityType: "organization", 163 ServiceOfferingGUID: "59d428b9-75b4-44db-addf-19c85c7f0f1e", 164 }, 165 )) 166 Expect(warnings).To(ConsistOf("warning-1", "warning-2")) 167 }) 168 }) 169 170 When("the cloud controller returns errors and warnings", func() { 171 BeforeEach(func() { 172 response := `{ 173 "errors": [ 174 { 175 "code": 42424, 176 "detail": "Some detailed error message", 177 "title": "CF-SomeErrorTitle" 178 }, 179 { 180 "code": 11111, 181 "detail": "Some other detailed error message", 182 "title": "CF-SomeOtherErrorTitle" 183 } 184 ] 185 }` 186 server.AppendHandlers( 187 CombineHandlers( 188 VerifyRequest(http.MethodGet, "/v3/service_plans"), 189 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 190 ), 191 ) 192 }) 193 194 It("returns the error and all warnings", func() { 195 Expect(executeErr).To(MatchError(ccerror.MultiError{ 196 ResponseCode: http.StatusTeapot, 197 Errors: []ccerror.V3Error{ 198 { 199 Code: 42424, 200 Detail: "Some detailed error message", 201 Title: "CF-SomeErrorTitle", 202 }, 203 { 204 Code: 11111, 205 Detail: "Some other detailed error message", 206 Title: "CF-SomeOtherErrorTitle", 207 }, 208 }, 209 })) 210 Expect(warnings).To(ConsistOf("this is a warning")) 211 }) 212 }) 213 }) 214 215 Describe("GetServicePlansWithSpaceAndOrganization", func() { 216 var ( 217 plans []ServicePlanWithSpaceAndOrganization 218 warnings Warnings 219 executeErr error 220 ) 221 222 JustBeforeEach(func() { 223 plans, warnings, executeErr = client.GetServicePlansWithSpaceAndOrganization(query...) 224 }) 225 226 When("when the query succeeds", func() { 227 BeforeEach(func() { 228 response1 := fmt.Sprintf(` 229 { 230 "pagination": { 231 "next": { 232 "href": "%s/v3/service_plans?include=space.organization&service_offering_names=someOffering&page=2" 233 } 234 }, 235 "resources": [ 236 { 237 "guid": "service-plan-1-guid", 238 "name": "service-plan-1-name", 239 "visibility_type": "public", 240 "relationships": { 241 "service_offering": { 242 "data": { 243 "guid": "79d428b9-75b4-44db-addf-19c85c7f0f1e" 244 } 245 } 246 } 247 }, 248 { 249 "guid": "service-plan-2-guid", 250 "name": "service-plan-2-name", 251 "visibility_type": "space", 252 "relationships": { 253 "service_offering": { 254 "data": { 255 "guid": "69d428b9-75b4-44db-addf-19c85c7f0f1e" 256 } 257 }, 258 "space": { 259 "data": { 260 "guid": "fake-space-guid" 261 } 262 } 263 } 264 } 265 ], 266 "included": { 267 "spaces": [ 268 { 269 "name": "matching-space", 270 "guid": "fake-space-guid", 271 "relationships": { 272 "organization": { 273 "data": { 274 "guid": "matching-org-guid" 275 } 276 } 277 } 278 }, 279 { 280 "name": "non-matching-space", 281 "guid": "fake-other-space-guid", 282 "relationships": { 283 "organization": { 284 "data": { 285 "guid": "other-org-guid" 286 } 287 } 288 } 289 } 290 ], 291 "organizations": [ 292 { 293 "name": "matching-org", 294 "guid": "matching-org-guid" 295 }, 296 { 297 "name": "non-matching-org", 298 "guid": "other-org-guid" 299 } 300 ] 301 } 302 }`, 303 server.URL()) 304 305 response2 := ` 306 { 307 "pagination": { 308 "next": { 309 "href": null 310 } 311 }, 312 "resources": [ 313 { 314 "guid": "service-plan-3-guid", 315 "name": "service-plan-3-name", 316 "visibility_type": "organization", 317 "relationships": { 318 "service_offering": { 319 "data": { 320 "guid": "59d428b9-75b4-44db-addf-19c85c7f0f1e" 321 } 322 } 323 } 324 } 325 ] 326 }` 327 328 server.AppendHandlers( 329 CombineHandlers( 330 VerifyRequest(http.MethodGet, "/v3/service_plans", "include=space.organization&service_offering_names=someOffering"), 331 RespondWith(http.StatusOK, response1, http.Header{"X-Cf-Warnings": {"warning-1"}}), 332 ), 333 CombineHandlers( 334 VerifyRequest(http.MethodGet, "/v3/service_plans", "include=space.organization&service_offering_names=someOffering&page=2"), 335 RespondWith(http.StatusOK, response2, http.Header{"X-Cf-Warnings": {"warning-2"}}), 336 ), 337 ) 338 339 query = []Query{ 340 { 341 Key: ServiceOfferingNamesFilter, 342 Values: []string{"someOffering"}, 343 }, 344 } 345 }) 346 347 It("returns space and org name for space-scoped plans", func() { 348 Expect(executeErr).ToNot(HaveOccurred()) 349 350 Expect(plans).To(ConsistOf( 351 ServicePlanWithSpaceAndOrganization{ 352 GUID: "service-plan-1-guid", 353 Name: "service-plan-1-name", 354 VisibilityType: "public", 355 ServiceOfferingGUID: "79d428b9-75b4-44db-addf-19c85c7f0f1e", 356 }, 357 ServicePlanWithSpaceAndOrganization{ 358 GUID: "service-plan-2-guid", 359 Name: "service-plan-2-name", 360 VisibilityType: "space", 361 ServiceOfferingGUID: "69d428b9-75b4-44db-addf-19c85c7f0f1e", 362 SpaceGUID: "fake-space-guid", 363 SpaceName: "matching-space", 364 OrganizationName: "matching-org", 365 }, 366 ServicePlanWithSpaceAndOrganization{ 367 GUID: "service-plan-3-guid", 368 Name: "service-plan-3-name", 369 VisibilityType: "organization", 370 ServiceOfferingGUID: "59d428b9-75b4-44db-addf-19c85c7f0f1e", 371 }, 372 )) 373 Expect(warnings).To(ConsistOf("warning-1", "warning-2")) 374 }) 375 }) 376 377 When("the query fails", func() { 378 BeforeEach(func() { 379 response := `{ 380 "errors": [ 381 { 382 "code": 42424, 383 "detail": "Some detailed error message", 384 "title": "CF-SomeErrorTitle" 385 }, 386 { 387 "code": 11111, 388 "detail": "Some other detailed error message", 389 "title": "CF-SomeOtherErrorTitle" 390 } 391 ] 392 }` 393 server.AppendHandlers( 394 CombineHandlers( 395 VerifyRequest(http.MethodGet, "/v3/service_plans", "include=space.organization"), 396 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 397 ), 398 ) 399 }) 400 401 It("returns the error and all warnings", func() { 402 Expect(executeErr).To(MatchError(ccerror.MultiError{ 403 ResponseCode: http.StatusTeapot, 404 Errors: []ccerror.V3Error{ 405 { 406 Code: 42424, 407 Detail: "Some detailed error message", 408 Title: "CF-SomeErrorTitle", 409 }, 410 { 411 Code: 11111, 412 Detail: "Some other detailed error message", 413 Title: "CF-SomeOtherErrorTitle", 414 }, 415 }, 416 })) 417 Expect(warnings).To(ConsistOf("this is a warning")) 418 }) 419 }) 420 }) 421 422 Describe("GetServicePlansWithOfferings", func() { 423 var ( 424 offerings []ServiceOfferingWithPlans 425 warnings Warnings 426 executeErr error 427 ) 428 429 JustBeforeEach(func() { 430 offerings, warnings, executeErr = client.GetServicePlansWithOfferings(query...) 431 }) 432 433 When("when the query succeeds", func() { 434 BeforeEach(func() { 435 436 response1Template := ` 437 { 438 "pagination": { 439 "next": { 440 "href": "%s/v3/service_plans?include=service_offering&space_guids=some-space-guid&organization_guids=some-org-guid&fields[service_offering.service_broker]=name,guid&page=2" 441 } 442 }, 443 "resources": [ 444 { 445 "guid": "service-plan-1-guid", 446 "name": "service-plan-1-name", 447 "description": "service-plan-1-description", 448 "available": true, 449 "free": true, 450 "relationships": { 451 "service_offering": { 452 "data": { 453 "guid": "79d428b9-75b4-44db-addf-19c85c7f0f1e" 454 } 455 } 456 } 457 }, 458 { 459 "guid": "service-plan-2-guid", 460 "name": "service-plan-2-name", 461 "description": "service-plan-2-description", 462 "available": false, 463 "free": false, 464 "costs": [ 465 { 466 "amount": 649.00, 467 "currency": "USD", 468 "unit": "MONTHLY" 469 }, 470 { 471 "amount": 649.123, 472 "currency": "GBP", 473 "unit": "MONTHLY" 474 } 475 ], 476 "broker_catalog": { 477 "metadata": { 478 "costs":[ 479 { 480 "amount": { 481 "usd": 649.0, 482 "gpb": 649.0 483 }, 484 "unit": "MONTHLY" 485 } 486 ] 487 } 488 }, 489 "relationships": { 490 "service_offering": { 491 "data": { 492 "guid": "69d428b9-75b4-44db-addf-19c85c7f0f1e" 493 } 494 } 495 } 496 } 497 ], 498 "included": { 499 "service_offerings": [ 500 { 501 "name": "service-offering-1", 502 "guid": "79d428b9-75b4-44db-addf-19c85c7f0f1e", 503 "description": "something about service offering 1", 504 "relationships": { 505 "service_broker": { 506 "data": { 507 "guid": "service-broker-1-guid" 508 } 509 } 510 } 511 }, 512 { 513 "name": "service-offering-2", 514 "guid": "69d428b9-75b4-44db-addf-19c85c7f0f1e", 515 "description": "something about service offering 2", 516 "relationships": { 517 "service_broker": { 518 "data": { 519 "guid": "service-broker-2-guid" 520 } 521 } 522 } 523 } 524 ], 525 "service_brokers": [ 526 { 527 "name": "service-broker-1", 528 "guid": "service-broker-1-guid" 529 }, 530 { 531 "name": "service-broker-2", 532 "guid": "service-broker-2-guid" 533 } 534 ] 535 } 536 }` 537 538 response1 := fmt.Sprintf(response1Template, server.URL()) 539 response2 := ` 540 { 541 "pagination": { 542 "next": { 543 "href": null 544 } 545 }, 546 "resources": [ 547 { 548 "guid": "service-plan-3-guid", 549 "name": "service-plan-3-name", 550 "description": "service-plan-3-description", 551 "available": true, 552 "free": true, 553 "relationships": { 554 "service_offering": { 555 "data": { 556 "guid": "79d428b9-75b4-44db-addf-19c85c7f0f1e" 557 } 558 } 559 } 560 } 561 ], 562 "included": { 563 "service_offerings": [ 564 { 565 "name": "service-offering-1", 566 "guid": "79d428b9-75b4-44db-addf-19c85c7f0f1e", 567 "description": "something about service offering 1", 568 "relationships": { 569 "service_broker": { 570 "data": { 571 "guid": "service-broker-1-guid" 572 } 573 } 574 } 575 } 576 ], 577 "service_brokers": [ 578 { 579 "name": "service-broker-1", 580 "guid": "service-broker-1-guid" 581 } 582 ] 583 } 584 }` 585 586 server.AppendHandlers( 587 CombineHandlers( 588 VerifyRequest(http.MethodGet, "/v3/service_plans", "include=service_offering&space_guids=some-space-guid&organization_guids=some-org-guid&fields[service_offering.service_broker]=name,guid"), 589 RespondWith(http.StatusOK, response1, http.Header{"X-Cf-Warnings": {"warning-1"}}), 590 ), 591 CombineHandlers( 592 VerifyRequest(http.MethodGet, "/v3/service_plans", "include=service_offering&space_guids=some-space-guid&organization_guids=some-org-guid&fields[service_offering.service_broker]=name,guid&page=2"), 593 RespondWith(http.StatusOK, response2, http.Header{"X-Cf-Warnings": {"warning-2"}}), 594 ), 595 ) 596 597 query = []Query{ 598 { 599 Key: OrganizationGUIDFilter, 600 Values: []string{"some-org-guid"}, 601 }, 602 { 603 Key: SpaceGUIDFilter, 604 Values: []string{"some-space-guid"}, 605 }, 606 } 607 }) 608 609 It("returns service offerings and service plans", func() { 610 Expect(executeErr).NotTo(HaveOccurred()) 611 Expect(warnings).To(ConsistOf("warning-1", "warning-2")) 612 Expect(offerings).To(Equal([]ServiceOfferingWithPlans{ 613 { 614 GUID: "79d428b9-75b4-44db-addf-19c85c7f0f1e", 615 Name: "service-offering-1", 616 Description: "something about service offering 1", 617 ServiceBrokerName: "service-broker-1", 618 Plans: []resources.ServicePlan{ 619 { 620 GUID: "service-plan-1-guid", 621 Name: "service-plan-1-name", 622 Description: "service-plan-1-description", 623 Available: true, 624 Free: true, 625 ServiceOfferingGUID: "79d428b9-75b4-44db-addf-19c85c7f0f1e", 626 }, 627 { 628 GUID: "service-plan-3-guid", 629 Name: "service-plan-3-name", 630 Description: "service-plan-3-description", 631 Available: true, 632 Free: true, 633 ServiceOfferingGUID: "79d428b9-75b4-44db-addf-19c85c7f0f1e", 634 }, 635 }, 636 }, 637 { 638 GUID: "69d428b9-75b4-44db-addf-19c85c7f0f1e", 639 Name: "service-offering-2", 640 Description: "something about service offering 2", 641 ServiceBrokerName: "service-broker-2", 642 Plans: []resources.ServicePlan{ 643 { 644 GUID: "service-plan-2-guid", 645 Name: "service-plan-2-name", 646 Available: false, 647 Description: "service-plan-2-description", 648 Free: false, 649 Costs: []resources.ServicePlanCost{ 650 { 651 Amount: 649.00, 652 Currency: "USD", 653 Unit: "MONTHLY", 654 }, 655 { 656 Amount: 649.123, 657 Currency: "GBP", 658 Unit: "MONTHLY", 659 }, 660 }, 661 ServiceOfferingGUID: "69d428b9-75b4-44db-addf-19c85c7f0f1e", 662 }, 663 }, 664 }, 665 })) 666 }) 667 }) 668 669 When("the query fails", func() { 670 BeforeEach(func() { 671 response := `{ 672 "errors": [ 673 { 674 "code": 42424, 675 "detail": "Some detailed error message", 676 "title": "CF-SomeErrorTitle" 677 }, 678 { 679 "code": 11111, 680 "detail": "Some other detailed error message", 681 "title": "CF-SomeOtherErrorTitle" 682 } 683 ] 684 }` 685 server.AppendHandlers( 686 CombineHandlers( 687 VerifyRequest(http.MethodGet, "/v3/service_plans", "include=service_offering&fields[service_offering.service_broker]=name,guid"), 688 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 689 ), 690 ) 691 }) 692 693 It("returns the error and all warnings", func() { 694 Expect(executeErr).To(MatchError(ccerror.MultiError{ 695 ResponseCode: http.StatusTeapot, 696 Errors: []ccerror.V3Error{ 697 { 698 Code: 42424, 699 Detail: "Some detailed error message", 700 Title: "CF-SomeErrorTitle", 701 }, 702 { 703 Code: 11111, 704 Detail: "Some other detailed error message", 705 Title: "CF-SomeOtherErrorTitle", 706 }, 707 }, 708 })) 709 Expect(warnings).To(ConsistOf("this is a warning")) 710 }) 711 }) 712 713 }) 714 })