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