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 })