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