github.com/sleungcy/cli@v7.1.0+incompatible/api/cloudcontroller/ccv2/errors_test.go (about)

     1  package ccv2_test
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  
     7  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
     8  	. "code.cloudfoundry.org/cli/api/cloudcontroller/ccv2"
     9  
    10  	. "github.com/onsi/ginkgo"
    11  	. "github.com/onsi/gomega"
    12  	. "github.com/onsi/gomega/ghttp"
    13  )
    14  
    15  var _ = Describe("Error Wrapper", func() {
    16  	var (
    17  		serverResponse     string
    18  		serverResponseCode int
    19  		executeErr         error
    20  
    21  		client *Client
    22  	)
    23  
    24  	Describe("Make", func() {
    25  		BeforeEach(func() {
    26  			serverResponse = `{
    27  					"code": 777,
    28  					"description": "SomeCC Error Message",
    29  					"error_code": "CF-SomeError"
    30  				}`
    31  
    32  			client = NewTestClient()
    33  		})
    34  
    35  		JustBeforeEach(func() {
    36  			server.AppendHandlers(
    37  				CombineHandlers(
    38  					VerifyRequest(http.MethodGet, "/v2/apps"),
    39  					RespondWith(serverResponseCode, serverResponse, http.Header{
    40  						"X-Vcap-Request-Id": {
    41  							"6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95",
    42  							"6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95::7445d9db-c31e-410d-8dc5-9f79ec3fc26f",
    43  						},
    44  					},
    45  					),
    46  				),
    47  			)
    48  
    49  			// Make a request to a CC endpoint - we stub the response, so the actual endpoint isn't relevant
    50  			_, _, executeErr = client.GetApplications()
    51  		})
    52  
    53  		When("we can't unmarshal the response successfully", func() {
    54  			BeforeEach(func() {
    55  				serverResponse = "I am not unmarshallable"
    56  			})
    57  
    58  			When("response code is 4XX", func() {
    59  				BeforeEach(func() {
    60  					serverResponseCode = http.StatusNotFound
    61  				})
    62  
    63  				It("returns an unknown http source error", func() {
    64  					Expect(executeErr).To(MatchError(ccerror.UnknownHTTPSourceError{StatusCode: serverResponseCode, RawResponse: []byte(serverResponse)}))
    65  				})
    66  			})
    67  
    68  			When("response code is 5XX", func() {
    69  				BeforeEach(func() {
    70  					serverResponseCode = http.StatusBadGateway
    71  				})
    72  
    73  				It("returns an unknown http source error", func() {
    74  					Expect(executeErr).To(MatchError(ccerror.UnknownHTTPSourceError{StatusCode: serverResponseCode, RawResponse: []byte(serverResponse)}))
    75  				})
    76  			})
    77  		})
    78  
    79  		When("the error is from the cloud controller", func() {
    80  			When("the error is a 4XX error", func() {
    81  				Context("(400) Bad Request", func() {
    82  					BeforeEach(func() {
    83  						serverResponseCode = http.StatusBadRequest
    84  					})
    85  
    86  					Context("generic 400", func() {
    87  						BeforeEach(func() {
    88  							serverResponse = `{
    89  							"description": "bad request",
    90  							"error_code": "CF-BadRequest"
    91  						}`
    92  						})
    93  
    94  						It("returns a BadRequestError", func() {
    95  							Expect(executeErr).To(MatchError(ccerror.BadRequestError{
    96  								Message: "bad request",
    97  							}))
    98  						})
    99  					})
   100  
   101  					When("a not staged error is encountered", func() {
   102  						BeforeEach(func() {
   103  							serverResponse = `{
   104  								"description": "App has not finished staging",
   105  								"error_code": "CF-NotStaged"
   106  							}`
   107  						})
   108  
   109  						It("returns a NotStagedError", func() {
   110  							Expect(executeErr).To(MatchError(ccerror.NotStagedError{
   111  								Message: "App has not finished staging",
   112  							}))
   113  						})
   114  					})
   115  
   116  					When("an instances error is encountered", func() {
   117  						BeforeEach(func() {
   118  							serverResponse = `{
   119  								"description": "instances went bananas",
   120  								"error_code": "CF-InstancesError"
   121  							}`
   122  						})
   123  
   124  						It("returns an InstancesError", func() {
   125  							Expect(executeErr).To(MatchError(ccerror.InstancesError{
   126  								Message: "instances went bananas",
   127  							}))
   128  						})
   129  					})
   130  
   131  					When("creating a relation that is invalid", func() {
   132  						BeforeEach(func() {
   133  							serverResponse = `{
   134  							"code": 1002,
   135  							"description": "The requested app relation is invalid: the app and route must belong to the same space",
   136  							"error_code": "CF-InvalidRelation"
   137  						}`
   138  						})
   139  
   140  						It("returns an InvalidRelationError", func() {
   141  							Expect(executeErr).To(MatchError(ccerror.InvalidRelationError{
   142  								Message: "The requested app relation is invalid: the app and route must belong to the same space",
   143  							}))
   144  						})
   145  					})
   146  
   147  					Context("getting stats for a stopped app", func() {
   148  						BeforeEach(func() {
   149  							serverResponse = `{
   150  							"code": 200003,
   151  							"description": "Could not fetch stats for stopped app: some-app",
   152  							"error_code": "CF-AppStoppedStatsError"
   153  						}`
   154  						})
   155  
   156  						It("returns an AppStoppedStatsError", func() {
   157  							Expect(executeErr).To(MatchError(ccerror.ApplicationStoppedStatsError{
   158  								Message: "Could not fetch stats for stopped app: some-app",
   159  							}))
   160  						})
   161  					})
   162  
   163  					When("creating an invalid buildpack", func() {
   164  						BeforeEach(func() {
   165  							serverResponse = `{
   166  							 "description": "Buildpack is invalid: goofy is a dog",
   167  							 "error_code": "CF-BuildpackInvalid",
   168  							 "code": 290003
   169  						}`
   170  						})
   171  
   172  						It("returns an BuildpackInvalidError", func() {
   173  							Expect(executeErr).To(MatchError(ccerror.BuildpackInvalidError{
   174  								Message: "Buildpack is invalid: goofy is a dog",
   175  							}))
   176  						})
   177  					})
   178  
   179  					When("creating a buildpack causes a name collision", func() {
   180  						BeforeEach(func() {
   181  							serverResponse = `{
   182  							 "code": 290001,
   183  							 "description": "The buildpack name is already in use: foo",
   184  							 "error_code": "CF-BuildpackNameTaken"
   185  						}`
   186  						})
   187  
   188  						It("returns an BuildpackNameTakenError", func() {
   189  							Expect(executeErr).To(MatchError(ccerror.BuildpackNameTakenError{
   190  								Message: "The buildpack name is already in use: foo",
   191  							}))
   192  						})
   193  					})
   194  
   195  					When("creating an organization fails because the name is taken", func() {
   196  						BeforeEach(func() {
   197  							serverResponse = `{
   198  								"code": 30002,
   199  								"description": "The organization name is taken: potato",
   200  								"error_code": "CF-OrganizationNameTaken"
   201  							  }`
   202  						})
   203  
   204  						It("returns a OrganizationNameTakenError", func() {
   205  							Expect(executeErr).To(MatchError(ccerror.OrganizationNameTakenError{}))
   206  						})
   207  					})
   208  
   209  					When("creating a space fails because the name is taken", func() {
   210  						BeforeEach(func() {
   211  							serverResponse = `{
   212  								"code": 40002,
   213  								"description": "The app space name is taken: potato",
   214  								"error_code": "CF-SpaceNameTaken"
   215  							  }`
   216  						})
   217  
   218  						It("returns a SpaceNameTakenError", func() {
   219  							if e, ok := executeErr.(ccerror.UnknownHTTPSourceError); ok {
   220  								fmt.Printf("TV %s", string(e.RawResponse))
   221  							}
   222  							Expect(executeErr).To(MatchError(ccerror.SpaceNameTakenError{
   223  								Message: "The app space name is taken: potato",
   224  							}))
   225  						})
   226  					})
   227  
   228  					When("creating a service instance fails because the name is taken", func() {
   229  						BeforeEach(func() {
   230  							serverResponse = `{
   231  								"code": 40002,
   232  								"description": "Service instance name is taken: potato",
   233  								"error_code": "CF-ServiceInstanceNameTaken"
   234  							  }`
   235  						})
   236  
   237  						It("returns a ServiceInstanceNameTakenError", func() {
   238  							Expect(executeErr).To(MatchError(ccerror.ServiceInstanceNameTakenError{
   239  								Message: "Service instance name is taken: potato",
   240  							}))
   241  						})
   242  					})
   243  
   244  					When("creating a service plan visibility fails because it already exists", func() {
   245  						BeforeEach(func() {
   246  							serverResponse = `{
   247  								"code": 40002,
   248  								"description": "Service plan visibility already exists",
   249  								"error_code": "CF-ServicePlanVisibilityAlreadyExists"
   250  							  }`
   251  						})
   252  
   253  						It("returns a ServicePlanVisibilityExistsError", func() {
   254  							Expect(executeErr).To(MatchError(ccerror.ServicePlanVisibilityExistsError{
   255  								Message: "Service plan visibility already exists",
   256  							}))
   257  						})
   258  					})
   259  				})
   260  
   261  				Context("(401) Unauthorized", func() {
   262  					BeforeEach(func() {
   263  						serverResponseCode = http.StatusUnauthorized
   264  					})
   265  
   266  					Context("generic 401", func() {
   267  						It("returns a UnauthorizedError", func() {
   268  							Expect(executeErr).To(MatchError(ccerror.UnauthorizedError{Message: "SomeCC Error Message"}))
   269  						})
   270  					})
   271  
   272  					Context("invalid token", func() {
   273  						BeforeEach(func() {
   274  							serverResponse = `{
   275  						"code": 1000,
   276  						"description": "Invalid Auth Token",
   277  						"error_code": "CF-InvalidAuthToken"
   278  					}`
   279  						})
   280  
   281  						It("returns an InvalidAuthTokenError", func() {
   282  							Expect(executeErr).To(MatchError(ccerror.InvalidAuthTokenError{Message: "Invalid Auth Token"}))
   283  						})
   284  					})
   285  				})
   286  
   287  				Context("(403) Forbidden", func() {
   288  					BeforeEach(func() {
   289  						serverResponseCode = http.StatusForbidden
   290  					})
   291  
   292  					It("returns a ForbiddenError", func() {
   293  						Expect(executeErr).To(MatchError(ccerror.ForbiddenError{Message: "SomeCC Error Message"}))
   294  					})
   295  				})
   296  
   297  				Context("(404) Not Found", func() {
   298  					BeforeEach(func() {
   299  						serverResponseCode = http.StatusNotFound
   300  					})
   301  
   302  					When("the error is a json response from the cloud controller", func() {
   303  						It("returns a ResourceNotFoundError", func() {
   304  							Expect(executeErr).To(MatchError(ccerror.ResourceNotFoundError{Message: "SomeCC Error Message"}))
   305  						})
   306  					})
   307  				})
   308  
   309  				Context("(422) Unprocessable Entity", func() {
   310  					BeforeEach(func() {
   311  						serverResponseCode = http.StatusUnprocessableEntity
   312  					})
   313  
   314  					Context("generic Unprocessable entity", func() {
   315  						It("returns a UnprocessableEntityError", func() {
   316  							Expect(executeErr).To(MatchError(ccerror.UnprocessableEntityError{Message: "SomeCC Error Message"}))
   317  						})
   318  					})
   319  
   320  					When("creating a buildpack causes a name and stack collision", func() {
   321  						BeforeEach(func() {
   322  							serverResponse = `{
   323  							 "code": 290000,
   324  							 "description": "The buildpack name foo is already in use for the stack bar",
   325  							 "error_code": "CF-BuildpackNameStackTaken"
   326  						}`
   327  						})
   328  
   329  						It("returns an BuildpackAlreadyExistsForStackError", func() {
   330  							Expect(executeErr).To(MatchError(ccerror.BuildpackAlreadyExistsForStackError{
   331  								Message: "The buildpack name foo is already in use for the stack bar",
   332  							}))
   333  						})
   334  					})
   335  
   336  				})
   337  
   338  				Context("unhandled Error Codes", func() {
   339  					BeforeEach(func() {
   340  						serverResponseCode = http.StatusTeapot
   341  					})
   342  
   343  					It("returns an UnexpectedResponseError", func() {
   344  						Expect(executeErr).To(MatchError(ccerror.V2UnexpectedResponseError{
   345  							ResponseCode: http.StatusTeapot,
   346  							V2ErrorResponse: ccerror.V2ErrorResponse{
   347  								Code:        777,
   348  								Description: "SomeCC Error Message",
   349  								ErrorCode:   "CF-SomeError",
   350  							},
   351  							RequestIDs: []string{
   352  								"6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95",
   353  								"6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95::7445d9db-c31e-410d-8dc5-9f79ec3fc26f",
   354  							},
   355  						}))
   356  					})
   357  				})
   358  			})
   359  
   360  			When("the error is a 5XX error", func() {
   361  				BeforeEach(func() {
   362  					serverResponseCode = http.StatusInternalServerError
   363  					serverResponse = "{}"
   364  				})
   365  
   366  				Context("(502) Bad Gateway", func() {
   367  					BeforeEach(func() {
   368  						serverResponseCode = http.StatusBadGateway
   369  					})
   370  
   371  					When("the service broker catalog is invalid", func() {
   372  						BeforeEach(func() {
   373  							serverResponse = `{
   374  								"description": "Service broker catalog is invalid: \nService overview-service\n  Service dashboard client id must be unique\n",
   375  								"error_code": "CF-ServiceBrokerCatalogInvalid",
   376  								"code": 270012
   377  							}`
   378  						})
   379  
   380  						It("returns a ServiceBrokerCatalogInvalidError", func() {
   381  							Expect(executeErr).To(MatchError(ccerror.ServiceBrokerCatalogInvalidError{
   382  								Message: "Service broker catalog is invalid: \nService overview-service\n  Service dashboard client id must be unique\n",
   383  							}))
   384  						})
   385  					})
   386  
   387  					When("the service broker rejected the request", func() {
   388  						BeforeEach(func() {
   389  							serverResponse = `{
   390  								"description": "The service broker rejected the request to https://broker.example.com/v2/service_instances/1a3794e9-7ddf-4cae-b66a-6a0453c85a3e?accepts_incomplete=true. Status Code: 400 Bad Request, Body: instance requires property \"name\"",
   391  								"error_code": "CF-ServiceBrokerRequestRejected",
   392  								"code": 10001
   393  							}`
   394  						})
   395  
   396  						It("returns a ServiceBrokerRequestRejectedError", func() {
   397  							Expect(executeErr).To(MatchError(ccerror.ServiceBrokerRequestRejectedError{
   398  								Message: `The service broker rejected the request to https://broker.example.com/v2/service_instances/1a3794e9-7ddf-4cae-b66a-6a0453c85a3e?accepts_incomplete=true. Status Code: 400 Bad Request, Body: instance requires property "name"`,
   399  							}))
   400  						})
   401  					})
   402  
   403  					When("the service broker responded with bad response", func() {
   404  						BeforeEach(func() {
   405  							serverResponse = `{
   406  								"description": "Service broker error",
   407  									"error_code": "CF-ServiceBrokerBadResponse",
   408  									"code": 10001,
   409  									"http": {
   410  										"uri": "https://broker.url/v2/service_instances/593fe03a-3eda-4a53-93ed-aa30f309b120?accepts_incomplete=true",
   411  										"method": "PUT",
   412  										"status": 500
   413  									}
   414  								}`
   415  						})
   416  
   417  						It("returns a ServiceBrokerBadResponseError", func() {
   418  							Expect(executeErr).To(MatchError(ccerror.ServiceBrokerBadResponseError{
   419  								Message: `Service broker error`,
   420  							}))
   421  						})
   422  					})
   423  
   424  					When("the error_code is unknown", func() {
   425  						It("returns a V2UnexpectedResponseError with no json", func() {
   426  							Expect(executeErr).To(MatchError(ccerror.V2UnexpectedResponseError{
   427  								ResponseCode: http.StatusBadGateway,
   428  								V2ErrorResponse: ccerror.V2ErrorResponse{
   429  									Description: serverResponse,
   430  								},
   431  								RequestIDs: []string{
   432  									"6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95",
   433  									"6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95::7445d9db-c31e-410d-8dc5-9f79ec3fc26f",
   434  								},
   435  							}))
   436  						})
   437  					})
   438  				})
   439  
   440  				Context("unhandled error codes", func() {
   441  					It("returns a V2UnexpectedResponseError with no json", func() {
   442  						Expect(executeErr).To(MatchError(ccerror.V2UnexpectedResponseError{
   443  							ResponseCode: http.StatusInternalServerError,
   444  							V2ErrorResponse: ccerror.V2ErrorResponse{
   445  								Description: serverResponse,
   446  							},
   447  							RequestIDs: []string{
   448  								"6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95",
   449  								"6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95::7445d9db-c31e-410d-8dc5-9f79ec3fc26f",
   450  							},
   451  						}))
   452  					})
   453  				})
   454  			})
   455  		})
   456  	})
   457  })