github.com/LukasHeimann/cloudfoundrycli@v7.1.0+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/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  				}
   454  
   455  				server.AppendHandlers(
   456  					CombineHandlers(
   457  						VerifyRequest(http.MethodPost, "/v3/organizations"),
   458  						VerifyJSONRepresenting(expectedBody),
   459  						RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   460  					),
   461  				)
   462  			})
   463  
   464  			It("returns the created org", func() {
   465  				Expect(executeErr).ToNot(HaveOccurred())
   466  				Expect(warnings).To(ConsistOf("this is a warning"))
   467  				Expect(createdOrg).To(Equal(Organization{
   468  					GUID: "some-org-guid",
   469  					Name: "some-org-name",
   470  				}))
   471  			})
   472  		})
   473  
   474  		When("an organization with the same name already exists", func() {
   475  			BeforeEach(func() {
   476  				response := `{
   477  					 "errors": [
   478  							{
   479  								 "detail": "Organization 'some-org-name' already exists.",
   480  								 "title": "CF-UnprocessableEntity",
   481  								 "code": 10008
   482  							}
   483  					 ]
   484  				}`
   485  
   486  				expectedBody := map[string]interface{}{
   487  					"name": "some-org-name",
   488  				}
   489  
   490  				server.AppendHandlers(
   491  					CombineHandlers(
   492  						VerifyRequest(http.MethodPost, "/v3/organizations"),
   493  						VerifyJSONRepresenting(expectedBody),
   494  						RespondWith(http.StatusUnprocessableEntity, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   495  					),
   496  				)
   497  			})
   498  
   499  			It("returns a meaningful organization-name-taken error", func() {
   500  				Expect(executeErr).To(MatchError(ccerror.OrganizationNameTakenError{
   501  					UnprocessableEntityError: ccerror.UnprocessableEntityError{
   502  						Message: "Organization 'some-org-name' already exists.",
   503  					},
   504  				}))
   505  				Expect(warnings).To(ConsistOf("this is a warning"))
   506  			})
   507  		})
   508  
   509  		When("creating the org fails", func() {
   510  			BeforeEach(func() {
   511  				response := `{
   512  					 "errors": [
   513  							{
   514  								 "detail": "Fail",
   515  								 "title": "CF-SomeError",
   516  								 "code": 10002
   517  							},
   518  							{
   519  								 "detail": "Something went terribly wrong",
   520  								 "title": "CF-UnknownError",
   521  								 "code": 10001
   522  							}
   523  					 ]
   524  				}`
   525  
   526  				expectedBody := map[string]interface{}{
   527  					"name": "some-org-name",
   528  				}
   529  
   530  				server.AppendHandlers(
   531  					CombineHandlers(
   532  						VerifyRequest(http.MethodPost, "/v3/organizations"),
   533  						VerifyJSONRepresenting(expectedBody),
   534  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   535  					),
   536  				)
   537  			})
   538  
   539  			It("returns an error", func() {
   540  				Expect(executeErr).To(MatchError(ccerror.MultiError{
   541  					ResponseCode: http.StatusTeapot,
   542  					Errors: []ccerror.V3Error{
   543  						{
   544  							Code:   10002,
   545  							Detail: "Fail",
   546  							Title:  "CF-SomeError",
   547  						},
   548  						{
   549  							Code:   10001,
   550  							Detail: "Something went terribly wrong",
   551  							Title:  "CF-UnknownError",
   552  						},
   553  					},
   554  				}))
   555  				Expect(warnings).To(ConsistOf("this is a warning"))
   556  			})
   557  		})
   558  	})
   559  
   560  	Describe("UpdateOrganization", func() {
   561  		var (
   562  			orgToUpdate Organization
   563  			updatedOrg  Organization
   564  			warnings    Warnings
   565  			executeErr  error
   566  		)
   567  
   568  		JustBeforeEach(func() {
   569  			updatedOrg, warnings, executeErr = client.UpdateOrganization(orgToUpdate)
   570  		})
   571  
   572  		When("the organization is updated successfully", func() {
   573  			BeforeEach(func() {
   574  				response := `{
   575  					"guid": "some-org-guid",
   576  					"name": "some-org-name",
   577  					"metadata": {
   578  						"labels": {
   579  							"k1":"v1",
   580  							"k2":"v2"
   581  						}
   582  					}
   583  				}`
   584  
   585  				expectedBody := map[string]interface{}{
   586  					"name": "some-org-name",
   587  					"metadata": map[string]interface{}{
   588  						"labels": map[string]string{
   589  							"k1": "v1",
   590  							"k2": "v2",
   591  						},
   592  					},
   593  				}
   594  
   595  				server.AppendHandlers(
   596  					CombineHandlers(
   597  						VerifyRequest(http.MethodPatch, "/v3/organizations/some-guid"),
   598  						VerifyJSONRepresenting(expectedBody),
   599  						RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   600  					),
   601  				)
   602  
   603  				orgToUpdate = Organization{
   604  					Name: "some-org-name",
   605  					GUID: "some-guid",
   606  					Metadata: &Metadata{
   607  						Labels: map[string]types.NullString{
   608  							"k1": types.NewNullString("v1"),
   609  							"k2": types.NewNullString("v2"),
   610  						},
   611  					},
   612  				}
   613  			})
   614  
   615  			It("should include the labels in the JSON", func() {
   616  				Expect(executeErr).ToNot(HaveOccurred())
   617  				Expect(len(warnings)).To(Equal(1))
   618  				Expect(updatedOrg.Metadata.Labels).To(BeEquivalentTo(
   619  					map[string]types.NullString{
   620  						"k1": types.NewNullString("v1"),
   621  						"k2": types.NewNullString("v2"),
   622  					}))
   623  			})
   624  		})
   625  	})
   626  
   627  	Describe("DeleteOrganization", func() {
   628  		var (
   629  			jobURL     JobURL
   630  			warnings   Warnings
   631  			executeErr error
   632  		)
   633  
   634  		JustBeforeEach(func() {
   635  			jobURL, warnings, executeErr = client.DeleteOrganization("org-guid")
   636  		})
   637  
   638  		When("no errors are encountered", func() {
   639  			BeforeEach(func() {
   640  				server.AppendHandlers(
   641  					CombineHandlers(
   642  						VerifyRequest(http.MethodDelete, "/v3/organizations/org-guid"),
   643  						RespondWith(http.StatusAccepted, nil, http.Header{"X-Cf-Warnings": {"warning-1, warning-2"}, "Location": []string{"job-url"}}),
   644  					))
   645  			})
   646  
   647  			It("deletes the Org and returns all warnings", func() {
   648  				Expect(executeErr).NotTo(HaveOccurred())
   649  				Expect(warnings).To(ConsistOf(Warnings{"warning-1", "warning-2"}))
   650  				Expect(jobURL).To(Equal(JobURL("job-url")))
   651  			})
   652  		})
   653  
   654  		When("an error is encountered", func() {
   655  			BeforeEach(func() {
   656  				response := `{
   657     "errors": [
   658        {
   659           "detail": "Organization not found",
   660           "title": "CF-ResourceNotFound",
   661           "code": 10010
   662        }
   663     ]
   664  }`
   665  				server.AppendHandlers(
   666  					CombineHandlers(
   667  						VerifyRequest(http.MethodDelete, "/v3/organizations/org-guid"),
   668  						RespondWith(http.StatusNotFound, response, http.Header{"X-Cf-Warnings": {"warning-1, warning-2"}}),
   669  					))
   670  			})
   671  
   672  			It("returns an error and all warnings", func() {
   673  				Expect(executeErr).To(MatchError(ccerror.ResourceNotFoundError{
   674  					Message: "Organization not found",
   675  				}))
   676  				Expect(warnings).To(ConsistOf(Warnings{"warning-1", "warning-2"}))
   677  			})
   678  		})
   679  	})
   680  })