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 })