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