github.com/franc20/ayesa_sap@v7.0.0-beta.28.0.20200124003224-302d4d52fa6c+incompatible/api/cloudcontroller/ccv3/package_test.go (about) 1 package ccv3_test 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "io" 8 "io/ioutil" 9 "mime/multipart" 10 "net/http" 11 "os" 12 "strings" 13 14 "code.cloudfoundry.org/cli/api/cloudcontroller" 15 "code.cloudfoundry.org/cli/api/cloudcontroller/ccerror" 16 . "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3" 17 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/ccv3fakes" 18 "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant" 19 "code.cloudfoundry.org/cli/api/cloudcontroller/wrapper" 20 . "github.com/onsi/ginkgo" 21 . "github.com/onsi/gomega" 22 . "github.com/onsi/gomega/gbytes" 23 . "github.com/onsi/gomega/ghttp" 24 ) 25 26 var _ = Describe("Package", func() { 27 var client *Client 28 29 BeforeEach(func() { 30 client, _ = NewTestClient() 31 }) 32 33 Describe("CreatePackage", func() { 34 var ( 35 inputPackage Package 36 37 pkg Package 38 warnings Warnings 39 executeErr error 40 ) 41 42 JustBeforeEach(func() { 43 pkg, warnings, executeErr = client.CreatePackage(inputPackage) 44 }) 45 46 When("the package successfully is created", func() { 47 When("creating a docker package", func() { 48 BeforeEach(func() { 49 inputPackage = Package{ 50 Type: constant.PackageTypeDocker, 51 Relationships: Relationships{ 52 constant.RelationshipTypeApplication: Relationship{GUID: "some-app-guid"}, 53 }, 54 DockerImage: "some-docker-image", 55 DockerUsername: "some-username", 56 DockerPassword: "some-password", 57 } 58 59 response := `{ 60 "data": { 61 "image": "some-docker-image", 62 "username": "some-username", 63 "password": "some-password" 64 }, 65 "guid": "some-pkg-guid", 66 "type": "docker", 67 "state": "PROCESSING_UPLOAD", 68 "links": { 69 "upload": { 70 "href": "some-package-upload-url", 71 "method": "POST" 72 } 73 } 74 }` 75 76 expectedBody := map[string]interface{}{ 77 "type": "docker", 78 "data": map[string]string{ 79 "image": "some-docker-image", 80 "username": "some-username", 81 "password": "some-password", 82 }, 83 "relationships": map[string]interface{}{ 84 "app": map[string]interface{}{ 85 "data": map[string]string{ 86 "guid": "some-app-guid", 87 }, 88 }, 89 }, 90 } 91 server.AppendHandlers( 92 CombineHandlers( 93 VerifyRequest(http.MethodPost, "/v3/packages"), 94 VerifyJSONRepresenting(expectedBody), 95 RespondWith(http.StatusCreated, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 96 ), 97 ) 98 }) 99 100 It("returns the created package and warnings", func() { 101 Expect(executeErr).NotTo(HaveOccurred()) 102 Expect(warnings).To(ConsistOf("this is a warning")) 103 104 expectedPackage := Package{ 105 GUID: "some-pkg-guid", 106 Type: constant.PackageTypeDocker, 107 State: constant.PackageProcessingUpload, 108 Links: map[string]APILink{ 109 "upload": APILink{HREF: "some-package-upload-url", Method: http.MethodPost}, 110 }, 111 DockerImage: "some-docker-image", 112 DockerUsername: "some-username", 113 DockerPassword: "some-password", 114 } 115 Expect(pkg).To(Equal(expectedPackage)) 116 }) 117 }) 118 119 When("creating a bits package", func() { 120 BeforeEach(func() { 121 inputPackage = Package{ 122 Type: constant.PackageTypeBits, 123 Relationships: Relationships{ 124 constant.RelationshipTypeApplication: Relationship{GUID: "some-app-guid"}, 125 }, 126 } 127 response := `{ 128 "guid": "some-pkg-guid", 129 "type": "bits", 130 "state": "PROCESSING_UPLOAD", 131 "links": { 132 "upload": { 133 "href": "some-package-upload-url", 134 "method": "POST" 135 } 136 } 137 }` 138 139 expectedBody := map[string]interface{}{ 140 "type": "bits", 141 "relationships": map[string]interface{}{ 142 "app": map[string]interface{}{ 143 "data": map[string]string{ 144 "guid": "some-app-guid", 145 }, 146 }, 147 }, 148 } 149 server.AppendHandlers( 150 CombineHandlers( 151 VerifyRequest(http.MethodPost, "/v3/packages"), 152 VerifyJSONRepresenting(expectedBody), 153 RespondWith(http.StatusCreated, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 154 ), 155 ) 156 }) 157 158 It("omits data, and returns the created package and warnings", func() { 159 Expect(executeErr).NotTo(HaveOccurred()) 160 Expect(warnings).To(ConsistOf("this is a warning")) 161 162 expectedPackage := Package{ 163 GUID: "some-pkg-guid", 164 Type: constant.PackageTypeBits, 165 State: constant.PackageProcessingUpload, 166 Links: map[string]APILink{ 167 "upload": APILink{HREF: "some-package-upload-url", Method: http.MethodPost}, 168 }, 169 } 170 Expect(pkg).To(Equal(expectedPackage)) 171 }) 172 }) 173 }) 174 175 When("cc returns back an error or warnings", func() { 176 BeforeEach(func() { 177 inputPackage = Package{} 178 response := ` { 179 "errors": [ 180 { 181 "code": 10008, 182 "detail": "The request is semantically invalid: command presence", 183 "title": "CF-UnprocessableEntity" 184 }, 185 { 186 "code": 10010, 187 "detail": "Package not found", 188 "title": "CF-ResourceNotFound" 189 } 190 ] 191 }` 192 server.AppendHandlers( 193 CombineHandlers( 194 VerifyRequest(http.MethodPost, "/v3/packages"), 195 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 196 ), 197 ) 198 }) 199 200 It("returns the error and all warnings", func() { 201 Expect(executeErr).To(MatchError(ccerror.MultiError{ 202 ResponseCode: http.StatusTeapot, 203 Errors: []ccerror.V3Error{ 204 { 205 Code: 10008, 206 Detail: "The request is semantically invalid: command presence", 207 Title: "CF-UnprocessableEntity", 208 }, 209 { 210 Code: 10010, 211 Detail: "Package not found", 212 Title: "CF-ResourceNotFound", 213 }, 214 }, 215 })) 216 Expect(warnings).To(ConsistOf("this is a warning")) 217 }) 218 }) 219 }) 220 221 Describe("GetPackage", func() { 222 var ( 223 pkg Package 224 warnings Warnings 225 executeErr error 226 ) 227 228 JustBeforeEach(func() { 229 pkg, warnings, executeErr = client.GetPackage("some-pkg-guid") 230 }) 231 232 When("the package exists", func() { 233 BeforeEach(func() { 234 response := `{ 235 "guid": "some-pkg-guid", 236 "state": "PROCESSING_UPLOAD", 237 "links": { 238 "upload": { 239 "href": "some-package-upload-url", 240 "method": "POST" 241 } 242 } 243 }` 244 server.AppendHandlers( 245 CombineHandlers( 246 VerifyRequest(http.MethodGet, "/v3/packages/some-pkg-guid"), 247 RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 248 ), 249 ) 250 }) 251 252 It("returns the queried package and all warnings", func() { 253 Expect(executeErr).NotTo(HaveOccurred()) 254 255 expectedPackage := Package{ 256 GUID: "some-pkg-guid", 257 State: constant.PackageProcessingUpload, 258 Links: map[string]APILink{ 259 "upload": APILink{HREF: "some-package-upload-url", Method: http.MethodPost}, 260 }, 261 } 262 Expect(pkg).To(Equal(expectedPackage)) 263 Expect(warnings).To(ConsistOf("this is a warning")) 264 }) 265 }) 266 267 When("the cloud controller returns errors and warnings", func() { 268 BeforeEach(func() { 269 response := `{ 270 "errors": [ 271 { 272 "code": 10008, 273 "detail": "The request is semantically invalid: command presence", 274 "title": "CF-UnprocessableEntity" 275 }, 276 { 277 "code": 10010, 278 "detail": "Package not found", 279 "title": "CF-ResourceNotFound" 280 } 281 ] 282 }` 283 server.AppendHandlers( 284 CombineHandlers( 285 VerifyRequest(http.MethodGet, "/v3/packages/some-pkg-guid"), 286 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 287 ), 288 ) 289 }) 290 291 It("returns the error and all warnings", func() { 292 Expect(executeErr).To(MatchError(ccerror.MultiError{ 293 ResponseCode: http.StatusTeapot, 294 Errors: []ccerror.V3Error{ 295 { 296 Code: 10008, 297 Detail: "The request is semantically invalid: command presence", 298 Title: "CF-UnprocessableEntity", 299 }, 300 { 301 Code: 10010, 302 Detail: "Package not found", 303 Title: "CF-ResourceNotFound", 304 }, 305 }, 306 })) 307 Expect(warnings).To(ConsistOf("this is a warning")) 308 }) 309 }) 310 }) 311 312 Describe("GetPackages", func() { 313 var ( 314 pkgs []Package 315 warnings Warnings 316 executeErr error 317 ) 318 319 JustBeforeEach(func() { 320 pkgs, warnings, executeErr = client.GetPackages(Query{Key: AppGUIDFilter, Values: []string{"some-app-guid"}}) 321 }) 322 323 When("cloud controller returns list of packages", func() { 324 BeforeEach(func() { 325 response := `{ 326 "resources": [ 327 { 328 "guid": "some-pkg-guid-1", 329 "type": "bits", 330 "state": "PROCESSING_UPLOAD", 331 "created_at": "2017-08-14T21:16:12Z", 332 "links": { 333 "upload": { 334 "href": "some-pkg-upload-url-1", 335 "method": "POST" 336 } 337 } 338 }, 339 { 340 "guid": "some-pkg-guid-2", 341 "type": "bits", 342 "state": "READY", 343 "created_at": "2017-08-14T21:20:13Z", 344 "links": { 345 "upload": { 346 "href": "some-pkg-upload-url-2", 347 "method": "POST" 348 } 349 } 350 } 351 ] 352 }` 353 server.AppendHandlers( 354 CombineHandlers( 355 VerifyRequest(http.MethodGet, "/v3/packages", "app_guids=some-app-guid"), 356 RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 357 ), 358 ) 359 }) 360 361 It("returns the queried packages and all warnings", func() { 362 Expect(executeErr).NotTo(HaveOccurred()) 363 364 Expect(pkgs).To(Equal([]Package{ 365 { 366 GUID: "some-pkg-guid-1", 367 Type: constant.PackageTypeBits, 368 State: constant.PackageProcessingUpload, 369 CreatedAt: "2017-08-14T21:16:12Z", 370 Links: map[string]APILink{ 371 "upload": APILink{HREF: "some-pkg-upload-url-1", Method: http.MethodPost}, 372 }, 373 }, 374 { 375 GUID: "some-pkg-guid-2", 376 Type: constant.PackageTypeBits, 377 State: constant.PackageReady, 378 CreatedAt: "2017-08-14T21:20:13Z", 379 Links: map[string]APILink{ 380 "upload": APILink{HREF: "some-pkg-upload-url-2", Method: http.MethodPost}, 381 }, 382 }, 383 })) 384 Expect(warnings).To(ConsistOf("this is a warning")) 385 }) 386 }) 387 388 When("the cloud controller returns errors and warnings", func() { 389 BeforeEach(func() { 390 response := `{ 391 "errors": [ 392 { 393 "code": 10008, 394 "detail": "The request is semantically invalid: command presence", 395 "title": "CF-UnprocessableEntity" 396 }, 397 { 398 "code": 10010, 399 "detail": "Package not found", 400 "title": "CF-ResourceNotFound" 401 } 402 ] 403 }` 404 server.AppendHandlers( 405 CombineHandlers( 406 VerifyRequest(http.MethodGet, "/v3/packages", "app_guids=some-app-guid"), 407 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 408 ), 409 ) 410 }) 411 412 It("returns the error and all warnings", func() { 413 Expect(executeErr).To(MatchError(ccerror.MultiError{ 414 ResponseCode: http.StatusTeapot, 415 Errors: []ccerror.V3Error{ 416 { 417 Code: 10008, 418 Detail: "The request is semantically invalid: command presence", 419 Title: "CF-UnprocessableEntity", 420 }, 421 { 422 Code: 10010, 423 Detail: "Package not found", 424 Title: "CF-ResourceNotFound", 425 }, 426 }, 427 })) 428 Expect(warnings).To(ConsistOf("this is a warning")) 429 }) 430 }) 431 }) 432 433 Describe("UploadBitsPackage", func() { 434 var ( 435 inputPackage Package 436 ) 437 438 BeforeEach(func() { 439 client, _ = NewTestClient() 440 441 inputPackage = Package{ 442 GUID: "package-guid", 443 } 444 }) 445 446 When("the upload is successful", func() { 447 var ( 448 resources []Resource 449 readerBody []byte 450 verifyHeaderAndBody func(http.ResponseWriter, *http.Request) 451 ) 452 453 BeforeEach(func() { 454 resources = []Resource{ 455 {FilePath: "foo"}, 456 {FilePath: "bar"}, 457 } 458 459 response := `{ 460 "guid": "some-package-guid", 461 "type": "bits", 462 "state": "PROCESSING_UPLOAD" 463 }` 464 465 server.AppendHandlers( 466 CombineHandlers( 467 VerifyRequest(http.MethodPost, "/v3/packages/package-guid/upload"), 468 func(writer http.ResponseWriter, req *http.Request) { 469 verifyHeaderAndBody(writer, req) 470 }, 471 RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 472 ), 473 ) 474 }) 475 476 When("the upload has application bits to upload", func() { 477 var reader io.Reader 478 479 BeforeEach(func() { 480 readerBody = []byte("hello world") 481 reader = bytes.NewReader(readerBody) 482 483 verifyHeaderAndBody = func(_ http.ResponseWriter, req *http.Request) { 484 contentType := req.Header.Get("Content-Type") 485 Expect(contentType).To(MatchRegexp("multipart/form-data; boundary=[\\w\\d]+")) 486 487 defer req.Body.Close() 488 requestReader := multipart.NewReader(req.Body, contentType[30:]) 489 490 // Verify that matched resources are sent properly 491 resourcesPart, err := requestReader.NextPart() 492 Expect(err).NotTo(HaveOccurred()) 493 494 Expect(resourcesPart.FormName()).To(Equal("resources")) 495 496 defer resourcesPart.Close() 497 expectedJSON, err := json.Marshal(resources) 498 Expect(err).NotTo(HaveOccurred()) 499 Expect(ioutil.ReadAll(resourcesPart)).To(MatchJSON(expectedJSON)) 500 501 // Verify that the application bits are sent properly 502 resourcesPart, err = requestReader.NextPart() 503 Expect(err).NotTo(HaveOccurred()) 504 505 Expect(resourcesPart.FormName()).To(Equal("bits")) 506 Expect(resourcesPart.FileName()).To(Equal("package.zip")) 507 508 defer resourcesPart.Close() 509 Expect(ioutil.ReadAll(resourcesPart)).To(Equal(readerBody)) 510 } 511 }) 512 513 It("returns the created job and warnings", func() { 514 pkg, warnings, err := client.UploadBitsPackage(inputPackage, resources, reader, int64(len(readerBody))) 515 Expect(err).NotTo(HaveOccurred()) 516 Expect(warnings).To(ConsistOf("this is a warning")) 517 Expect(pkg).To(Equal(Package{ 518 GUID: "some-package-guid", 519 Type: constant.PackageTypeBits, 520 State: constant.PackageProcessingUpload, 521 })) 522 }) 523 }) 524 525 When("there are no application bits to upload", func() { 526 BeforeEach(func() { 527 verifyHeaderAndBody = func(_ http.ResponseWriter, req *http.Request) { 528 contentType := req.Header.Get("Content-Type") 529 Expect(contentType).To(MatchRegexp("multipart/form-data; boundary=[\\w\\d]+")) 530 531 defer req.Body.Close() 532 requestReader := multipart.NewReader(req.Body, contentType[30:]) 533 534 // Verify that matched resources are sent properly 535 resourcesPart, err := requestReader.NextPart() 536 Expect(err).NotTo(HaveOccurred()) 537 538 Expect(resourcesPart.FormName()).To(Equal("resources")) 539 540 defer resourcesPart.Close() 541 expectedJSON, err := json.Marshal(resources) 542 Expect(err).NotTo(HaveOccurred()) 543 Expect(ioutil.ReadAll(resourcesPart)).To(MatchJSON(expectedJSON)) 544 545 // Verify that the application bits are not sent 546 _, err = requestReader.NextPart() 547 Expect(err).To(MatchError(io.EOF)) 548 } 549 }) 550 551 It("does not send the application bits", func() { 552 pkg, warnings, err := client.UploadBitsPackage(inputPackage, resources, nil, 33513531353) 553 Expect(err).NotTo(HaveOccurred()) 554 Expect(warnings).To(ConsistOf("this is a warning")) 555 Expect(pkg).To(Equal(Package{ 556 GUID: "some-package-guid", 557 Type: constant.PackageTypeBits, 558 State: constant.PackageProcessingUpload, 559 })) 560 }) 561 }) 562 }) 563 564 When("the CC returns an error", func() { 565 BeforeEach(func() { 566 response := ` { 567 "errors": [ 568 { 569 "code": 10008, 570 "detail": "Banana", 571 "title": "CF-Banana" 572 } 573 ] 574 }` 575 576 server.AppendHandlers( 577 CombineHandlers( 578 VerifyRequest(http.MethodPost, "/v3/packages/package-guid/upload"), 579 RespondWith(http.StatusNotFound, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 580 ), 581 ) 582 }) 583 584 It("returns the error", func() { 585 _, warnings, err := client.UploadBitsPackage(inputPackage, []Resource{}, bytes.NewReader(nil), 0) 586 Expect(err).To(MatchError(ccerror.ResourceNotFoundError{Message: "Banana"})) 587 Expect(warnings).To(ConsistOf("this is a warning")) 588 }) 589 }) 590 591 When("passed a nil resources", func() { 592 It("returns a NilObjectError", func() { 593 _, _, err := client.UploadBitsPackage(inputPackage, nil, bytes.NewReader(nil), 0) 594 Expect(err).To(MatchError(ccerror.NilObjectError{Object: "matchedResources"})) 595 }) 596 }) 597 598 When("an error is returned from the new resources reader", func() { 599 var ( 600 fakeReader *ccv3fakes.FakeReader 601 expectedErr error 602 ) 603 604 BeforeEach(func() { 605 expectedErr = errors.New("some read error") 606 fakeReader = new(ccv3fakes.FakeReader) 607 fakeReader.ReadReturns(0, expectedErr) 608 609 server.AppendHandlers( 610 VerifyRequest(http.MethodPost, "/v3/packages/package-guid/upload"), 611 ) 612 }) 613 614 It("returns the error", func() { 615 _, _, err := client.UploadBitsPackage(inputPackage, []Resource{}, fakeReader, 3) 616 Expect(err).To(MatchError(expectedErr)) 617 }) 618 }) 619 620 When("a retryable error occurs", func() { 621 BeforeEach(func() { 622 wrapper := &wrapper.CustomWrapper{ 623 CustomMake: func(connection cloudcontroller.Connection, request *cloudcontroller.Request, response *cloudcontroller.Response) error { 624 defer GinkgoRecover() // Since this will be running in a thread 625 626 if strings.HasSuffix(request.URL.String(), "/v3/packages/package-guid/upload") { 627 _, err := ioutil.ReadAll(request.Body) 628 Expect(err).ToNot(HaveOccurred()) 629 Expect(request.Body.Close()).ToNot(HaveOccurred()) 630 return request.ResetBody() 631 } 632 return connection.Make(request, response) 633 }, 634 } 635 636 client, _ = NewTestClient(Config{Wrappers: []ConnectionWrapper{wrapper}}) 637 }) 638 639 It("returns the PipeSeekError", func() { 640 _, _, err := client.UploadBitsPackage(inputPackage, []Resource{}, strings.NewReader("hello world"), 3) 641 Expect(err).To(MatchError(ccerror.PipeSeekError{})) 642 }) 643 }) 644 645 When("an http error occurs mid-transfer", func() { 646 var expectedErr error 647 const UploadSize = 33 * 1024 648 649 BeforeEach(func() { 650 expectedErr = errors.New("some read error") 651 652 wrapper := &wrapper.CustomWrapper{ 653 CustomMake: func(connection cloudcontroller.Connection, request *cloudcontroller.Request, response *cloudcontroller.Response) error { 654 defer GinkgoRecover() // Since this will be running in a thread 655 656 if strings.HasSuffix(request.URL.String(), "/v3/packages/package-guid/upload") { 657 defer request.Body.Close() 658 readBytes, err := ioutil.ReadAll(request.Body) 659 Expect(err).ToNot(HaveOccurred()) 660 Expect(len(readBytes)).To(BeNumerically(">", UploadSize)) 661 return expectedErr 662 } 663 return connection.Make(request, response) 664 }, 665 } 666 667 client, _ = NewTestClient(Config{Wrappers: []ConnectionWrapper{wrapper}}) 668 }) 669 670 It("returns the http error", func() { 671 _, _, err := client.UploadBitsPackage(inputPackage, []Resource{}, strings.NewReader(strings.Repeat("a", UploadSize)), 3) 672 Expect(err).To(MatchError(expectedErr)) 673 }) 674 }) 675 }) 676 677 Describe("UploadPackage", func() { 678 var ( 679 inputPackage Package 680 fileToUpload string 681 682 pkg Package 683 warnings Warnings 684 executeErr error 685 ) 686 687 JustBeforeEach(func() { 688 pkg, warnings, executeErr = client.UploadPackage(inputPackage, fileToUpload) 689 }) 690 691 When("the package successfully is created", func() { 692 var tempFile *os.File 693 694 BeforeEach(func() { 695 var err error 696 697 inputPackage = Package{ 698 State: constant.PackageAwaitingUpload, 699 GUID: "package-guid", 700 } 701 702 tempFile, err = ioutil.TempFile("", "package-upload") 703 Expect(err).ToNot(HaveOccurred()) 704 defer tempFile.Close() 705 706 fileToUpload = tempFile.Name() 707 708 fileSize := 1024 709 contents := strings.Repeat("A", fileSize) 710 err = ioutil.WriteFile(tempFile.Name(), []byte(contents), 0666) 711 Expect(err).NotTo(HaveOccurred()) 712 713 verifyHeaderAndBody := func(_ http.ResponseWriter, req *http.Request) { 714 contentType := req.Header.Get("Content-Type") 715 Expect(contentType).To(MatchRegexp("multipart/form-data; boundary=[\\w\\d]+")) 716 717 boundary := contentType[30:] 718 719 defer req.Body.Close() 720 rawBody, err := ioutil.ReadAll(req.Body) 721 Expect(err).NotTo(HaveOccurred()) 722 body := BufferWithBytes(rawBody) 723 Expect(body).To(Say("--%s", boundary)) 724 Expect(body).To(Say(`name="bits"`)) 725 Expect(body).To(Say(contents)) 726 Expect(body).To(Say("--%s--", boundary)) 727 } 728 729 response := `{ 730 "guid": "some-pkg-guid", 731 "state": "PROCESSING_UPLOAD", 732 "links": { 733 "upload": { 734 "href": "some-package-upload-url", 735 "method": "POST" 736 } 737 } 738 }` 739 740 server.AppendHandlers( 741 CombineHandlers( 742 VerifyRequest(http.MethodPost, "/v3/packages/package-guid/upload"), 743 verifyHeaderAndBody, 744 RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 745 ), 746 ) 747 }) 748 749 AfterEach(func() { 750 if tempFile != nil { 751 Expect(os.RemoveAll(tempFile.Name())).ToNot(HaveOccurred()) 752 } 753 }) 754 755 It("returns the created package and warnings", func() { 756 Expect(executeErr).NotTo(HaveOccurred()) 757 758 expectedPackage := Package{ 759 GUID: "some-pkg-guid", 760 State: constant.PackageProcessingUpload, 761 Links: map[string]APILink{ 762 "upload": APILink{HREF: "some-package-upload-url", Method: http.MethodPost}, 763 }, 764 } 765 Expect(pkg).To(Equal(expectedPackage)) 766 Expect(warnings).To(ConsistOf("this is a warning")) 767 }) 768 }) 769 770 When("cc returns back an error or warnings", func() { 771 var tempFile *os.File 772 773 BeforeEach(func() { 774 var err error 775 776 inputPackage = Package{ 777 GUID: "package-guid", 778 State: constant.PackageAwaitingUpload, 779 } 780 781 tempFile, err = ioutil.TempFile("", "package-upload") 782 Expect(err).ToNot(HaveOccurred()) 783 defer tempFile.Close() 784 785 fileToUpload = tempFile.Name() 786 787 fileSize := 1024 788 contents := strings.Repeat("A", fileSize) 789 err = ioutil.WriteFile(tempFile.Name(), []byte(contents), 0666) 790 Expect(err).NotTo(HaveOccurred()) 791 792 response := ` { 793 "errors": [ 794 { 795 "code": 10008, 796 "detail": "The request is semantically invalid: command presence", 797 "title": "CF-UnprocessableEntity" 798 }, 799 { 800 "code": 10008, 801 "detail": "The request is semantically invalid: command presence", 802 "title": "CF-UnprocessableEntity" 803 } 804 ] 805 }` 806 807 server.AppendHandlers( 808 CombineHandlers( 809 VerifyRequest(http.MethodPost, "/v3/packages/package-guid/upload"), 810 RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}), 811 ), 812 ) 813 }) 814 815 AfterEach(func() { 816 if tempFile != nil { 817 Expect(os.RemoveAll(tempFile.Name())).ToNot(HaveOccurred()) 818 } 819 }) 820 821 It("returns the error and all warnings", func() { 822 Expect(executeErr).To(MatchError(ccerror.MultiError{ 823 ResponseCode: http.StatusTeapot, 824 Errors: []ccerror.V3Error{ 825 { 826 Code: 10008, 827 Detail: "The request is semantically invalid: command presence", 828 Title: "CF-UnprocessableEntity", 829 }, 830 { 831 Code: 10008, 832 Detail: "The request is semantically invalid: command presence", 833 Title: "CF-UnprocessableEntity", 834 }, 835 }, 836 })) 837 Expect(warnings).To(ConsistOf("this is a warning")) 838 }) 839 840 }) 841 }) 842 })