github.com/wanddynosios/cli/v8@v8.7.9-0.20240221182337-1a92e3a7017f/api/cloudcontroller/ccv3/space_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  	. "code.cloudfoundry.org/cli/resources"
    12  	"code.cloudfoundry.org/cli/types"
    13  	. "github.com/onsi/ginkgo"
    14  	. "github.com/onsi/gomega"
    15  	. "github.com/onsi/gomega/ghttp"
    16  )
    17  
    18  var _ = Describe("Spaces", func() {
    19  	var client *Client
    20  
    21  	BeforeEach(func() {
    22  		client, _ = NewTestClient()
    23  	})
    24  
    25  	Describe("CreateSpace", func() {
    26  		var (
    27  			space         Space
    28  			spaceToCreate Space
    29  			warnings      Warnings
    30  			executeErr    error
    31  		)
    32  
    33  		JustBeforeEach(func() {
    34  			spaceToCreate = Space{Name: "some-space", Relationships: resources.Relationships{constant.RelationshipTypeOrganization: resources.Relationship{GUID: "some-org-guid"}}}
    35  			space, warnings, executeErr = client.CreateSpace(spaceToCreate)
    36  		})
    37  
    38  		When("spaces exist", func() {
    39  			BeforeEach(func() {
    40  				response := `{
    41        "name": "some-space",
    42        "guid": "some-space-guid"
    43      }`
    44  
    45  				expectedBody := `{
    46  "name": "some-space",
    47  "relationships": {
    48        "organization": {
    49        "data": { "guid": "some-org-guid" }
    50      }
    51    }
    52  }`
    53  				server.AppendHandlers(
    54  					CombineHandlers(
    55  						VerifyRequest(http.MethodPost, "/v3/spaces"),
    56  						VerifyJSON(expectedBody),
    57  						RespondWith(http.StatusCreated, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
    58  					),
    59  				)
    60  			})
    61  
    62  			It("returns the given space and all warnings", func() {
    63  				Expect(executeErr).ToNot(HaveOccurred())
    64  				Expect(warnings).To(ConsistOf("this is a warning"))
    65  
    66  				Expect(space).To(Equal(Space{
    67  					GUID: "some-space-guid",
    68  					Name: "some-space",
    69  				}))
    70  			})
    71  		})
    72  
    73  		When("the cloud controller returns errors and warnings", func() {
    74  			BeforeEach(func() {
    75  				response := `{
    76    "errors": [
    77      {
    78        "code": 10008,
    79        "detail": "The request is semantically invalid: command presence",
    80        "title": "CF-UnprocessableEntity"
    81      },
    82  		{
    83        "code": 10010,
    84        "detail": "Isolation segment not found",
    85        "title": "CF-ResourceNotFound"
    86      }
    87    ]
    88  }`
    89  				server.AppendHandlers(
    90  					CombineHandlers(
    91  						VerifyRequest(http.MethodPost, "/v3/spaces"),
    92  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
    93  					),
    94  				)
    95  			})
    96  
    97  			It("returns the error and all warnings", func() {
    98  				Expect(executeErr).To(MatchError(ccerror.MultiError{
    99  					ResponseCode: http.StatusTeapot,
   100  					Errors: []ccerror.V3Error{
   101  						{
   102  							Code:   10008,
   103  							Detail: "The request is semantically invalid: command presence",
   104  							Title:  "CF-UnprocessableEntity",
   105  						},
   106  						{
   107  							Code:   10010,
   108  							Detail: "Isolation segment not found",
   109  							Title:  "CF-ResourceNotFound",
   110  						},
   111  					},
   112  				}))
   113  				Expect(warnings).To(ConsistOf("this is a warning"))
   114  			})
   115  		})
   116  
   117  	})
   118  
   119  	Describe("GetSpaces", func() {
   120  		var (
   121  			query []Query
   122  
   123  			includes   IncludedResources
   124  			spaces     []Space
   125  			warnings   Warnings
   126  			executeErr error
   127  		)
   128  
   129  		JustBeforeEach(func() {
   130  			spaces, includes, warnings, executeErr = client.GetSpaces(query...)
   131  		})
   132  
   133  		When("spaces exist", func() {
   134  			BeforeEach(func() {
   135  				response1 := fmt.Sprintf(`{
   136  	"pagination": {
   137  		"next": {
   138  			"href": "%s/v3/spaces?names=some-space-name&page=2&per_page=2"
   139  		}
   140  	},
   141    "resources": [
   142      {
   143        "name": "space-name-1",
   144        "guid": "space-guid-1",
   145        "relationships": {
   146          "organization": {
   147            "data": { "guid": "org-guid-1" }
   148          }
   149        }
   150      },
   151      {
   152        "name": "space-name-2",
   153        "guid": "space-guid-2",
   154        "relationships": {
   155          "organization": {
   156            "data": { "guid": "org-guid-2" }
   157          }
   158        }
   159      }
   160    ]
   161  }`, server.URL())
   162  				response2 := `{
   163    "pagination": {
   164      "next": null
   165    },
   166    "resources": [
   167      {
   168        "name": "space-name-3",
   169        "guid": "space-guid-3",
   170        "relationships": {
   171          "organization": {
   172            "data": { "guid": "org-guid-3" }
   173          }
   174        }
   175      }
   176    ]
   177  }`
   178  
   179  				server.AppendHandlers(
   180  					CombineHandlers(
   181  						VerifyRequest(http.MethodGet, "/v3/spaces", "names=some-space-name"),
   182  						RespondWith(http.StatusOK, response1, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   183  					),
   184  				)
   185  				server.AppendHandlers(
   186  					CombineHandlers(
   187  						VerifyRequest(http.MethodGet, "/v3/spaces", "names=some-space-name&page=2&per_page=2"),
   188  						RespondWith(http.StatusOK, response2, http.Header{"X-Cf-Warnings": {"this is another warning"}}),
   189  					),
   190  				)
   191  
   192  				query = []Query{{
   193  					Key:    NameFilter,
   194  					Values: []string{"some-space-name"},
   195  				}}
   196  			})
   197  
   198  			It("returns the queried spaces and all warnings", func() {
   199  				Expect(executeErr).NotTo(HaveOccurred())
   200  
   201  				Expect(spaces).To(ConsistOf(
   202  					Space{Name: "space-name-1", GUID: "space-guid-1", Relationships: resources.Relationships{
   203  						constant.RelationshipTypeOrganization: resources.Relationship{GUID: "org-guid-1"},
   204  					}},
   205  					Space{Name: "space-name-2", GUID: "space-guid-2", Relationships: resources.Relationships{
   206  						constant.RelationshipTypeOrganization: resources.Relationship{GUID: "org-guid-2"},
   207  					}},
   208  					Space{Name: "space-name-3", GUID: "space-guid-3", Relationships: resources.Relationships{
   209  						constant.RelationshipTypeOrganization: resources.Relationship{GUID: "org-guid-3"},
   210  					}},
   211  				))
   212  				Expect(warnings).To(ConsistOf("this is a warning", "this is another warning"))
   213  			})
   214  		})
   215  
   216  		When("the request uses the `include` query key", func() {
   217  			BeforeEach(func() {
   218  				response1 := fmt.Sprintf(`{
   219  	"pagination": {
   220  		"next": {
   221  			"href": "%s/v3/spaces?names=some-space-name&include=organizations&page=2&per_page=2"
   222  		}
   223  	},
   224    "resources": [
   225      {
   226        "name": "space-name-1",
   227        "guid": "space-guid-1",
   228        "relationships": {
   229          "organization": {
   230            "data": { "guid": "org-guid-1" }
   231          }
   232        }
   233      },
   234      {
   235        "name": "space-name-2",
   236        "guid": "space-guid-2",
   237        "relationships": {
   238          "organization": {
   239            "data": { "guid": "org-guid-2" }
   240          }
   241        }
   242      }
   243    ],
   244    "included": {
   245    		"organizations": [
   246  	  {
   247  		  "guid": "org-guid-1",
   248  		  "name": "org-name-1"
   249  	  }
   250        ]
   251    }
   252  }`, server.URL())
   253  				response2 := `{
   254    "pagination": {
   255      "next": null
   256    },
   257    "resources": [
   258      {
   259        "name": "space-name-3",
   260        "guid": "space-guid-3",
   261        "relationships": {
   262          "organization": {
   263            "data": { "guid": "org-guid-3" }
   264          }
   265        }
   266      }
   267    ],
   268  "included": {
   269  	"organizations": [
   270  		{
   271  			"guid": "org-guid-2",
   272  			"name": "org-name-2"
   273  		}
   274  	]
   275  }
   276  }`
   277  
   278  				server.AppendHandlers(
   279  					CombineHandlers(
   280  						VerifyRequest(http.MethodGet, "/v3/spaces", "names=some-space-name&include=organizations"),
   281  						RespondWith(http.StatusOK, response1, http.Header{"X-Cf-Warnings": {"warning-1"}}),
   282  					),
   283  				)
   284  				server.AppendHandlers(
   285  					CombineHandlers(
   286  						VerifyRequest(http.MethodGet, "/v3/spaces", "names=some-space-name&page=2&per_page=2&include=organizations"),
   287  						RespondWith(http.StatusOK, response2, http.Header{"X-Cf-Warnings": {"warning-2"}}),
   288  					),
   289  				)
   290  
   291  				query = []Query{
   292  					{
   293  						Key:    NameFilter,
   294  						Values: []string{"some-space-name"},
   295  					},
   296  					{
   297  						Key:    Include,
   298  						Values: []string{"organizations"},
   299  					},
   300  				}
   301  			})
   302  
   303  			It("returns the given route and all warnings", func() {
   304  				Expect(executeErr).ToNot(HaveOccurred())
   305  				Expect(warnings).To(ConsistOf("warning-1", "warning-2"))
   306  
   307  				Expect(spaces).To(ConsistOf(
   308  					Space{Name: "space-name-1", GUID: "space-guid-1", Relationships: resources.Relationships{
   309  						constant.RelationshipTypeOrganization: resources.Relationship{GUID: "org-guid-1"},
   310  					}},
   311  					Space{Name: "space-name-2", GUID: "space-guid-2", Relationships: resources.Relationships{
   312  						constant.RelationshipTypeOrganization: resources.Relationship{GUID: "org-guid-2"},
   313  					}},
   314  					Space{Name: "space-name-3", GUID: "space-guid-3", Relationships: resources.Relationships{
   315  						constant.RelationshipTypeOrganization: resources.Relationship{GUID: "org-guid-3"},
   316  					}},
   317  				))
   318  
   319  				Expect(includes).To(Equal(IncludedResources{
   320  					Organizations: []Organization{
   321  						{GUID: "org-guid-1", Name: "org-name-1"},
   322  						{GUID: "org-guid-2", Name: "org-name-2"},
   323  					},
   324  				}))
   325  			})
   326  		})
   327  
   328  		When("the cloud controller returns errors and warnings", func() {
   329  			BeforeEach(func() {
   330  				response := `{
   331    "errors": [
   332      {
   333        "code": 10008,
   334        "detail": "The request is semantically invalid: command presence",
   335        "title": "CF-UnprocessableEntity"
   336      },
   337      {
   338        "code": 10010,
   339        "detail": "Space not found",
   340        "title": "CF-SpaceNotFound"
   341      }
   342    ]
   343  }`
   344  				server.AppendHandlers(
   345  					CombineHandlers(
   346  						VerifyRequest(http.MethodGet, "/v3/spaces"),
   347  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   348  					),
   349  				)
   350  			})
   351  
   352  			It("returns the error and all warnings", func() {
   353  				Expect(executeErr).To(MatchError(ccerror.MultiError{
   354  					ResponseCode: http.StatusTeapot,
   355  					Errors: []ccerror.V3Error{
   356  						{
   357  							Code:   10008,
   358  							Detail: "The request is semantically invalid: command presence",
   359  							Title:  "CF-UnprocessableEntity",
   360  						},
   361  						{
   362  							Code:   10010,
   363  							Detail: "Space not found",
   364  							Title:  "CF-SpaceNotFound",
   365  						},
   366  					},
   367  				}))
   368  				Expect(warnings).To(ConsistOf("this is a warning"))
   369  			})
   370  		})
   371  	})
   372  
   373  	Describe("UpdateSpace", func() {
   374  		var (
   375  			spaceToUpdate Space
   376  			updatedSpace  Space
   377  			warnings      Warnings
   378  			executeErr    error
   379  		)
   380  
   381  		JustBeforeEach(func() {
   382  			updatedSpace, warnings, executeErr = client.UpdateSpace(spaceToUpdate)
   383  		})
   384  
   385  		When("the organization is updated successfully", func() {
   386  			BeforeEach(func() {
   387  				response := `{
   388  					"guid": "some-space-guid",
   389  					"name": "some-space-name",
   390  					"metadata": {
   391  						"labels": {
   392  							"k1":"v1",
   393  							"k2":"v2"
   394  						}
   395  					}
   396  				}`
   397  
   398  				expectedBody := map[string]interface{}{
   399  					"name": "some-space-name",
   400  					"metadata": map[string]interface{}{
   401  						"labels": map[string]string{
   402  							"k1": "v1",
   403  							"k2": "v2",
   404  						},
   405  					},
   406  				}
   407  
   408  				server.AppendHandlers(
   409  					CombineHandlers(
   410  						VerifyRequest(http.MethodPatch, "/v3/spaces/some-guid"),
   411  						VerifyJSONRepresenting(expectedBody),
   412  						RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   413  					),
   414  				)
   415  
   416  				spaceToUpdate = Space{
   417  					Name: "some-space-name",
   418  					GUID: "some-guid",
   419  					Metadata: &Metadata{
   420  						Labels: map[string]types.NullString{
   421  							"k1": types.NewNullString("v1"),
   422  							"k2": types.NewNullString("v2"),
   423  						},
   424  					},
   425  				}
   426  			})
   427  
   428  			It("should include the labels in the JSON", func() {
   429  				Expect(executeErr).ToNot(HaveOccurred())
   430  				Expect(server.ReceivedRequests()).To(HaveLen(1))
   431  				Expect(len(warnings)).To(Equal(1))
   432  				Expect(warnings).To(ConsistOf("this is a warning"))
   433  				Expect(updatedSpace.Metadata.Labels).To(BeEquivalentTo(
   434  					map[string]types.NullString{
   435  						"k1": types.NewNullString("v1"),
   436  						"k2": types.NewNullString("v2"),
   437  					}))
   438  			})
   439  
   440  		})
   441  
   442  	})
   443  
   444  	Describe("DeleteSpace", func() {
   445  		var (
   446  			jobURL     JobURL
   447  			warnings   Warnings
   448  			executeErr error
   449  		)
   450  
   451  		JustBeforeEach(func() {
   452  			jobURL, warnings, executeErr = client.DeleteSpace("space-guid")
   453  		})
   454  
   455  		When("no errors are encountered", func() {
   456  			BeforeEach(func() {
   457  				server.AppendHandlers(
   458  					CombineHandlers(
   459  						VerifyRequest(http.MethodDelete, "/v3/spaces/space-guid"),
   460  						RespondWith(http.StatusAccepted, nil, http.Header{"X-Cf-Warnings": {"warning-1, warning-2"}, "Location": []string{"job-url"}}),
   461  					))
   462  			})
   463  
   464  			It("deletes the resources.Space and returns all warnings", func() {
   465  				Expect(executeErr).NotTo(HaveOccurred())
   466  				Expect(warnings).To(ConsistOf(Warnings{"warning-1", "warning-2"}))
   467  				Expect(jobURL).To(Equal(JobURL("job-url")))
   468  			})
   469  		})
   470  
   471  		When("an error is encountered", func() {
   472  			BeforeEach(func() {
   473  				response := `{
   474     "errors": [
   475        {
   476           "detail": "Space not found",
   477           "title": "CF-ResourceNotFound",
   478           "code": 10010
   479        }
   480     ]
   481  }`
   482  				server.AppendHandlers(
   483  					CombineHandlers(
   484  						VerifyRequest(http.MethodDelete, "/v3/spaces/space-guid"),
   485  						RespondWith(http.StatusNotFound, response, http.Header{"X-Cf-Warnings": {"warning-1, warning-2"}}),
   486  					))
   487  			})
   488  
   489  			It("returns an error and all warnings", func() {
   490  				Expect(executeErr).To(MatchError(ccerror.ResourceNotFoundError{
   491  					Message: "Space not found",
   492  				}))
   493  				Expect(warnings).To(ConsistOf(Warnings{"warning-1", "warning-2"}))
   494  			})
   495  		})
   496  	})
   497  })