github.com/LukasHeimann/cloudfoundrycli@v7.1.0+incompatible/api/cloudcontroller/ccv3/errors_test.go (about)

     1  package ccv3_test
     2  
     3  import (
     4  	"net/http"
     5  
     6  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
     7  	. "code.cloudfoundry.org/cli/api/cloudcontroller/ccv3"
     8  
     9  	. "github.com/onsi/ginkgo"
    10  	. "github.com/onsi/gomega"
    11  	. "github.com/onsi/gomega/ghttp"
    12  )
    13  
    14  var _ = Describe("Error Wrapper", func() {
    15  	var client *Client
    16  
    17  	BeforeEach(func() {
    18  		client, _ = NewTestClient()
    19  	})
    20  
    21  	Describe("Make", func() {
    22  		var (
    23  			serverResponse     string
    24  			serverResponseCode int
    25  			makeError          error
    26  		)
    27  
    28  		BeforeEach(func() {
    29  			serverResponse = `
    30  {
    31    "errors": [
    32      {
    33        "code": 777,
    34        "detail": "SomeCC Error Message",
    35        "title": "CF-SomeError"
    36      }
    37    ]
    38  }`
    39  
    40  		})
    41  
    42  		JustBeforeEach(func() {
    43  			server.AppendHandlers(
    44  				CombineHandlers(
    45  					VerifyRequest(http.MethodGet, "/v3/apps"),
    46  					RespondWith(serverResponseCode, serverResponse, http.Header{
    47  						"X-Vcap-Request-Id": {
    48  							"6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95",
    49  							"6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95::7445d9db-c31e-410d-8dc5-9f79ec3fc26f",
    50  						},
    51  					},
    52  					),
    53  				),
    54  			)
    55  
    56  			_, _, makeError = client.GetApplications()
    57  		})
    58  
    59  		When("we can't unmarshal the response successfully", func() {
    60  			BeforeEach(func() {
    61  				serverResponse = "I am not unmarshallable"
    62  				serverResponseCode = http.StatusNotFound
    63  			})
    64  
    65  			It("returns an unknown http source error", func() {
    66  				Expect(makeError).To(MatchError(ccerror.UnknownHTTPSourceError{StatusCode: serverResponseCode, RawResponse: []byte(serverResponse)}))
    67  			})
    68  		})
    69  
    70  		When("the error is from the cloud controller", func() {
    71  			When("an empty list of errors is returned", func() {
    72  				BeforeEach(func() {
    73  					serverResponseCode = http.StatusUnauthorized
    74  					serverResponse = `{ "errors": [] }`
    75  				})
    76  
    77  				It("returns an UnexpectedResponseError", func() {
    78  					Expect(makeError).To(MatchError(ccerror.V3UnexpectedResponseError{
    79  						ResponseCode:    http.StatusUnauthorized,
    80  						V3ErrorResponse: ccerror.V3ErrorResponse{Errors: []ccerror.V3Error{}},
    81  					}))
    82  				})
    83  			})
    84  
    85  			When("the error is a 4XX error", func() {
    86  				Context("(400) Bad Request", func() {
    87  					BeforeEach(func() {
    88  						serverResponseCode = http.StatusBadRequest
    89  					})
    90  
    91  					When("the query parameter is invalid", func() {
    92  						BeforeEach(func() {
    93  							serverResponse = `
    94  {
    95     "errors": [
    96        {
    97           "detail": "The query parameter is invalid: Missing label_selector value",
    98           "title": "CF-BadQueryParameter",
    99           "code": 10005
   100        }
   101     ]
   102  }`
   103  						})
   104  
   105  						It("returns a BadRequestError", func() {
   106  							Expect(makeError).To(MatchError(ccerror.BadRequestError{Message: "The query parameter is invalid: Missing label_selector value"}))
   107  						})
   108  
   109  					})
   110  				})
   111  				Context("(401) Unauthorized", func() {
   112  					BeforeEach(func() {
   113  						serverResponseCode = http.StatusUnauthorized
   114  					})
   115  
   116  					Context("generic 401", func() {
   117  						It("returns a UnauthorizedError", func() {
   118  							Expect(makeError).To(MatchError(ccerror.UnauthorizedError{Message: "SomeCC Error Message"}))
   119  						})
   120  					})
   121  
   122  					Context("invalid token", func() {
   123  						BeforeEach(func() {
   124  							serverResponse = `{
   125  							"errors": [
   126  								{
   127  									"code": 1000,
   128  									"detail": "Invalid Auth Token",
   129  									"title": "CF-InvalidAuthToken"
   130  								}
   131  							]
   132  						}`
   133  						})
   134  
   135  						It("returns an InvalidAuthTokenError", func() {
   136  							Expect(makeError).To(MatchError(ccerror.InvalidAuthTokenError{Message: "Invalid Auth Token"}))
   137  						})
   138  					})
   139  				})
   140  
   141  				Context("(403) Forbidden", func() {
   142  					BeforeEach(func() {
   143  						serverResponseCode = http.StatusForbidden
   144  					})
   145  
   146  					It("returns a ForbiddenError", func() {
   147  						Expect(makeError).To(MatchError(ccerror.ForbiddenError{Message: "SomeCC Error Message"}))
   148  					})
   149  				})
   150  
   151  				Context("(404) Not Found", func() {
   152  					BeforeEach(func() {
   153  						serverResponseCode = http.StatusNotFound
   154  					})
   155  
   156  					Context("API is not found", func() {
   157  
   158  						BeforeEach(func() {
   159  							serverResponse = `{
   160  								"errors": [
   161  									{
   162  										"detail": "Unknown request",
   163  										"title": "CF-NotFound",
   164  										"code": 10000
   165  									}
   166  								]
   167  							}`
   168  						})
   169  
   170  						It("returns a APINotFoundError", func() {
   171  							Expect(makeError).To(MatchError(ccerror.APINotFoundError{URL: server.URL() + "/v3/apps"}))
   172  						})
   173  					})
   174  
   175  					When("a process is not found", func() {
   176  						BeforeEach(func() {
   177  							serverResponse = `
   178  {
   179    "errors": [
   180      {
   181        "code": 10010,
   182        "detail": "Process not found",
   183        "title": "CF-ResourceNotFound"
   184      }
   185    ]
   186  }`
   187  						})
   188  
   189  						It("returns a ProcessNotFoundError", func() {
   190  							Expect(makeError).To(MatchError(ccerror.ProcessNotFoundError{}))
   191  						})
   192  					})
   193  
   194  					When("an instance is not found", func() {
   195  						BeforeEach(func() {
   196  							serverResponse = `
   197  {
   198    "errors": [
   199      {
   200        "code": 10010,
   201        "detail": "Instance not found",
   202        "title": "CF-ResourceNotFound"
   203      }
   204    ]
   205  }`
   206  						})
   207  
   208  						It("returns an InstanceNotFoundError", func() {
   209  							Expect(makeError).To(MatchError(ccerror.InstanceNotFoundError{}))
   210  						})
   211  					})
   212  
   213  					When("an application is not found", func() {
   214  						BeforeEach(func() {
   215  							serverResponse = `
   216  {
   217    "errors": [
   218      {
   219        "code": 10010,
   220        "detail": "App not found",
   221        "title": "CF-ResourceNotFound"
   222      }
   223    ]
   224  }`
   225  						})
   226  
   227  						It("returns an AppNotFoundError", func() {
   228  							Expect(makeError).To(MatchError(ccerror.ApplicationNotFoundError{}))
   229  						})
   230  					})
   231  
   232  					When("a droplet is not found", func() {
   233  						BeforeEach(func() {
   234  							serverResponse = `
   235  {
   236    "errors": [
   237      {
   238        "code": 10010,
   239        "detail": "Droplet not found",
   240        "title": "CF-ResourceNotFound"
   241      }
   242    ]
   243  }`
   244  						})
   245  
   246  						It("returns a DropletNotFoundError", func() {
   247  							Expect(makeError).To(MatchError(ccerror.DropletNotFoundError{}))
   248  						})
   249  					})
   250  
   251  					When("a user is not found", func() {
   252  						BeforeEach(func() {
   253  							serverResponse = `
   254  							{
   255  							  "errors": [
   256  							    {
   257  							      "code": 10010,
   258  							      "detail": "User not found",
   259  							      "title": "CF-ResourceNotFound"
   260  							    }
   261  							  ]
   262  							}`
   263  						})
   264  
   265  						It("returns a UserNotFoundError", func() {
   266  							Expect(makeError).To(MatchError(ccerror.UserNotFoundError{}))
   267  						})
   268  					})
   269  
   270  					Context("generic not found", func() {
   271  						It("returns a ResourceNotFoundError", func() {
   272  							Expect(makeError).To(MatchError(ccerror.ResourceNotFoundError{Message: "SomeCC Error Message"}))
   273  						})
   274  					})
   275  				})
   276  
   277  				Context("(422) Unprocessable Entity", func() {
   278  					BeforeEach(func() {
   279  						serverResponseCode = http.StatusUnprocessableEntity
   280  					})
   281  
   282  					When("the name isn't unique to space (old error message)", func() {
   283  						BeforeEach(func() {
   284  							serverResponse = `
   285  {
   286    "errors": [
   287      {
   288        "code": 10008,
   289        "detail": "name must be unique in space",
   290        "title": "CF-UnprocessableEntity"
   291      }
   292    ]
   293  }`
   294  						})
   295  
   296  						It("returns a NameNotUniqueInSpaceError", func() {
   297  							Expect(makeError).To(Equal(
   298  								ccerror.NameNotUniqueInSpaceError{
   299  									UnprocessableEntityError: ccerror.UnprocessableEntityError{
   300  										Message: "name must be unique in space",
   301  									},
   302  								},
   303  							))
   304  						})
   305  					})
   306  
   307  					When("the name isn't unique to space (new error message)", func() {
   308  						BeforeEach(func() {
   309  							serverResponse = `
   310  {
   311    "errors": [
   312      {
   313        "code": 10008,
   314        "detail": "App with the name 'eli' already exists.",
   315        "title": "CF-UnprocessableEntity"
   316      }
   317    ]
   318  }`
   319  						})
   320  
   321  						It("returns a NameNotUniqueInSpaceError", func() {
   322  							Expect(makeError).To(Equal(
   323  								ccerror.NameNotUniqueInSpaceError{
   324  									UnprocessableEntityError: ccerror.UnprocessableEntityError{
   325  										Message: "App with the name 'eli' already exists.",
   326  									},
   327  								},
   328  							))
   329  						})
   330  					})
   331  
   332  					When("the name isn't unique to organization", func() {
   333  						BeforeEach(func() {
   334  							serverResponse = `
   335  {
   336    "errors": [
   337      {
   338        "code": 10008,
   339        "detail": "Name must be unique per organization",
   340        "title": "CF-UnprocessableEntity"
   341      }
   342    ]
   343  }`
   344  						})
   345  
   346  						It("returns a NameNotUniqueInOrgError", func() {
   347  							Expect(makeError).To(MatchError(ccerror.NameNotUniqueInOrgError{}))
   348  						})
   349  					})
   350  
   351  					When("the role already exists", func() {
   352  						BeforeEach(func() {
   353  							serverResponse = `
   354  {
   355    "errors": [
   356      {
   357        "code": 10008,
   358        "detail": "User 'wow' already has 'organization_auditor' role in organization 'wow'.",
   359        "title": "CF-UnprocessableEntity"
   360      }
   361    ]
   362  }`
   363  						})
   364  
   365  						It("returns a RoleAlreadyExistsError", func() {
   366  							Expect(makeError).To(Equal(
   367  								ccerror.RoleAlreadyExistsError{
   368  									UnprocessableEntityError: ccerror.UnprocessableEntityError{
   369  										Message: "User 'wow' already has 'organization_auditor' role in organization 'wow'.",
   370  									},
   371  								}),
   372  							)
   373  						})
   374  					})
   375  
   376  					When("the quota already exists", func() {
   377  						BeforeEach(func() {
   378  							serverResponse = `
   379  {
   380    "errors": [
   381      {
   382        "code": 10008,
   383        "detail": "Organization Quota 'default' already exists.",
   384        "title": "CF-UnprocessableEntity"
   385      }
   386    ]
   387  }`
   388  						})
   389  
   390  						It("returns a QuotaAlreadyExists error", func() {
   391  							Expect(makeError).To(Equal(
   392  								ccerror.QuotaAlreadyExists{
   393  									Message: "Organization Quota 'default' already exists.",
   394  								}),
   395  							)
   396  						})
   397  					})
   398  
   399  					When("the security group already exists", func() {
   400  						BeforeEach(func() {
   401  							serverResponse = `
   402  {
   403    "errors": [
   404      {
   405        "detail": "Security group with name 'sec-group' already exists.",
   406        "title": "CF-UnprocessableEntity",
   407        "code": 10008
   408      }
   409    ]
   410  }`
   411  						})
   412  
   413  						It("returns a SecurityGroupAlreadyExists error", func() {
   414  							Expect(makeError).To(Equal(
   415  								ccerror.SecurityGroupAlreadyExists{
   416  									Message: "Security group with name 'sec-group' already exists.",
   417  								}),
   418  							)
   419  						})
   420  					})
   421  
   422  					When("the buildpack is invalid", func() {
   423  						BeforeEach(func() {
   424  							serverResponse = `
   425  {
   426    "errors": [
   427      {
   428        "code": 10008,
   429        "detail": "Buildpack must be an existing admin buildpack or a valid git URI",
   430        "title": "CF-UnprocessableEntity"
   431      }
   432    ]
   433  }`
   434  						})
   435  
   436  						It("returns an InvalidBuildpackError", func() {
   437  							Expect(makeError).To(MatchError(ccerror.InvalidBuildpackError{}))
   438  						})
   439  					})
   440  
   441  					When("the buildpack is invalid", func() {
   442  						BeforeEach(func() {
   443  							serverResponse = `
   444  {
   445    "errors": [
   446      {
   447        "code": 10008,
   448        "detail": "Assign a droplet before starting this app.",
   449        "title": "CF-UnprocessableEntity"
   450      }
   451    ]
   452  }`
   453  						})
   454  
   455  						It("returns an InvalidStartError", func() {
   456  							Expect(makeError).To(MatchError(ccerror.InvalidStartError{}))
   457  						})
   458  					})
   459  
   460  					When("the detail describes something else", func() {
   461  						BeforeEach(func() {
   462  							serverResponse = `
   463  {
   464    "errors": [
   465      {
   466        "code": 10008,
   467        "detail": "SomeCC Error Message",
   468        "title": "CF-UnprocessableEntity"
   469      }
   470    ]
   471  }`
   472  						})
   473  
   474  						It("returns a UnprocessableEntityError", func() {
   475  							Expect(makeError).To(MatchError(ccerror.UnprocessableEntityError{Message: "SomeCC Error Message"}))
   476  						})
   477  					})
   478  				})
   479  			})
   480  
   481  			When("the error is a 5XX error", func() {
   482  				Context("(503) Service Unavailable", func() {
   483  					BeforeEach(func() {
   484  						serverResponseCode = http.StatusServiceUnavailable
   485  					})
   486  
   487  					It("returns a ServiceUnavailableError", func() {
   488  						Expect(makeError).To(MatchError(ccerror.ServiceUnavailableError{Message: "SomeCC Error Message"}))
   489  					})
   490  
   491  					When("the title is 'CF-TaskWorkersUnavailable'", func() {
   492  						BeforeEach(func() {
   493  							serverResponse = `{
   494    "errors": [
   495      {
   496        "code": 170020,
   497        "detail": "Task workers are unavailable: Failed to open TCP connection to nsync.service.cf.internal:8787 (getaddrinfo: Name or service not known)",
   498        "title": "CF-TaskWorkersUnavailable"
   499      }
   500    ]
   501  }`
   502  						})
   503  
   504  						It("returns a TaskWorkersUnavailableError", func() {
   505  							Expect(makeError).To(MatchError(ccerror.TaskWorkersUnavailableError{Message: "Task workers are unavailable: Failed to open TCP connection to nsync.service.cf.internal:8787 (getaddrinfo: Name or service not known)"}))
   506  						})
   507  					})
   508  				})
   509  
   510  				Context("all other 5XX", func() {
   511  					BeforeEach(func() {
   512  						serverResponseCode = http.StatusBadGateway
   513  						serverResponse = "I am some text"
   514  					})
   515  
   516  					It("returns a ServiceUnavailableError", func() {
   517  						Expect(makeError).To(MatchError(ccerror.V3UnexpectedResponseError{
   518  							ResponseCode: http.StatusBadGateway,
   519  							RequestIDs: []string{
   520  								"6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95",
   521  								"6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95::7445d9db-c31e-410d-8dc5-9f79ec3fc26f",
   522  							},
   523  							V3ErrorResponse: ccerror.V3ErrorResponse{
   524  								Errors: []ccerror.V3Error{{
   525  									Detail: serverResponse,
   526  								}},
   527  							},
   528  						}))
   529  					})
   530  				})
   531  			})
   532  
   533  			Context("Unhandled Error Codes", func() {
   534  				BeforeEach(func() {
   535  					serverResponseCode = http.StatusTeapot
   536  				})
   537  
   538  				It("returns an UnexpectedResponseError", func() {
   539  					Expect(makeError).To(MatchError(ccerror.V3UnexpectedResponseError{
   540  						ResponseCode: http.StatusTeapot,
   541  						V3ErrorResponse: ccerror.V3ErrorResponse{
   542  							Errors: []ccerror.V3Error{
   543  								{
   544  									Code:   777,
   545  									Detail: "SomeCC Error Message",
   546  									Title:  "CF-SomeError",
   547  								},
   548  							},
   549  						},
   550  						RequestIDs: []string{
   551  							"6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95",
   552  							"6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95::7445d9db-c31e-410d-8dc5-9f79ec3fc26f",
   553  						},
   554  					}))
   555  				})
   556  			})
   557  
   558  			Context("multiple errors", func() {
   559  				BeforeEach(func() {
   560  					serverResponseCode = http.StatusTeapot
   561  					serverResponse = `{
   562  							"errors": [
   563  								{
   564  									"code": 1000,
   565  									"detail": "Some CC Error Message",
   566  									"title": "CF-UnprocessableEntity"
   567  								},
   568  								{
   569  									"code": 1001,
   570  									"detail": "Some CC Error Message",
   571  									"title": "CF-UnprocessableEntity"
   572  								}
   573  							]
   574  						}`
   575  				})
   576  
   577  				It("returns a MultiError", func() {
   578  					Expect(makeError).To(MatchError(ccerror.MultiError{
   579  						ResponseCode: http.StatusTeapot,
   580  						Errors: []ccerror.V3Error{
   581  							{
   582  								Code:   1000,
   583  								Detail: "Some CC Error Message",
   584  								Title:  "CF-UnprocessableEntity",
   585  							},
   586  							{
   587  								Code:   1001,
   588  								Detail: "Some CC Error Message",
   589  								Title:  "CF-UnprocessableEntity",
   590  							},
   591  						},
   592  					}))
   593  				})
   594  			})
   595  		})
   596  	})
   597  })