github.com/franc20/ayesa_sap@v7.0.0-beta.28.0.20200124003224-302d4d52fa6c+incompatible/api/cloudcontroller/ccv3/service_broker_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/types" 10 . "github.com/onsi/ginkgo" 11 . "github.com/onsi/gomega" 12 . "github.com/onsi/gomega/ghttp" 13 ) 14 15 var _ = Describe("ServiceBroker", func() { 16 var client *Client 17 18 BeforeEach(func() { 19 client, _ = NewTestClient() 20 }) 21 22 Describe("GetServiceBrokers", func() { 23 var ( 24 query []Query 25 serviceBrokers []ServiceBroker 26 warnings Warnings 27 executeErr error 28 ) 29 30 JustBeforeEach(func() { 31 serviceBrokers, warnings, executeErr = client.GetServiceBrokers(query...) 32 }) 33 34 When("there are no service brokers", func() { 35 BeforeEach(func() { 36 response := ` 37 { 38 "pagination": { 39 "next": null 40 }, 41 "resources": [] 42 }` 43 44 server.AppendHandlers( 45 CombineHandlers( 46 VerifyRequest(http.MethodGet, "/v3/service_brokers"), 47 RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 48 ), 49 ) 50 }) 51 52 It("returns an empty list", func() { 53 Expect(executeErr).NotTo(HaveOccurred()) 54 Expect(serviceBrokers).To(HaveLen(0)) 55 Expect(warnings).To(ConsistOf("this is a warning")) 56 }) 57 }) 58 59 When("there is a service broker", func() { 60 BeforeEach(func() { 61 response := ` 62 { 63 "pagination": { 64 "next": null 65 }, 66 "resources": [ 67 { 68 "name": "service-broker-name-1", 69 "guid": "service-broker-guid-1", 70 "url": "service-broker-url-1", 71 "status": "synchronization in progress" 72 } 73 ] 74 }` 75 76 server.AppendHandlers( 77 CombineHandlers( 78 VerifyRequest(http.MethodGet, "/v3/service_brokers"), 79 RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is another warning"}}), 80 ), 81 ) 82 }) 83 84 It("returns the service broker", func() { 85 Expect(executeErr).NotTo(HaveOccurred()) 86 Expect(serviceBrokers).To(ConsistOf(ServiceBroker{ 87 Name: "service-broker-name-1", 88 GUID: "service-broker-guid-1", 89 URL: "service-broker-url-1", 90 Status: "synchronization in progress", 91 Metadata: nil, 92 })) 93 Expect(warnings).To(ConsistOf("this is another warning")) 94 }) 95 }) 96 97 When("the service broker has labels", func() { 98 BeforeEach(func() { 99 response := ` 100 { 101 "pagination": { 102 "next": null 103 }, 104 "resources": [ 105 { 106 "name": "service-broker-name-1", 107 "guid": "service-broker-guid-1", 108 "url": "service-broker-url-1", 109 "status": "synchronization in progress", 110 "metadata": { 111 "labels": { 112 "some-key":"some-value", 113 "other-key":"other-value" 114 } 115 } 116 } 117 ] 118 }` 119 120 server.AppendHandlers( 121 CombineHandlers( 122 VerifyRequest(http.MethodGet, "/v3/service_brokers"), 123 RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is another warning"}}), 124 ), 125 ) 126 }) 127 128 It("returns the service broker with the labels in Metadata", func() { 129 Expect(executeErr).NotTo(HaveOccurred()) 130 Expect(serviceBrokers).To(ConsistOf(ServiceBroker{ 131 Name: "service-broker-name-1", 132 GUID: "service-broker-guid-1", 133 URL: "service-broker-url-1", 134 Status: "synchronization in progress", 135 Metadata: &Metadata{ 136 Labels: map[string]types.NullString{ 137 "some-key": types.NewNullString("some-value"), 138 "other-key": types.NewNullString("other-value"), 139 }, 140 }, 141 })) 142 }) 143 }) 144 145 When("there is more than one page of service brokers", func() { 146 BeforeEach(func() { 147 response1 := fmt.Sprintf(` 148 { 149 "pagination": { 150 "next": { 151 "href": "%s/v3/service_brokers?page=2&per_page=2" 152 } 153 }, 154 "resources": [ 155 { 156 "name": "service-broker-name-1", 157 "guid": "service-broker-guid-1", 158 "url": "service-broker-url-1", 159 "status": "synchronization in progress", 160 "relationships": {} 161 }, 162 { 163 "name": "service-broker-name-2", 164 "guid": "service-broker-guid-2", 165 "url": "service-broker-url-2", 166 "status": "synchronization failed", 167 "relationships": {} 168 } 169 ] 170 }`, server.URL()) 171 172 response2 := ` 173 { 174 "pagination": { 175 "next": null 176 }, 177 "resources": [ 178 { 179 "name": "service-broker-name-3", 180 "guid": "service-broker-guid-3", 181 "url": "service-broker-url-3", 182 "status": "available", 183 "relationships": {} 184 } 185 ] 186 }` 187 188 server.AppendHandlers( 189 CombineHandlers( 190 VerifyRequest(http.MethodGet, "/v3/service_brokers"), 191 RespondWith(http.StatusOK, response1, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 192 ), 193 ) 194 server.AppendHandlers( 195 CombineHandlers( 196 VerifyRequest(http.MethodGet, "/v3/service_brokers", "page=2&per_page=2"), 197 RespondWith(http.StatusOK, response2, http.Header{"X-Cf-Warnings": {"this is another warning"}}), 198 ), 199 ) 200 }) 201 202 It("returns the queried service-broker and all warnings", func() { 203 Expect(executeErr).NotTo(HaveOccurred()) 204 205 Expect(serviceBrokers).To(ConsistOf( 206 ServiceBroker{Name: "service-broker-name-1", GUID: "service-broker-guid-1", URL: "service-broker-url-1", Status: "synchronization in progress"}, 207 ServiceBroker{Name: "service-broker-name-2", GUID: "service-broker-guid-2", URL: "service-broker-url-2", Status: "synchronization failed"}, 208 ServiceBroker{Name: "service-broker-name-3", GUID: "service-broker-guid-3", URL: "service-broker-url-3", Status: "available"}, 209 )) 210 Expect(warnings).To(ConsistOf("this is a warning", "this is another warning")) 211 }) 212 }) 213 214 When("a filter is specified", func() { 215 BeforeEach(func() { 216 query = []Query{ 217 { 218 Key: NameFilter, 219 Values: []string{"special-unicorn-broker"}, 220 }, 221 } 222 223 response := ` 224 { 225 "pagination": { 226 "next": null 227 }, 228 "resources": [ 229 { 230 "name": "special-unicorn-broker", 231 "guid": "service-broker-guid-1", 232 "url": "service-broker-url-1", 233 "status": "synchronization in progress" 234 } 235 ] 236 }` 237 238 server.AppendHandlers( 239 CombineHandlers( 240 VerifyRequest(http.MethodGet, "/v3/service_brokers", "names=special-unicorn-broker"), 241 RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is another warning"}}), 242 ), 243 ) 244 }) 245 246 It("passes the filter in the query and returns the result", func() { 247 Expect(executeErr).NotTo(HaveOccurred()) 248 Expect(serviceBrokers[0].Name).To(Equal("special-unicorn-broker")) 249 Expect(warnings).To(ConsistOf("this is another warning")) 250 }) 251 }) 252 253 When("the cloud controller returns errors and warnings", func() { 254 BeforeEach(func() { 255 response := `{ 256 "errors": [ 257 { 258 "code": 10008, 259 "detail": "The request is semantically invalid: command presence", 260 "title": "CF-UnprocessableEntity" 261 }, 262 { 263 "code": 10010, 264 "detail": "Isolation segment not found", 265 "title": "CF-ResourceNotFound" 266 } 267 ] 268 }` 269 server.AppendHandlers( 270 CombineHandlers( 271 VerifyRequest(http.MethodGet, "/v3/service_brokers"), 272 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 273 ), 274 ) 275 }) 276 277 It("returns the error and all warnings", func() { 278 Expect(executeErr).To(MatchError(ccerror.MultiError{ 279 ResponseCode: http.StatusTeapot, 280 Errors: []ccerror.V3Error{ 281 { 282 Code: 10008, 283 Detail: "The request is semantically invalid: command presence", 284 Title: "CF-UnprocessableEntity", 285 }, 286 { 287 Code: 10010, 288 Detail: "Isolation segment not found", 289 Title: "CF-ResourceNotFound", 290 }, 291 }, 292 })) 293 Expect(warnings).To(ConsistOf("this is a warning")) 294 }) 295 }) 296 }) 297 298 Describe("DeleteServiceBroker", func() { 299 var ( 300 warnings Warnings 301 executeErr error 302 serviceBrokerGUID string 303 jobURL JobURL 304 ) 305 306 BeforeEach(func() { 307 serviceBrokerGUID = "some-service-broker-guid" 308 }) 309 310 JustBeforeEach(func() { 311 jobURL, warnings, executeErr = client.DeleteServiceBroker(serviceBrokerGUID) 312 }) 313 314 When("the Cloud Controller successfully deletes the broker", func() { 315 BeforeEach(func() { 316 server.AppendHandlers( 317 CombineHandlers( 318 VerifyRequest(http.MethodDelete, "/v3/service_brokers/some-service-broker-guid"), 319 RespondWith(http.StatusOK, "", http.Header{ 320 "X-Cf-Warnings": {"this is a warning"}, 321 "Location": {"some-job-url"}, 322 }), 323 ), 324 ) 325 }) 326 327 It("succeeds and returns warnings", func() { 328 Expect(executeErr).NotTo(HaveOccurred()) 329 Expect(warnings).To(ConsistOf("this is a warning")) 330 Expect(jobURL).To(Equal(JobURL("some-job-url"))) 331 }) 332 }) 333 334 When("the broker is space scoped", func() { 335 BeforeEach(func() { 336 server.AppendHandlers( 337 CombineHandlers( 338 VerifyRequest(http.MethodDelete, "/v3/service_brokers/some-service-broker-guid"), 339 RespondWith(http.StatusOK, "", http.Header{"X-Cf-Warnings": {"this is a warning"}}), 340 ), 341 ) 342 }) 343 344 It("succeeds and returns warnings", func() { 345 Expect(executeErr).NotTo(HaveOccurred()) 346 Expect(warnings).To(ConsistOf("this is a warning")) 347 }) 348 }) 349 350 When("the Cloud Controller fails to delete the broker", func() { 351 BeforeEach(func() { 352 response := `{ 353 "errors": [ 354 { 355 "code": 10008, 356 "detail": "The request is semantically invalid: command presence", 357 "title": "CF-UnprocessableEntity" 358 }, 359 { 360 "code": 10010, 361 "detail": "Service broker not found", 362 "title": "CF-ResourceNotFound" 363 } 364 ] 365 }` 366 367 server.AppendHandlers( 368 CombineHandlers( 369 VerifyRequest(http.MethodDelete, "/v3/service_brokers/some-service-broker-guid"), 370 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 371 ), 372 ) 373 }) 374 375 It("returns parsed errors and warnings", func() { 376 Expect(executeErr).To(MatchError(ccerror.MultiError{ 377 ResponseCode: http.StatusTeapot, 378 Errors: []ccerror.V3Error{ 379 { 380 Code: 10008, 381 Detail: "The request is semantically invalid: command presence", 382 Title: "CF-UnprocessableEntity", 383 }, 384 { 385 Code: 10010, 386 Detail: "Service broker not found", 387 Title: "CF-ResourceNotFound", 388 }, 389 }, 390 })) 391 Expect(warnings).To(ConsistOf("this is a warning")) 392 }) 393 }) 394 }) 395 396 Describe("CreateServiceBroker", func() { 397 const ( 398 name = "name" 399 url = "url" 400 username = "username" 401 password = "password" 402 ) 403 404 var ( 405 jobURL JobURL 406 warnings Warnings 407 executeErr error 408 spaceGUID string 409 expectedBody map[string]interface{} 410 ) 411 412 BeforeEach(func() { 413 spaceGUID = "" 414 expectedBody = map[string]interface{}{ 415 "name": "name", 416 "url": "url", 417 "authentication": map[string]interface{}{ 418 "type": "basic", 419 "credentials": map[string]string{ 420 "username": "username", 421 "password": "password", 422 }, 423 }, 424 } 425 }) 426 427 JustBeforeEach(func() { 428 serviceBrokerRequest := ServiceBrokerModel{ 429 Name: name, 430 URL: url, 431 Username: username, 432 Password: password, 433 SpaceGUID: spaceGUID, 434 } 435 jobURL, warnings, executeErr = client.CreateServiceBroker(serviceBrokerRequest) 436 }) 437 438 When("the Cloud Controller successfully creates the broker", func() { 439 BeforeEach(func() { 440 server.AppendHandlers( 441 CombineHandlers( 442 VerifyRequest(http.MethodPost, "/v3/service_brokers"), 443 VerifyJSONRepresenting(expectedBody), 444 RespondWith(http.StatusOK, "", http.Header{ 445 "X-Cf-Warnings": {"this is a warning"}, 446 "Location": {"some-job-url"}, 447 }), 448 ), 449 ) 450 }) 451 452 It("succeeds, returns warnings and job URL", func() { 453 Expect(jobURL).To(Equal(JobURL("some-job-url"))) 454 Expect(executeErr).NotTo(HaveOccurred()) 455 Expect(warnings).To(ConsistOf("this is a warning")) 456 }) 457 }) 458 459 When("the broker is space scoped", func() { 460 BeforeEach(func() { 461 spaceGUID = "space-guid" 462 expectedBody["relationships"] = map[string]interface{}{ 463 "space": map[string]interface{}{ 464 "data": map[string]string{ 465 "guid": "space-guid", 466 }, 467 }, 468 } 469 server.AppendHandlers( 470 CombineHandlers( 471 VerifyRequest(http.MethodPost, "/v3/service_brokers"), 472 VerifyJSONRepresenting(expectedBody), 473 RespondWith(http.StatusOK, "", http.Header{"X-Cf-Warnings": {"this is a warning"}}), 474 ), 475 ) 476 }) 477 478 It("succeeds and returns warnings", func() { 479 Expect(executeErr).NotTo(HaveOccurred()) 480 Expect(warnings).To(ConsistOf("this is a warning")) 481 }) 482 }) 483 484 When("the Cloud Controller fails to create the broker", func() { 485 BeforeEach(func() { 486 response := `{ 487 "errors": [ 488 { 489 "code": 10008, 490 "detail": "The request is semantically invalid: command presence", 491 "title": "CF-UnprocessableEntity" 492 }, 493 { 494 "code": 10010, 495 "detail": "Isolation segment not found", 496 "title": "CF-ResourceNotFound" 497 } 498 ] 499 }` 500 501 server.AppendHandlers( 502 CombineHandlers( 503 VerifyRequest(http.MethodPost, "/v3/service_brokers"), 504 VerifyJSONRepresenting(expectedBody), 505 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 506 ), 507 ) 508 }) 509 510 It("returns parsed errors and warnings", func() { 511 Expect(executeErr).To(MatchError(ccerror.MultiError{ 512 ResponseCode: http.StatusTeapot, 513 Errors: []ccerror.V3Error{ 514 { 515 Code: 10008, 516 Detail: "The request is semantically invalid: command presence", 517 Title: "CF-UnprocessableEntity", 518 }, 519 { 520 Code: 10010, 521 Detail: "Isolation segment not found", 522 Title: "CF-ResourceNotFound", 523 }, 524 }, 525 })) 526 Expect(warnings).To(ConsistOf("this is a warning")) 527 }) 528 }) 529 }) 530 531 Describe("UpdateServiceBroker", func() { 532 var ( 533 name, guid, url, username, password string 534 jobURL JobURL 535 warnings Warnings 536 executeErr error 537 expectedBody map[string]interface{} 538 ) 539 540 BeforeEach(func() { 541 expectedBody = map[string]interface{}{ 542 "url": "new-url", 543 "authentication": map[string]interface{}{ 544 "type": "basic", 545 "credentials": map[string]string{ 546 "username": "new-username", 547 "password": "new-password", 548 }, 549 }, 550 } 551 name = "" 552 guid = "broker-guid" 553 url = "new-url" 554 username = "new-username" 555 password = "new-password" 556 }) 557 558 When("the Cloud Controller successfully updates the broker", func() { 559 BeforeEach(func() { 560 server.AppendHandlers( 561 CombineHandlers( 562 VerifyRequest(http.MethodPatch, "/v3/service_brokers/"+guid), 563 VerifyJSONRepresenting(expectedBody), 564 RespondWith(http.StatusOK, "", http.Header{ 565 "X-Cf-Warnings": {"this is a warning"}, 566 "Location": {"some-job-url"}, 567 }), 568 ), 569 ) 570 }) 571 572 It("succeeds, returns warnings and job URL", func() { 573 jobURL, warnings, executeErr = client.UpdateServiceBroker( 574 guid, 575 ServiceBrokerModel{ 576 Name: name, 577 URL: url, 578 Username: username, 579 Password: password, 580 }) 581 582 Expect(jobURL).To(Equal(JobURL("some-job-url"))) 583 Expect(executeErr).NotTo(HaveOccurred()) 584 Expect(warnings).To(ConsistOf("this is a warning")) 585 }) 586 }) 587 588 When("the Cloud Controller fails to update the broker", func() { 589 BeforeEach(func() { 590 response := `{ 591 "errors": [ 592 { 593 "code": 10008, 594 "detail": "The request is semantically invalid: command presence", 595 "title": "CF-UnprocessableEntity" 596 }, 597 { 598 "code": 10010, 599 "detail": "Isolation segment not found", 600 "title": "CF-ResourceNotFound" 601 } 602 ] 603 }` 604 605 server.AppendHandlers( 606 CombineHandlers( 607 VerifyRequest(http.MethodPatch, "/v3/service_brokers/"+guid), 608 VerifyJSONRepresenting(expectedBody), 609 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 610 ), 611 ) 612 }) 613 614 It("returns parsed errors and warnings", func() { 615 jobURL, warnings, executeErr = client.UpdateServiceBroker(guid, 616 ServiceBrokerModel{ 617 Name: name, 618 URL: url, 619 Username: username, 620 Password: password, 621 }) 622 623 Expect(executeErr).To(MatchError(ccerror.MultiError{ 624 ResponseCode: http.StatusTeapot, 625 Errors: []ccerror.V3Error{ 626 { 627 Code: 10008, 628 Detail: "The request is semantically invalid: command presence", 629 Title: "CF-UnprocessableEntity", 630 }, 631 { 632 Code: 10010, 633 Detail: "Isolation segment not found", 634 Title: "CF-ResourceNotFound", 635 }, 636 }, 637 })) 638 Expect(warnings).To(ConsistOf("this is a warning")) 639 }) 640 }) 641 642 When("only name is provided", func() { 643 BeforeEach(func() { 644 name = "some-name" 645 username = "" 646 password = "" 647 url = "" 648 649 expectedBody = map[string]interface{}{ 650 "name": name, 651 } 652 653 server.AppendHandlers( 654 CombineHandlers( 655 VerifyRequest(http.MethodPatch, "/v3/service_brokers/"+guid), 656 VerifyJSONRepresenting(expectedBody), 657 RespondWith(http.StatusOK, "", http.Header{ 658 "X-Cf-Warnings": {"this is a warning"}, 659 "Location": {"some-job-url"}, 660 }), 661 ), 662 ) 663 }) 664 665 It("includes only the name in the request body", func() { 666 jobURL, warnings, executeErr = client.UpdateServiceBroker( 667 guid, 668 ServiceBrokerModel{ 669 Name: name, 670 }) 671 Expect(executeErr).NotTo(HaveOccurred()) 672 }) 673 }) 674 675 When("partial authentication credentials are provided", func() { 676 It("errors without sending any request", func() { 677 _, _, executeErr = client.UpdateServiceBroker( 678 guid, 679 ServiceBrokerModel{Password: password}, 680 ) 681 Expect(executeErr).To(HaveOccurred()) 682 683 _, _, executeErr = client.UpdateServiceBroker( 684 guid, 685 ServiceBrokerModel{Username: username}, 686 ) 687 Expect(executeErr).To(HaveOccurred()) 688 Expect(executeErr).To(MatchError("Incorrect usage: both username and password must be defined in order to do an update")) 689 }) 690 }) 691 }) 692 })