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