github.com/ablease/cli@v6.37.1-0.20180613014814-3adbb7d7fb19+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  		Context("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  		Context("when the error is from the cloud controller", func() {
    71  			Context("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  			Context("when the error is a 4XX error", func() {
    86  				Context("(401) Unauthorized", func() {
    87  					BeforeEach(func() {
    88  						serverResponseCode = http.StatusUnauthorized
    89  					})
    90  
    91  					Context("generic 401", func() {
    92  						It("returns a UnauthorizedError", func() {
    93  							Expect(makeError).To(MatchError(ccerror.UnauthorizedError{Message: "SomeCC Error Message"}))
    94  						})
    95  					})
    96  
    97  					Context("invalid token", func() {
    98  						BeforeEach(func() {
    99  							serverResponse = `{
   100  							"errors": [
   101  								{
   102  									"code": 1000,
   103  									"detail": "Invalid Auth Token",
   104  									"title": "CF-InvalidAuthToken"
   105  								}
   106  							]
   107  						}`
   108  						})
   109  
   110  						It("returns an InvalidAuthTokenError", func() {
   111  							Expect(makeError).To(MatchError(ccerror.InvalidAuthTokenError{Message: "Invalid Auth Token"}))
   112  						})
   113  					})
   114  				})
   115  
   116  				Context("(403) Forbidden", func() {
   117  					BeforeEach(func() {
   118  						serverResponseCode = http.StatusForbidden
   119  					})
   120  
   121  					It("returns a ForbiddenError", func() {
   122  						Expect(makeError).To(MatchError(ccerror.ForbiddenError{Message: "SomeCC Error Message"}))
   123  					})
   124  				})
   125  
   126  				Context("(404) Not Found", func() {
   127  					BeforeEach(func() {
   128  						serverResponseCode = http.StatusNotFound
   129  					})
   130  
   131  					Context("when a process is not found", func() {
   132  						BeforeEach(func() {
   133  							serverResponse = `
   134  {
   135    "errors": [
   136      {
   137        "code": 10010,
   138        "detail": "Process not found",
   139        "title": "CF-ResourceNotFound"
   140      }
   141    ]
   142  }`
   143  						})
   144  
   145  						It("returns a ProcessNotFoundError", func() {
   146  							Expect(makeError).To(MatchError(ccerror.ProcessNotFoundError{}))
   147  						})
   148  					})
   149  
   150  					Context("when an instance is not found", func() {
   151  						BeforeEach(func() {
   152  							serverResponse = `
   153  {
   154    "errors": [
   155      {
   156        "code": 10010,
   157        "detail": "Instance not found",
   158        "title": "CF-ResourceNotFound"
   159      }
   160    ]
   161  }`
   162  						})
   163  
   164  						It("returns an InstanceNotFoundError", func() {
   165  							Expect(makeError).To(MatchError(ccerror.InstanceNotFoundError{}))
   166  						})
   167  					})
   168  
   169  					Context("when an application is not found", func() {
   170  						BeforeEach(func() {
   171  							serverResponse = `
   172  {
   173    "errors": [
   174      {
   175        "code": 10010,
   176        "detail": "App not found",
   177        "title": "CF-ResourceNotFound"
   178      }
   179    ]
   180  }`
   181  						})
   182  
   183  						It("returns an AppNotFoundError", func() {
   184  							Expect(makeError).To(MatchError(ccerror.ApplicationNotFoundError{}))
   185  						})
   186  					})
   187  
   188  					Context("when a droplet is not found", func() {
   189  						BeforeEach(func() {
   190  							serverResponse = `
   191  {
   192    "errors": [
   193      {
   194        "code": 10010,
   195        "detail": "Droplet not found",
   196        "title": "CF-ResourceNotFound"
   197      }
   198    ]
   199  }`
   200  						})
   201  
   202  						It("returns a DropletNotFoundError", func() {
   203  							Expect(makeError).To(MatchError(ccerror.DropletNotFoundError{}))
   204  						})
   205  					})
   206  
   207  					Context("generic not found", func() {
   208  
   209  						It("returns a ResourceNotFoundError", func() {
   210  							Expect(makeError).To(MatchError(ccerror.ResourceNotFoundError{Message: "SomeCC Error Message"}))
   211  						})
   212  					})
   213  				})
   214  
   215  				Context("(422) Unprocessable Entity", func() {
   216  					BeforeEach(func() {
   217  						serverResponseCode = http.StatusUnprocessableEntity
   218  					})
   219  
   220  					Context("when the name isn't unique to space", func() {
   221  						BeforeEach(func() {
   222  							serverResponse = `
   223  {
   224    "errors": [
   225      {
   226        "code": 10008,
   227        "detail": "name must be unique in space",
   228        "title": "CF-UnprocessableEntity"
   229      }
   230    ]
   231  }`
   232  						})
   233  
   234  						It("returns a NameNotUniqueInSpaceError", func() {
   235  							Expect(makeError).To(MatchError(ccerror.NameNotUniqueInSpaceError{}))
   236  						})
   237  					})
   238  
   239  					Context("when the buildpack is invalid", func() {
   240  						BeforeEach(func() {
   241  							serverResponse = `
   242  {
   243    "errors": [
   244      {
   245        "code": 10008,
   246        "detail": "Buildpack must be an existing admin buildpack or a valid git URI",
   247        "title": "CF-UnprocessableEntity"
   248      }
   249    ]
   250  }`
   251  						})
   252  
   253  						It("returns an InvalidBuildpackError", func() {
   254  							Expect(makeError).To(MatchError(ccerror.InvalidBuildpackError{}))
   255  						})
   256  					})
   257  
   258  					Context("when the detail describes something else", func() {
   259  						BeforeEach(func() {
   260  							serverResponse = `
   261  {
   262    "errors": [
   263      {
   264        "code": 10008,
   265        "detail": "SomeCC Error Message",
   266        "title": "CF-UnprocessableEntity"
   267      }
   268    ]
   269  }`
   270  						})
   271  
   272  						It("returns a UnprocessableEntityError", func() {
   273  							Expect(makeError).To(MatchError(ccerror.UnprocessableEntityError{Message: "SomeCC Error Message"}))
   274  						})
   275  					})
   276  				})
   277  			})
   278  
   279  			Context("when the error is a 5XX error", func() {
   280  				Context("(503) Service Unavailable", func() {
   281  					BeforeEach(func() {
   282  						serverResponseCode = http.StatusServiceUnavailable
   283  					})
   284  
   285  					It("returns a ServiceUnavailableError", func() {
   286  						Expect(makeError).To(MatchError(ccerror.ServiceUnavailableError{Message: "SomeCC Error Message"}))
   287  					})
   288  
   289  					Context("when the title is 'CF-TaskWorkersUnavailable'", func() {
   290  						BeforeEach(func() {
   291  							serverResponse = `{
   292    "errors": [
   293      {
   294        "code": 170020,
   295        "detail": "Task workers are unavailable: Failed to open TCP connection to nsync.service.cf.internal:8787 (getaddrinfo: Name or service not known)",
   296        "title": "CF-TaskWorkersUnavailable"
   297      }
   298    ]
   299  }`
   300  						})
   301  
   302  						It("returns a TaskWorkersUnavailableError", func() {
   303  							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)"}))
   304  						})
   305  					})
   306  				})
   307  
   308  				Context("all other 5XX", func() {
   309  					BeforeEach(func() {
   310  						serverResponseCode = http.StatusBadGateway
   311  						serverResponse = "I am some text"
   312  					})
   313  
   314  					It("returns a ServiceUnavailableError", func() {
   315  						Expect(makeError).To(MatchError(ccerror.V3UnexpectedResponseError{
   316  							ResponseCode: http.StatusBadGateway,
   317  							RequestIDs: []string{
   318  								"6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95",
   319  								"6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95::7445d9db-c31e-410d-8dc5-9f79ec3fc26f",
   320  							},
   321  							V3ErrorResponse: ccerror.V3ErrorResponse{
   322  								Errors: []ccerror.V3Error{{
   323  									Detail: serverResponse,
   324  								}},
   325  							},
   326  						}))
   327  					})
   328  				})
   329  			})
   330  
   331  			Context("Unhandled Error Codes", func() {
   332  				BeforeEach(func() {
   333  					serverResponseCode = http.StatusTeapot
   334  				})
   335  
   336  				It("returns an UnexpectedResponseError", func() {
   337  					Expect(makeError).To(MatchError(ccerror.V3UnexpectedResponseError{
   338  						ResponseCode: http.StatusTeapot,
   339  						V3ErrorResponse: ccerror.V3ErrorResponse{
   340  							Errors: []ccerror.V3Error{
   341  								{
   342  									Code:   777,
   343  									Detail: "SomeCC Error Message",
   344  									Title:  "CF-SomeError",
   345  								},
   346  							},
   347  						},
   348  						RequestIDs: []string{
   349  							"6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95",
   350  							"6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95::7445d9db-c31e-410d-8dc5-9f79ec3fc26f",
   351  						},
   352  					}))
   353  				})
   354  			})
   355  
   356  			Context("multiple errors", func() {
   357  				BeforeEach(func() {
   358  					serverResponseCode = http.StatusTeapot
   359  					serverResponse = `{
   360  							"errors": [
   361  								{
   362  									"code": 1000,
   363  									"detail": "Some CC Error Message",
   364  									"title": "CF-UnprocessableEntity"
   365  								},
   366  								{
   367  									"code": 1001,
   368  									"detail": "Some CC Error Message",
   369  									"title": "CF-UnprocessableEntity"
   370  								}
   371  							]
   372  						}`
   373  				})
   374  
   375  				It("returns a MultiError", func() {
   376  					Expect(makeError).To(MatchError(ccerror.MultiError{
   377  						ResponseCode: http.StatusTeapot,
   378  						Errors: []ccerror.V3Error{
   379  							{
   380  								Code:   1000,
   381  								Detail: "Some CC Error Message",
   382  								Title:  "CF-UnprocessableEntity",
   383  							},
   384  							{
   385  								Code:   1001,
   386  								Detail: "Some CC Error Message",
   387  								Title:  "CF-UnprocessableEntity",
   388  							},
   389  						},
   390  					}))
   391  				})
   392  			})
   393  		})
   394  	})
   395  })