github.com/wanddynosios/cli/v8@v8.7.9-0.20240221182337-1a92e3a7017f/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("GetOrganizationDefaultIsolationSegment", func() {
    87  		When("getting the isolation segment is successful", func() {
    88  			BeforeEach(func() {
    89  				response := `{
    90  					"data": {
    91  						"guid": "some-isolation-segment-guid"
    92  					}
    93  				}`
    94  
    95  				server.AppendHandlers(
    96  					CombineHandlers(
    97  						VerifyRequest(http.MethodGet, "/v3/organizations/some-org-guid/relationships/default_isolation_segment"),
    98  						RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
    99  					),
   100  				)
   101  			})
   102  
   103  			It("returns the relationship and warnings", func() {
   104  				relationship, warnings, err := client.GetOrganizationDefaultIsolationSegment("some-org-guid")
   105  				Expect(err).NotTo(HaveOccurred())
   106  				Expect(warnings).To(ConsistOf("this is a warning"))
   107  				Expect(relationship).To(Equal(resources.Relationship{
   108  					GUID: "some-isolation-segment-guid",
   109  				}))
   110  			})
   111  		})
   112  
   113  		When("getting the isolation segment fails with an error", func() {
   114  			BeforeEach(func() {
   115  				response := `{
   116  					"errors": [
   117  						{
   118  							"detail": "Organization not found",
   119  							"title": "CF-ResourceNotFound",
   120  							"code": 10010
   121  						}
   122  					]
   123  				}`
   124  				server.AppendHandlers(
   125  					CombineHandlers(
   126  						VerifyRequest(http.MethodGet, "/v3/organizations/some-org-guid/relationships/default_isolation_segment"),
   127  						RespondWith(http.StatusNotFound, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   128  					),
   129  				)
   130  			})
   131  
   132  			It("returns an error and warnings", func() {
   133  				_, warnings, err := client.GetOrganizationDefaultIsolationSegment("some-org-guid")
   134  				Expect(err).To(MatchError(ccerror.ResourceNotFoundError{
   135  					Message: "Organization not found",
   136  				}))
   137  				Expect(warnings).To(ConsistOf("this is a warning"))
   138  			})
   139  		})
   140  	})
   141  
   142  	Describe("GetSpaceIsolationSegment", func() {
   143  		When("getting the isolation segment is successful", func() {
   144  			BeforeEach(func() {
   145  				response := `{
   146  					"data": {
   147  						"guid": "some-isolation-segment-guid"
   148  					}
   149  				}`
   150  
   151  				server.AppendHandlers(
   152  					CombineHandlers(
   153  						VerifyRequest(http.MethodGet, "/v3/spaces/some-space-guid/relationships/isolation_segment"),
   154  						RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   155  					),
   156  				)
   157  			})
   158  
   159  			It("returns the relationship and warnings", func() {
   160  				relationship, warnings, err := client.GetSpaceIsolationSegment("some-space-guid")
   161  				Expect(err).NotTo(HaveOccurred())
   162  				Expect(warnings).To(ConsistOf("this is a warning"))
   163  				Expect(relationship).To(Equal(resources.Relationship{
   164  					GUID: "some-isolation-segment-guid",
   165  				}))
   166  			})
   167  		})
   168  	})
   169  
   170  	Describe("UpdateOrganizationDefaultIsolationSegmentRelationship", func() {
   171  		When("patching the default organization isolation segment with non-empty isolation segment guid", func() {
   172  			BeforeEach(func() {
   173  				expectedBody := `{
   174  					"data": {
   175  						"guid": "some-isolation-segment-guid"
   176  					}
   177  				}`
   178  				responseBody := `{
   179  					"data": {
   180  						"guid": "some-isolation-segment-guid"
   181  					}
   182  				}`
   183  
   184  				server.AppendHandlers(
   185  					CombineHandlers(
   186  						VerifyRequest(http.MethodPatch, "/v3/organizations/some-org-guid/relationships/default_isolation_segment"),
   187  						VerifyJSON(expectedBody),
   188  						RespondWith(http.StatusOK, responseBody, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   189  					),
   190  				)
   191  			})
   192  
   193  			It("patches the organization's default isolation segment", func() {
   194  				relationship, warnings, err := client.UpdateOrganizationDefaultIsolationSegmentRelationship("some-org-guid", "some-isolation-segment-guid")
   195  				Expect(relationship).To(Equal(resources.Relationship{GUID: "some-isolation-segment-guid"}))
   196  				Expect(err).ToNot(HaveOccurred())
   197  				Expect(warnings).To(ConsistOf("this is a warning"))
   198  			})
   199  		})
   200  
   201  		When("patching the default organization isolation segment with empty isolation segment guid", func() {
   202  			BeforeEach(func() {
   203  				expectedBody := `{
   204  					"data": null
   205  				}`
   206  				responseBody := `{
   207  					"data": null
   208  				}`
   209  				server.AppendHandlers(
   210  					CombineHandlers(
   211  						VerifyRequest(http.MethodPatch, "/v3/organizations/some-org-guid/relationships/default_isolation_segment"),
   212  						VerifyJSON(expectedBody),
   213  						RespondWith(http.StatusOK, responseBody, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   214  					),
   215  				)
   216  			})
   217  
   218  			It("patches the organization's default isolation segment with nil guid", func() {
   219  				relationship, warnings, err := client.UpdateOrganizationDefaultIsolationSegmentRelationship("some-org-guid", "")
   220  				Expect(relationship).To(BeZero())
   221  				Expect(err).ToNot(HaveOccurred())
   222  				Expect(warnings).To(ConsistOf("this is a warning"))
   223  			})
   224  		})
   225  
   226  		When("patching the isolation segment fails with an error", func() {
   227  			BeforeEach(func() {
   228  				response := `{
   229  					"errors": [
   230  						{
   231  							"detail": "Organization not found",
   232  							"title": "CF-ResourceNotFound",
   233  							"code": 10010
   234  						}
   235  					]
   236  				}`
   237  
   238  				server.AppendHandlers(
   239  					CombineHandlers(
   240  						VerifyRequest(http.MethodPatch, "/v3/organizations/some-org-guid/relationships/default_isolation_segment"),
   241  						RespondWith(http.StatusNotFound, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   242  					),
   243  				)
   244  			})
   245  
   246  			It("returns the empty relationship, an error and warnings", func() {
   247  				relationship, warnings, err := client.UpdateOrganizationDefaultIsolationSegmentRelationship("some-org-guid", "some-isolation-segment-guid")
   248  				Expect(relationship).To(BeZero())
   249  				Expect(err).To(MatchError(ccerror.ResourceNotFoundError{
   250  					Message: "Organization not found",
   251  				}))
   252  				Expect(warnings).To(ConsistOf("this is a warning"))
   253  			})
   254  		})
   255  	})
   256  
   257  	Describe("DeleteIsolationSegmentOrganization", func() {
   258  		When("relationship exists", func() {
   259  			BeforeEach(func() {
   260  				server.AppendHandlers(
   261  					CombineHandlers(
   262  						VerifyRequest(http.MethodDelete, "/v3/isolation_segments/segment-guid/relationships/organizations/org-guid"),
   263  						RespondWith(http.StatusOK, "", http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   264  					),
   265  				)
   266  			})
   267  
   268  			It("revoke the relationship", func() {
   269  				warnings, err := client.DeleteIsolationSegmentOrganization("segment-guid", "org-guid")
   270  				Expect(err).ToNot(HaveOccurred())
   271  				Expect(warnings).To(ConsistOf("this is a warning"))
   272  
   273  				Expect(server.ReceivedRequests()).To(HaveLen(1))
   274  			})
   275  		})
   276  
   277  		When("an error occurs", func() {
   278  			BeforeEach(func() {
   279  				response := `{
   280  					"errors": [
   281  						{
   282  							"code": 10008,
   283  							"detail": "The request is semantically invalid: command presence",
   284  							"title": "CF-UnprocessableEntity"
   285  						},
   286  						{
   287  							"code": 10008,
   288  							"detail": "The request is semantically invalid: command presence",
   289  							"title": "CF-UnprocessableEntity"
   290  						}
   291  					]
   292  				}`
   293  
   294  				server.AppendHandlers(
   295  					CombineHandlers(
   296  						VerifyRequest(http.MethodDelete, "/v3/isolation_segments/segment-guid/relationships/organizations/org-guid"),
   297  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   298  					),
   299  				)
   300  			})
   301  
   302  			It("returns the error and warnings", func() {
   303  				warnings, err := client.DeleteIsolationSegmentOrganization("segment-guid", "org-guid")
   304  				Expect(err).To(MatchError(ccerror.MultiError{
   305  					ResponseCode: http.StatusTeapot,
   306  					Errors: []ccerror.V3Error{
   307  						{
   308  							Code:   10008,
   309  							Detail: "The request is semantically invalid: command presence",
   310  							Title:  "CF-UnprocessableEntity",
   311  						},
   312  						{
   313  							Code:   10008,
   314  							Detail: "The request is semantically invalid: command presence",
   315  							Title:  "CF-UnprocessableEntity",
   316  						},
   317  					},
   318  				}))
   319  				Expect(warnings).To(ConsistOf("this is a warning"))
   320  			})
   321  		})
   322  	})
   323  
   324  	Describe("SetApplicationDroplet", func() {
   325  		Context("it sets the droplet", func() {
   326  			BeforeEach(func() {
   327  				response := `
   328  {
   329    "data": {
   330      "guid": "some-droplet-guid"
   331    },
   332    "links": {
   333      "self": {
   334        "href": "https://api.example.org/v3/apps/some-app-guid/relationships/current_droplet"
   335      },
   336      "related": {
   337        "href": "https://api.example.org/v3/apps/some-app-guid/droplets/current"
   338      }
   339    }
   340  }`
   341  				requestBody := map[string]interface{}{
   342  					"data": map[string]string{
   343  						"guid": "some-droplet-guid",
   344  					},
   345  				}
   346  
   347  				server.AppendHandlers(
   348  					CombineHandlers(
   349  						VerifyRequest(http.MethodPatch, "/v3/apps/some-app-guid/relationships/current_droplet"),
   350  						VerifyJSONRepresenting(requestBody),
   351  						RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   352  					),
   353  				)
   354  			})
   355  
   356  			It("returns warnings and no error", func() {
   357  				relationship, warnings, err := client.SetApplicationDroplet("some-app-guid", "some-droplet-guid")
   358  				Expect(err).ToNot(HaveOccurred())
   359  				Expect(warnings).To(ConsistOf("this is a warning"))
   360  				Expect(relationship.GUID).To(Equal("some-droplet-guid"))
   361  			})
   362  		})
   363  
   364  		When("the CC returns an error", func() {
   365  			BeforeEach(func() {
   366  				response := `{
   367    "errors": [
   368      {
   369        "code": 10008,
   370        "detail": "The request is semantically invalid: command presence",
   371        "title": "CF-UnprocessableEntity"
   372      },
   373      {
   374        "code": 10010,
   375        "detail": "App not found",
   376        "title": "CF-ResourceNotFound"
   377      }
   378    ]
   379  }`
   380  				requestBody := map[string]interface{}{
   381  					"data": map[string]string{
   382  						"guid": "some-droplet-guid",
   383  					},
   384  				}
   385  
   386  				server.AppendHandlers(
   387  					CombineHandlers(
   388  						VerifyRequest(http.MethodPatch, "/v3/apps/no-such-app-guid/relationships/current_droplet"),
   389  						VerifyJSONRepresenting(requestBody),
   390  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   391  					),
   392  				)
   393  
   394  			})
   395  
   396  			It("returns the error and all warnings", func() {
   397  				_, warnings, err := client.SetApplicationDroplet("no-such-app-guid", "some-droplet-guid")
   398  				Expect(err).To(MatchError(ccerror.MultiError{
   399  					ResponseCode: http.StatusTeapot,
   400  					Errors: []ccerror.V3Error{
   401  						{
   402  							Code:   10008,
   403  							Detail: "The request is semantically invalid: command presence",
   404  							Title:  "CF-UnprocessableEntity",
   405  						},
   406  						{
   407  							Code:   10010,
   408  							Detail: "App not found",
   409  							Title:  "CF-ResourceNotFound",
   410  						},
   411  					},
   412  				}))
   413  				Expect(warnings).To(ConsistOf("this is a warning"))
   414  			})
   415  		})
   416  	})
   417  })