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