github.com/sleungcy/cli@v7.1.0+incompatible/api/cloudcontroller/ccv3/relationship_test.go (about)

     1  package ccv3_test
     2  
     3  import (
     4  	"encoding/json"
     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  	. "github.com/onsi/ginkgo"
    11  	. "github.com/onsi/gomega"
    12  	. "github.com/onsi/gomega/ghttp"
    13  )
    14  
    15  var _ = Describe("Relationship", func() {
    16  	var (
    17  		client *Client
    18  	)
    19  
    20  	BeforeEach(func() {
    21  		client, _ = NewTestClient()
    22  	})
    23  
    24  	Describe("Relationship", func() {
    25  		Describe("MarshalJSON", func() {
    26  			When("the isolation segment is specified by name", func() {
    27  				It("contains the name in the marshaled JSON", func() {
    28  					body, err := json.Marshal(resources.Relationship{GUID: "some-iso-guid"})
    29  					expectedJSON := `{
    30  					"data": {
    31  						"guid": "some-iso-guid"
    32  					}
    33  				}`
    34  
    35  					Expect(err).NotTo(HaveOccurred())
    36  					Expect(body).To(MatchJSON(expectedJSON))
    37  				})
    38  			})
    39  
    40  			When("the isolation segment is the empty string", func() {
    41  				It("contains null in the marshaled JSON", func() {
    42  					body, err := json.Marshal(resources.Relationship{GUID: ""})
    43  					expectedJSON := `{
    44  					"data": null
    45  				}`
    46  
    47  					Expect(err).NotTo(HaveOccurred())
    48  					Expect(body).To(MatchJSON(expectedJSON))
    49  				})
    50  			})
    51  		})
    52  	})
    53  
    54  	Describe("UpdateSpaceIsolationSegmentRelationship", func() {
    55  		When("the assignment is successful", func() {
    56  			BeforeEach(func() {
    57  				response := `{
    58  					"data": {
    59  						"guid": "some-isolation-segment-guid"
    60  					}
    61  				}`
    62  
    63  				requestBody := map[string]map[string]string{
    64  					"data": {"guid": "some-iso-guid"},
    65  				}
    66  				server.AppendHandlers(
    67  					CombineHandlers(
    68  						VerifyRequest(http.MethodPatch, "/v3/spaces/some-space-guid/relationships/isolation_segment"),
    69  						VerifyJSONRepresenting(requestBody),
    70  						RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
    71  					),
    72  				)
    73  			})
    74  
    75  			It("returns all relationships and warnings", func() {
    76  				relationship, warnings, err := client.UpdateSpaceIsolationSegmentRelationship("some-space-guid", "some-iso-guid")
    77  				Expect(err).NotTo(HaveOccurred())
    78  				Expect(warnings).To(ConsistOf("this is a warning"))
    79  				Expect(relationship).To(Equal(resources.Relationship{
    80  					GUID: "some-isolation-segment-guid",
    81  				}))
    82  			})
    83  		})
    84  	})
    85  
    86  	Describe("DeleteServiceInstanceRelationshipsSharedSpace", func() {
    87  		var (
    88  			serviceInstanceGUID string
    89  			spaceGUID           string
    90  
    91  			warnings   Warnings
    92  			executeErr error
    93  		)
    94  
    95  		BeforeEach(func() {
    96  			serviceInstanceGUID = "some-service-instance-guid"
    97  			spaceGUID = "some-space-guid"
    98  		})
    99  
   100  		JustBeforeEach(func() {
   101  			warnings, executeErr = client.DeleteServiceInstanceRelationshipsSharedSpace(serviceInstanceGUID, spaceGUID)
   102  		})
   103  
   104  		When("no errors occur deleting the shared space relationship", func() {
   105  			BeforeEach(func() {
   106  				server.AppendHandlers(
   107  					CombineHandlers(
   108  						VerifyRequest(http.MethodDelete, "/v3/service_instances/some-service-instance-guid/relationships/shared_spaces/some-space-guid"),
   109  						RespondWith(http.StatusNoContent, "{}", http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   110  					),
   111  				)
   112  			})
   113  
   114  			It("does not return any errors and returns all warnings", func() {
   115  				Expect(executeErr).NotTo(HaveOccurred())
   116  				Expect(warnings).To(ConsistOf("this is a warning"))
   117  			})
   118  		})
   119  
   120  		When("an error occurs deleting the shared space relationship", func() {
   121  			BeforeEach(func() {
   122  				response := `{
   123  						"errors": [
   124  							{
   125  								"code": 10008,
   126  								"detail": "The request is semantically invalid: command presence",
   127  								"title": "CF-UnprocessableEntity"
   128  							},
   129  							{
   130  								"code": 10008,
   131  								"detail": "The request is semantically invalid: command presence",
   132  								"title": "CF-UnprocessableEntity"
   133  							}
   134  						]
   135  					}`
   136  				server.AppendHandlers(
   137  					CombineHandlers(
   138  						VerifyRequest(http.MethodDelete, "/v3/service_instances/some-service-instance-guid/relationships/shared_spaces/some-space-guid"),
   139  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   140  					),
   141  				)
   142  			})
   143  
   144  			It("returns the errors and all warnings", func() {
   145  				Expect(executeErr).To(MatchError(ccerror.MultiError{
   146  					ResponseCode: http.StatusTeapot,
   147  					Errors: []ccerror.V3Error{
   148  						{
   149  							Code:   10008,
   150  							Detail: "The request is semantically invalid: command presence",
   151  							Title:  "CF-UnprocessableEntity",
   152  						},
   153  						{
   154  							Code:   10008,
   155  							Detail: "The request is semantically invalid: command presence",
   156  							Title:  "CF-UnprocessableEntity",
   157  						},
   158  					},
   159  				}))
   160  				Expect(warnings).To(ConsistOf("this is a warning"))
   161  			})
   162  		})
   163  	})
   164  
   165  	Describe("GetOrganizationDefaultIsolationSegment", func() {
   166  		When("getting the isolation segment is successful", func() {
   167  			BeforeEach(func() {
   168  				response := `{
   169  					"data": {
   170  						"guid": "some-isolation-segment-guid"
   171  					}
   172  				}`
   173  
   174  				server.AppendHandlers(
   175  					CombineHandlers(
   176  						VerifyRequest(http.MethodGet, "/v3/organizations/some-org-guid/relationships/default_isolation_segment"),
   177  						RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   178  					),
   179  				)
   180  			})
   181  
   182  			It("returns the relationship and warnings", func() {
   183  				relationship, warnings, err := client.GetOrganizationDefaultIsolationSegment("some-org-guid")
   184  				Expect(err).NotTo(HaveOccurred())
   185  				Expect(warnings).To(ConsistOf("this is a warning"))
   186  				Expect(relationship).To(Equal(resources.Relationship{
   187  					GUID: "some-isolation-segment-guid",
   188  				}))
   189  			})
   190  		})
   191  
   192  		When("getting the isolation segment fails with an error", func() {
   193  			BeforeEach(func() {
   194  				response := `{
   195  					"errors": [
   196  						{
   197  							"detail": "Organization not found",
   198  							"title": "CF-ResourceNotFound",
   199  							"code": 10010
   200  						}
   201  					]
   202  				}`
   203  				server.AppendHandlers(
   204  					CombineHandlers(
   205  						VerifyRequest(http.MethodGet, "/v3/organizations/some-org-guid/relationships/default_isolation_segment"),
   206  						RespondWith(http.StatusNotFound, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   207  					),
   208  				)
   209  			})
   210  
   211  			It("returns an error and warnings", func() {
   212  				_, warnings, err := client.GetOrganizationDefaultIsolationSegment("some-org-guid")
   213  				Expect(err).To(MatchError(ccerror.ResourceNotFoundError{
   214  					Message: "Organization not found",
   215  				}))
   216  				Expect(warnings).To(ConsistOf("this is a warning"))
   217  			})
   218  		})
   219  	})
   220  
   221  	Describe("GetSpaceIsolationSegment", func() {
   222  		When("getting the isolation segment is successful", func() {
   223  			BeforeEach(func() {
   224  				response := `{
   225  					"data": {
   226  						"guid": "some-isolation-segment-guid"
   227  					}
   228  				}`
   229  
   230  				server.AppendHandlers(
   231  					CombineHandlers(
   232  						VerifyRequest(http.MethodGet, "/v3/spaces/some-space-guid/relationships/isolation_segment"),
   233  						RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   234  					),
   235  				)
   236  			})
   237  
   238  			It("returns the relationship and warnings", func() {
   239  				relationship, warnings, err := client.GetSpaceIsolationSegment("some-space-guid")
   240  				Expect(err).NotTo(HaveOccurred())
   241  				Expect(warnings).To(ConsistOf("this is a warning"))
   242  				Expect(relationship).To(Equal(resources.Relationship{
   243  					GUID: "some-isolation-segment-guid",
   244  				}))
   245  			})
   246  		})
   247  	})
   248  
   249  	Describe("UpdateOrganizationDefaultIsolationSegmentRelationship", func() {
   250  		When("patching the default organization isolation segment with non-empty isolation segment guid", func() {
   251  			BeforeEach(func() {
   252  				expectedBody := `{
   253  					"data": {
   254  						"guid": "some-isolation-segment-guid"
   255  					}
   256  				}`
   257  				responseBody := `{
   258  					"data": {
   259  						"guid": "some-isolation-segment-guid"
   260  					}
   261  				}`
   262  
   263  				server.AppendHandlers(
   264  					CombineHandlers(
   265  						VerifyRequest(http.MethodPatch, "/v3/organizations/some-org-guid/relationships/default_isolation_segment"),
   266  						VerifyJSON(expectedBody),
   267  						RespondWith(http.StatusOK, responseBody, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   268  					),
   269  				)
   270  			})
   271  
   272  			It("patches the organization's default isolation segment", func() {
   273  				relationship, warnings, err := client.UpdateOrganizationDefaultIsolationSegmentRelationship("some-org-guid", "some-isolation-segment-guid")
   274  				Expect(relationship).To(Equal(resources.Relationship{GUID: "some-isolation-segment-guid"}))
   275  				Expect(err).ToNot(HaveOccurred())
   276  				Expect(warnings).To(ConsistOf("this is a warning"))
   277  			})
   278  		})
   279  
   280  		When("patching the default organization isolation segment with empty isolation segment guid", func() {
   281  			BeforeEach(func() {
   282  				expectedBody := `{
   283  					"data": null
   284  				}`
   285  				responseBody := `{
   286  					"data": null
   287  				}`
   288  				server.AppendHandlers(
   289  					CombineHandlers(
   290  						VerifyRequest(http.MethodPatch, "/v3/organizations/some-org-guid/relationships/default_isolation_segment"),
   291  						VerifyJSON(expectedBody),
   292  						RespondWith(http.StatusOK, responseBody, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   293  					),
   294  				)
   295  			})
   296  
   297  			It("patches the organization's default isolation segment with nil guid", func() {
   298  				relationship, warnings, err := client.UpdateOrganizationDefaultIsolationSegmentRelationship("some-org-guid", "")
   299  				Expect(relationship).To(BeZero())
   300  				Expect(err).ToNot(HaveOccurred())
   301  				Expect(warnings).To(ConsistOf("this is a warning"))
   302  			})
   303  		})
   304  
   305  		When("patching the isolation segment fails with an error", func() {
   306  			BeforeEach(func() {
   307  				response := `{
   308  					"errors": [
   309  						{
   310  							"detail": "Organization not found",
   311  							"title": "CF-ResourceNotFound",
   312  							"code": 10010
   313  						}
   314  					]
   315  				}`
   316  
   317  				server.AppendHandlers(
   318  					CombineHandlers(
   319  						VerifyRequest(http.MethodPatch, "/v3/organizations/some-org-guid/relationships/default_isolation_segment"),
   320  						RespondWith(http.StatusNotFound, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   321  					),
   322  				)
   323  			})
   324  
   325  			It("returns the empty relationship, an error and warnings", func() {
   326  				relationship, warnings, err := client.UpdateOrganizationDefaultIsolationSegmentRelationship("some-org-guid", "some-isolation-segment-guid")
   327  				Expect(relationship).To(BeZero())
   328  				Expect(err).To(MatchError(ccerror.ResourceNotFoundError{
   329  					Message: "Organization not found",
   330  				}))
   331  				Expect(warnings).To(ConsistOf("this is a warning"))
   332  			})
   333  		})
   334  	})
   335  
   336  	Describe("DeleteIsolationSegmentOrganization", func() {
   337  		When("relationship exists", func() {
   338  			BeforeEach(func() {
   339  				server.AppendHandlers(
   340  					CombineHandlers(
   341  						VerifyRequest(http.MethodDelete, "/v3/isolation_segments/segment-guid/relationships/organizations/org-guid"),
   342  						RespondWith(http.StatusOK, "", http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   343  					),
   344  				)
   345  			})
   346  
   347  			It("revoke the relationship", func() {
   348  				warnings, err := client.DeleteIsolationSegmentOrganization("segment-guid", "org-guid")
   349  				Expect(err).ToNot(HaveOccurred())
   350  				Expect(warnings).To(ConsistOf("this is a warning"))
   351  
   352  				Expect(server.ReceivedRequests()).To(HaveLen(3))
   353  			})
   354  		})
   355  
   356  		When("an error occurs", func() {
   357  			BeforeEach(func() {
   358  				response := `{
   359  					"errors": [
   360  						{
   361  							"code": 10008,
   362  							"detail": "The request is semantically invalid: command presence",
   363  							"title": "CF-UnprocessableEntity"
   364  						},
   365  						{
   366  							"code": 10008,
   367  							"detail": "The request is semantically invalid: command presence",
   368  							"title": "CF-UnprocessableEntity"
   369  						}
   370  					]
   371  				}`
   372  
   373  				server.AppendHandlers(
   374  					CombineHandlers(
   375  						VerifyRequest(http.MethodDelete, "/v3/isolation_segments/segment-guid/relationships/organizations/org-guid"),
   376  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   377  					),
   378  				)
   379  			})
   380  
   381  			It("returns the error and warnings", func() {
   382  				warnings, err := client.DeleteIsolationSegmentOrganization("segment-guid", "org-guid")
   383  				Expect(err).To(MatchError(ccerror.MultiError{
   384  					ResponseCode: http.StatusTeapot,
   385  					Errors: []ccerror.V3Error{
   386  						{
   387  							Code:   10008,
   388  							Detail: "The request is semantically invalid: command presence",
   389  							Title:  "CF-UnprocessableEntity",
   390  						},
   391  						{
   392  							Code:   10008,
   393  							Detail: "The request is semantically invalid: command presence",
   394  							Title:  "CF-UnprocessableEntity",
   395  						},
   396  					},
   397  				}))
   398  				Expect(warnings).To(ConsistOf("this is a warning"))
   399  			})
   400  		})
   401  	})
   402  
   403  	Describe("SetApplicationDroplet", func() {
   404  		Context("it sets the droplet", func() {
   405  			BeforeEach(func() {
   406  				response := `
   407  {
   408    "data": {
   409      "guid": "some-droplet-guid"
   410    },
   411    "links": {
   412      "self": {
   413        "href": "https://api.example.org/v3/apps/some-app-guid/relationships/current_droplet"
   414      },
   415      "related": {
   416        "href": "https://api.example.org/v3/apps/some-app-guid/droplets/current"
   417      }
   418    }
   419  }`
   420  				requestBody := map[string]interface{}{
   421  					"data": map[string]string{
   422  						"guid": "some-droplet-guid",
   423  					},
   424  				}
   425  
   426  				server.AppendHandlers(
   427  					CombineHandlers(
   428  						VerifyRequest(http.MethodPatch, "/v3/apps/some-app-guid/relationships/current_droplet"),
   429  						VerifyJSONRepresenting(requestBody),
   430  						RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   431  					),
   432  				)
   433  			})
   434  
   435  			It("returns warnings and no error", func() {
   436  				relationship, warnings, err := client.SetApplicationDroplet("some-app-guid", "some-droplet-guid")
   437  				Expect(err).ToNot(HaveOccurred())
   438  				Expect(warnings).To(ConsistOf("this is a warning"))
   439  				Expect(relationship.GUID).To(Equal("some-droplet-guid"))
   440  			})
   441  		})
   442  
   443  		When("the CC returns an error", func() {
   444  			BeforeEach(func() {
   445  				response := `{
   446    "errors": [
   447      {
   448        "code": 10008,
   449        "detail": "The request is semantically invalid: command presence",
   450        "title": "CF-UnprocessableEntity"
   451      },
   452      {
   453        "code": 10010,
   454        "detail": "App not found",
   455        "title": "CF-ResourceNotFound"
   456      }
   457    ]
   458  }`
   459  				requestBody := map[string]interface{}{
   460  					"data": map[string]string{
   461  						"guid": "some-droplet-guid",
   462  					},
   463  				}
   464  
   465  				server.AppendHandlers(
   466  					CombineHandlers(
   467  						VerifyRequest(http.MethodPatch, "/v3/apps/no-such-app-guid/relationships/current_droplet"),
   468  						VerifyJSONRepresenting(requestBody),
   469  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   470  					),
   471  				)
   472  
   473  			})
   474  
   475  			It("returns the error and all warnings", func() {
   476  				_, warnings, err := client.SetApplicationDroplet("no-such-app-guid", "some-droplet-guid")
   477  				Expect(err).To(MatchError(ccerror.MultiError{
   478  					ResponseCode: http.StatusTeapot,
   479  					Errors: []ccerror.V3Error{
   480  						{
   481  							Code:   10008,
   482  							Detail: "The request is semantically invalid: command presence",
   483  							Title:  "CF-UnprocessableEntity",
   484  						},
   485  						{
   486  							Code:   10010,
   487  							Detail: "App not found",
   488  							Title:  "CF-ResourceNotFound",
   489  						},
   490  					},
   491  				}))
   492  				Expect(warnings).To(ConsistOf("this is a warning"))
   493  			})
   494  		})
   495  	})
   496  })