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