github.com/ablease/cli@v6.37.1-0.20180613014814-3adbb7d7fb19+incompatible/api/cloudcontroller/ccv3/application_test.go (about)

     1  package ccv3_test
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"net/http"
     7  
     8  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
     9  	. "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3"
    10  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccv3/constant"
    11  	. "github.com/onsi/ginkgo"
    12  	. "github.com/onsi/gomega"
    13  	. "github.com/onsi/gomega/ghttp"
    14  )
    15  
    16  var _ = Describe("Application", func() {
    17  	var client *Client
    18  
    19  	BeforeEach(func() {
    20  		client = NewTestClient()
    21  	})
    22  
    23  	Describe("Application", func() {
    24  		Describe("MarshalJSON", func() {
    25  			var (
    26  				app      Application
    27  				appBytes []byte
    28  				err      error
    29  			)
    30  
    31  			BeforeEach(func() {
    32  				app = Application{}
    33  			})
    34  
    35  			JustBeforeEach(func() {
    36  				appBytes, err = app.MarshalJSON()
    37  				Expect(err).ToNot(HaveOccurred())
    38  			})
    39  
    40  			Context("when no lifecycle is provided", func() {
    41  				BeforeEach(func() {
    42  					app = Application{}
    43  				})
    44  
    45  				It("omits the lifecycle from the JSON", func() {
    46  					Expect(string(appBytes)).To(Equal("{}"))
    47  				})
    48  			})
    49  
    50  			Context("when lifecycle type docker is provided", func() {
    51  				BeforeEach(func() {
    52  					app = Application{
    53  						LifecycleType: constant.AppLifecycleTypeDocker,
    54  					}
    55  				})
    56  
    57  				It("sets lifecycle type to docker with empty data", func() {
    58  					Expect(string(appBytes)).To(MatchJSON(`{"lifecycle":{"type":"docker","data":{}}}`))
    59  				})
    60  			})
    61  
    62  			Context("when lifecycle type buildpack is provided", func() {
    63  				BeforeEach(func() {
    64  					app.LifecycleType = constant.AppLifecycleTypeBuildpack
    65  				})
    66  
    67  				Context("when no buildpacks are provided", func() {
    68  					It("omits the lifecycle from the JSON", func() {
    69  						Expect(string(appBytes)).To(Equal("{}"))
    70  					})
    71  				})
    72  
    73  				Context("when default buildpack is provided", func() {
    74  					BeforeEach(func() {
    75  						app.LifecycleBuildpacks = []string{"default"}
    76  					})
    77  
    78  					It("sets the lifecycle buildpack to be empty in the JSON", func() {
    79  						Expect(string(appBytes)).To(Equal(`{"lifecycle":{"data":{"buildpacks":null},"type":"buildpack"}}`))
    80  					})
    81  				})
    82  
    83  				Context("when null buildpack is provided", func() {
    84  					BeforeEach(func() {
    85  						app.LifecycleBuildpacks = []string{"null"}
    86  					})
    87  
    88  					It("sets the Lifecycle buildpack to be empty in the JSON", func() {
    89  						Expect(string(appBytes)).To(Equal(`{"lifecycle":{"data":{"buildpacks":null},"type":"buildpack"}}`))
    90  					})
    91  				})
    92  
    93  				Context("when other buildpacks are provided", func() {
    94  					BeforeEach(func() {
    95  						app.LifecycleBuildpacks = []string{"some-buildpack"}
    96  					})
    97  
    98  					It("sets them in the JSON", func() {
    99  						Expect(string(appBytes)).To(Equal(`{"lifecycle":{"data":{"buildpacks":["some-buildpack"]},"type":"buildpack"}}`))
   100  					})
   101  				})
   102  			})
   103  		})
   104  
   105  		Describe("UnmarshalJSON", func() {
   106  			var (
   107  				app      Application
   108  				appBytes []byte
   109  				err      error
   110  			)
   111  
   112  			BeforeEach(func() {
   113  				appBytes = []byte("{}")
   114  			})
   115  
   116  			JustBeforeEach(func() {
   117  				err = json.Unmarshal(appBytes, &app)
   118  				Expect(err).ToNot(HaveOccurred())
   119  			})
   120  
   121  			Context("when no lifecycle is provided", func() {
   122  				BeforeEach(func() {
   123  					appBytes = []byte("{}")
   124  				})
   125  
   126  				It("omits the lifecycle from the JSON", func() {
   127  					Expect(app).To(Equal(Application{}))
   128  				})
   129  			})
   130  
   131  			Context("when lifecycle type docker is provided", func() {
   132  				BeforeEach(func() {
   133  					appBytes = []byte(`{"lifecycle":{"type":"docker","data":{}}}`)
   134  				})
   135  				It("sets the lifecycle type to docker with empty data", func() {
   136  					Expect(app).To(Equal(Application{
   137  						LifecycleType: constant.AppLifecycleTypeDocker,
   138  					}))
   139  				})
   140  			})
   141  
   142  			Context("when lifecycle type buildpack is provided", func() {
   143  
   144  				Context("when other buildpacks are provided", func() {
   145  					BeforeEach(func() {
   146  						appBytes = []byte(`{"lifecycle":{"data":{"buildpacks":["some-buildpack"]},"type":"buildpack"}}`)
   147  					})
   148  
   149  					It("sets them in the JSON", func() {
   150  						Expect(app).To(Equal(Application{
   151  							LifecycleType:       constant.AppLifecycleTypeBuildpack,
   152  							LifecycleBuildpacks: []string{"some-buildpack"},
   153  						}))
   154  					})
   155  				})
   156  			})
   157  		})
   158  	})
   159  
   160  	Describe("CreateApplication", func() {
   161  		var (
   162  			appToCreate Application
   163  
   164  			createdApp Application
   165  			warnings   Warnings
   166  			executeErr error
   167  		)
   168  
   169  		JustBeforeEach(func() {
   170  			createdApp, warnings, executeErr = client.CreateApplication(appToCreate)
   171  		})
   172  
   173  		Context("when the application successfully is created", func() {
   174  			BeforeEach(func() {
   175  				response := `{
   176  					"guid": "some-app-guid",
   177  					"name": "some-app-name"
   178  				}`
   179  
   180  				expectedBody := map[string]interface{}{
   181  					"name": "some-app-name",
   182  					"relationships": map[string]interface{}{
   183  						"space": map[string]interface{}{
   184  							"data": map[string]string{
   185  								"guid": "some-space-guid",
   186  							},
   187  						},
   188  					},
   189  				}
   190  				server.AppendHandlers(
   191  					CombineHandlers(
   192  						VerifyRequest(http.MethodPost, "/v3/apps"),
   193  						VerifyJSONRepresenting(expectedBody),
   194  						RespondWith(http.StatusCreated, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   195  					),
   196  				)
   197  
   198  				appToCreate = Application{
   199  					Name: "some-app-name",
   200  					Relationships: Relationships{
   201  						constant.RelationshipTypeSpace: Relationship{GUID: "some-space-guid"},
   202  					},
   203  				}
   204  			})
   205  
   206  			It("returns the created app and warnings", func() {
   207  				Expect(executeErr).NotTo(HaveOccurred())
   208  				Expect(warnings).To(ConsistOf("this is a warning"))
   209  
   210  				Expect(createdApp).To(Equal(Application{
   211  					Name: "some-app-name",
   212  					GUID: "some-app-guid",
   213  				}))
   214  			})
   215  		})
   216  
   217  		Context("when the caller specifies a buildpack", func() {
   218  			BeforeEach(func() {
   219  				response := `{
   220  					"guid": "some-app-guid",
   221  					"name": "some-app-name",
   222  					"lifecycle": {
   223  						"type": "buildpack",
   224  						"data": {
   225  							"buildpacks": ["some-buildpack"]
   226  					  }
   227  					}
   228  				}`
   229  
   230  				expectedBody := map[string]interface{}{
   231  					"name": "some-app-name",
   232  					"lifecycle": map[string]interface{}{
   233  						"type": "buildpack",
   234  						"data": map[string]interface{}{
   235  							"buildpacks": []string{"some-buildpack"},
   236  						},
   237  					},
   238  					"relationships": map[string]interface{}{
   239  						"space": map[string]interface{}{
   240  							"data": map[string]string{
   241  								"guid": "some-space-guid",
   242  							},
   243  						},
   244  					},
   245  				}
   246  				server.AppendHandlers(
   247  					CombineHandlers(
   248  						VerifyRequest(http.MethodPost, "/v3/apps"),
   249  						VerifyJSONRepresenting(expectedBody),
   250  						RespondWith(http.StatusCreated, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   251  					),
   252  				)
   253  
   254  				appToCreate = Application{
   255  					Name:                "some-app-name",
   256  					LifecycleType:       constant.AppLifecycleTypeBuildpack,
   257  					LifecycleBuildpacks: []string{"some-buildpack"},
   258  					Relationships: Relationships{
   259  						constant.RelationshipTypeSpace: Relationship{GUID: "some-space-guid"},
   260  					},
   261  				}
   262  			})
   263  
   264  			It("returns the created app and warnings", func() {
   265  				Expect(executeErr).NotTo(HaveOccurred())
   266  				Expect(warnings).To(ConsistOf("this is a warning"))
   267  
   268  				Expect(createdApp).To(Equal(Application{
   269  					Name:                "some-app-name",
   270  					GUID:                "some-app-guid",
   271  					LifecycleType:       constant.AppLifecycleTypeBuildpack,
   272  					LifecycleBuildpacks: []string{"some-buildpack"},
   273  				}))
   274  			})
   275  		})
   276  
   277  		Context("when cc returns back an error or warnings", 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": 10010,
   288        "detail": "App not found",
   289        "title": "CF-ResourceNotFound"
   290      }
   291    ]
   292  }`
   293  				server.AppendHandlers(
   294  					CombineHandlers(
   295  						VerifyRequest(http.MethodPost, "/v3/apps"),
   296  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   297  					),
   298  				)
   299  			})
   300  
   301  			It("returns the error and all warnings", func() {
   302  				Expect(executeErr).To(MatchError(ccerror.MultiError{
   303  					ResponseCode: http.StatusTeapot,
   304  					Errors: []ccerror.V3Error{
   305  						{
   306  							Code:   10008,
   307  							Detail: "The request is semantically invalid: command presence",
   308  							Title:  "CF-UnprocessableEntity",
   309  						},
   310  						{
   311  							Code:   10010,
   312  							Detail: "App not found",
   313  							Title:  "CF-ResourceNotFound",
   314  						},
   315  					},
   316  				}))
   317  				Expect(warnings).To(ConsistOf("this is a warning"))
   318  			})
   319  		})
   320  	})
   321  
   322  	Describe("GetApplications", func() {
   323  		var (
   324  			filters []Query
   325  
   326  			apps       []Application
   327  			warnings   Warnings
   328  			executeErr error
   329  		)
   330  
   331  		JustBeforeEach(func() {
   332  			apps, warnings, executeErr = client.GetApplications(filters...)
   333  		})
   334  
   335  		Context("when applications exist", func() {
   336  			BeforeEach(func() {
   337  				response1 := fmt.Sprintf(`{
   338  	"pagination": {
   339  		"next": {
   340  			"href": "%s/v3/apps?space_guids=some-space-guid&names=some-app-name&page=2&per_page=2"
   341  		}
   342  	},
   343    "resources": [
   344      {
   345        "name": "app-name-1",
   346        "guid": "app-guid-1",
   347  			"lifecycle": {
   348  				"type": "buildpack",
   349  				"data": {
   350  					"buildpacks": ["some-buildpack"],
   351  					"stack": "some-stack"
   352  				}
   353  			}
   354      },
   355      {
   356        "name": "app-name-2",
   357        "guid": "app-guid-2"
   358      }
   359    ]
   360  }`, server.URL())
   361  				response2 := `{
   362  	"pagination": {
   363  		"next": null
   364  	},
   365  	"resources": [
   366  	  {
   367        "name": "app-name-3",
   368  		  "guid": "app-guid-3"
   369  		}
   370  	]
   371  }`
   372  				server.AppendHandlers(
   373  					CombineHandlers(
   374  						VerifyRequest(http.MethodGet, "/v3/apps", "space_guids=some-space-guid&names=some-app-name"),
   375  						RespondWith(http.StatusOK, response1, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   376  					),
   377  				)
   378  				server.AppendHandlers(
   379  					CombineHandlers(
   380  						VerifyRequest(http.MethodGet, "/v3/apps", "space_guids=some-space-guid&names=some-app-name&page=2&per_page=2"),
   381  						RespondWith(http.StatusOK, response2, http.Header{"X-Cf-Warnings": {"this is another warning"}}),
   382  					),
   383  				)
   384  
   385  				filters = []Query{
   386  					{Key: SpaceGUIDFilter, Values: []string{"some-space-guid"}},
   387  					{Key: NameFilter, Values: []string{"some-app-name"}},
   388  				}
   389  			})
   390  
   391  			It("returns the queried applications and all warnings", func() {
   392  				Expect(executeErr).NotTo(HaveOccurred())
   393  				Expect(warnings).To(ConsistOf("this is a warning", "this is another warning"))
   394  
   395  				Expect(apps).To(ConsistOf(
   396  					Application{
   397  						Name:                "app-name-1",
   398  						GUID:                "app-guid-1",
   399  						LifecycleType:       constant.AppLifecycleTypeBuildpack,
   400  						LifecycleBuildpacks: []string{"some-buildpack"},
   401  					},
   402  					Application{Name: "app-name-2", GUID: "app-guid-2"},
   403  					Application{Name: "app-name-3", GUID: "app-guid-3"},
   404  				))
   405  			})
   406  		})
   407  
   408  		Context("when the cloud controller returns errors and warnings", func() {
   409  			BeforeEach(func() {
   410  				response := `{
   411    "errors": [
   412      {
   413        "code": 10008,
   414        "detail": "The request is semantically invalid: command presence",
   415        "title": "CF-UnprocessableEntity"
   416      },
   417      {
   418        "code": 10010,
   419        "detail": "App not found",
   420        "title": "CF-ResourceNotFound"
   421      }
   422    ]
   423  }`
   424  				server.AppendHandlers(
   425  					CombineHandlers(
   426  						VerifyRequest(http.MethodGet, "/v3/apps"),
   427  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   428  					),
   429  				)
   430  			})
   431  
   432  			It("returns the error and all warnings", func() {
   433  				Expect(executeErr).To(MatchError(ccerror.MultiError{
   434  					ResponseCode: http.StatusTeapot,
   435  					Errors: []ccerror.V3Error{
   436  						{
   437  							Code:   10008,
   438  							Detail: "The request is semantically invalid: command presence",
   439  							Title:  "CF-UnprocessableEntity",
   440  						},
   441  						{
   442  							Code:   10010,
   443  							Detail: "App not found",
   444  							Title:  "CF-ResourceNotFound",
   445  						},
   446  					},
   447  				}))
   448  				Expect(warnings).To(ConsistOf("this is a warning"))
   449  			})
   450  		})
   451  	})
   452  
   453  	Describe("UpdateApplication", func() {
   454  		var (
   455  			appToUpdate Application
   456  
   457  			updatedApp Application
   458  			warnings   Warnings
   459  			executeErr error
   460  		)
   461  
   462  		JustBeforeEach(func() {
   463  			updatedApp, warnings, executeErr = client.UpdateApplication(appToUpdate)
   464  		})
   465  
   466  		Context("when the application successfully is updated", func() {
   467  			BeforeEach(func() {
   468  				response := `{
   469  					"guid": "some-app-guid",
   470  					"name": "some-app-name",
   471  					"lifecycle": {
   472  						"type": "buildpack",
   473  						"data": {
   474  							"buildpacks": ["some-buildpack"]
   475  						}
   476  					}
   477  				}`
   478  
   479  				expectedBody := map[string]interface{}{
   480  					"name": "some-app-name",
   481  					"lifecycle": map[string]interface{}{
   482  						"type": "buildpack",
   483  						"data": map[string]interface{}{
   484  							"buildpacks": []string{"some-buildpack"},
   485  						},
   486  					},
   487  					"relationships": map[string]interface{}{
   488  						"space": map[string]interface{}{
   489  							"data": map[string]string{
   490  								"guid": "some-space-guid",
   491  							},
   492  						},
   493  					},
   494  				}
   495  				server.AppendHandlers(
   496  					CombineHandlers(
   497  						VerifyRequest(http.MethodPatch, "/v3/apps/some-app-guid"),
   498  						VerifyJSONRepresenting(expectedBody),
   499  						RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   500  					),
   501  				)
   502  
   503  				appToUpdate = Application{
   504  					GUID:                "some-app-guid",
   505  					Name:                "some-app-name",
   506  					LifecycleType:       constant.AppLifecycleTypeBuildpack,
   507  					LifecycleBuildpacks: []string{"some-buildpack"},
   508  					Relationships: Relationships{
   509  						constant.RelationshipTypeSpace: Relationship{GUID: "some-space-guid"},
   510  					},
   511  				}
   512  			})
   513  
   514  			It("returns the updated app and warnings", func() {
   515  				Expect(executeErr).NotTo(HaveOccurred())
   516  				Expect(warnings).To(ConsistOf("this is a warning"))
   517  
   518  				Expect(updatedApp).To(Equal(Application{
   519  					GUID:                "some-app-guid",
   520  					LifecycleBuildpacks: []string{"some-buildpack"},
   521  					LifecycleType:       constant.AppLifecycleTypeBuildpack,
   522  					Name:                "some-app-name",
   523  				}))
   524  			})
   525  		})
   526  
   527  		Context("when cc returns back an error or warnings", func() {
   528  			BeforeEach(func() {
   529  				response := `{
   530    "errors": [
   531      {
   532        "code": 10008,
   533        "detail": "The request is semantically invalid: command presence",
   534        "title": "CF-UnprocessableEntity"
   535      },
   536      {
   537        "code": 10010,
   538        "detail": "App not found",
   539        "title": "CF-ResourceNotFound"
   540      }
   541    ]
   542  }`
   543  				server.AppendHandlers(
   544  					CombineHandlers(
   545  						VerifyRequest(http.MethodPatch, "/v3/apps/some-app-guid"),
   546  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   547  					),
   548  				)
   549  
   550  				appToUpdate = Application{
   551  					GUID: "some-app-guid",
   552  				}
   553  			})
   554  
   555  			It("returns the error and all warnings", func() {
   556  				Expect(executeErr).To(MatchError(ccerror.MultiError{
   557  					ResponseCode: http.StatusTeapot,
   558  					Errors: []ccerror.V3Error{
   559  						{
   560  							Code:   10008,
   561  							Detail: "The request is semantically invalid: command presence",
   562  							Title:  "CF-UnprocessableEntity",
   563  						},
   564  						{
   565  							Code:   10010,
   566  							Detail: "App not found",
   567  							Title:  "CF-ResourceNotFound",
   568  						},
   569  					},
   570  				}))
   571  				Expect(warnings).To(ConsistOf("this is a warning"))
   572  			})
   573  		})
   574  	})
   575  
   576  	Describe("UpdateApplicationStop", func() {
   577  		var (
   578  			responseApp Application
   579  			warnings    Warnings
   580  			executeErr  error
   581  		)
   582  
   583  		JustBeforeEach(func() {
   584  			responseApp, warnings, executeErr = client.UpdateApplicationStop("some-app-guid")
   585  		})
   586  
   587  		Context("when the response succeeds", func() {
   588  			BeforeEach(func() {
   589  				response := `
   590  {
   591  	"guid": "some-app-guid",
   592  	"name": "some-app",
   593  	"state": "STOPPED"
   594  }`
   595  				server.AppendHandlers(
   596  					CombineHandlers(
   597  						VerifyRequest(http.MethodPost, "/v3/apps/some-app-guid/actions/stop"),
   598  						RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   599  					),
   600  				)
   601  			})
   602  
   603  			It("returns the application, warnings, and no error", func() {
   604  				Expect(responseApp).To(Equal(Application{
   605  					GUID:  "some-app-guid",
   606  					Name:  "some-app",
   607  					State: constant.ApplicationStopped,
   608  				}))
   609  				Expect(executeErr).ToNot(HaveOccurred())
   610  				Expect(warnings).To(ConsistOf("this is a warning"))
   611  			})
   612  		})
   613  
   614  		Context("when the CC returns an error", func() {
   615  			BeforeEach(func() {
   616  				response := `{
   617    "errors": [
   618      {
   619        "code": 10008,
   620        "detail": "The request is semantically invalid: command presence",
   621        "title": "CF-UnprocessableEntity"
   622      },
   623      {
   624        "code": 10010,
   625        "detail": "App not found",
   626        "title": "CF-ResourceNotFound"
   627      }
   628    ]
   629  }`
   630  				server.AppendHandlers(
   631  					CombineHandlers(
   632  						VerifyRequest(http.MethodPost, "/v3/apps/some-app-guid/actions/stop"),
   633  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   634  					),
   635  				)
   636  			})
   637  
   638  			It("returns no app, the error and all warnings", func() {
   639  				Expect(executeErr).To(MatchError(ccerror.MultiError{
   640  					ResponseCode: http.StatusTeapot,
   641  					Errors: []ccerror.V3Error{
   642  						{
   643  							Code:   10008,
   644  							Detail: "The request is semantically invalid: command presence",
   645  							Title:  "CF-UnprocessableEntity",
   646  						},
   647  						{
   648  							Code:   10010,
   649  							Detail: "App not found",
   650  							Title:  "CF-ResourceNotFound",
   651  						},
   652  					},
   653  				}))
   654  				Expect(warnings).To(ConsistOf("this is a warning"))
   655  			})
   656  		})
   657  	})
   658  
   659  	Describe("UpdateApplicationStart", func() {
   660  		var (
   661  			app        Application
   662  			warnings   Warnings
   663  			executeErr error
   664  		)
   665  
   666  		JustBeforeEach(func() {
   667  			app, warnings, executeErr = client.UpdateApplicationStart("some-app-guid")
   668  		})
   669  
   670  		Context("when the response succeeds", func() {
   671  			BeforeEach(func() {
   672  				response := `
   673  {
   674  	"guid": "some-app-guid",
   675  	"name": "some-app"
   676  }`
   677  				server.AppendHandlers(
   678  					CombineHandlers(
   679  						VerifyRequest(http.MethodPost, "/v3/apps/some-app-guid/actions/start"),
   680  						RespondWith(http.StatusOK, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   681  					),
   682  				)
   683  			})
   684  
   685  			It("returns warnings and no error", func() {
   686  				Expect(executeErr).ToNot(HaveOccurred())
   687  				Expect(warnings).To(ConsistOf("this is a warning"))
   688  				Expect(app.GUID).To(Equal("some-app-guid"))
   689  			})
   690  		})
   691  
   692  		Context("when cc returns back an error or warnings", func() {
   693  			BeforeEach(func() {
   694  				response := `{
   695    "errors": [
   696      {
   697        "code": 10008,
   698        "detail": "The request is semantically invalid: command presence",
   699        "title": "CF-UnprocessableEntity"
   700      },
   701      {
   702        "code": 10010,
   703        "detail": "App not found",
   704        "title": "CF-ResourceNotFound"
   705      }
   706    ]
   707  }`
   708  				server.AppendHandlers(
   709  					CombineHandlers(
   710  						VerifyRequest(http.MethodPost, "/v3/apps/some-app-guid/actions/start"),
   711  						RespondWith(http.StatusTeapot, response, http.Header{"X-Cf-Warnings": {"this is a warning"}}),
   712  					),
   713  				)
   714  			})
   715  
   716  			It("returns the error and all warnings", func() {
   717  				Expect(executeErr).To(MatchError(ccerror.MultiError{
   718  					ResponseCode: http.StatusTeapot,
   719  					Errors: []ccerror.V3Error{
   720  						{
   721  							Code:   10008,
   722  							Detail: "The request is semantically invalid: command presence",
   723  							Title:  "CF-UnprocessableEntity",
   724  						},
   725  						{
   726  							Code:   10010,
   727  							Detail: "App not found",
   728  							Title:  "CF-ResourceNotFound",
   729  						},
   730  					},
   731  				}))
   732  				Expect(warnings).To(ConsistOf("this is a warning"))
   733  			})
   734  		})
   735  	})
   736  })