github.com/pivotal-cf/go-pivnet/v6@v6.0.2/pivnet_test.go (about) 1 package pivnet_test 2 3 import ( 4 "fmt" 5 "github.com/pivotal-cf/go-pivnet/v6/go-pivnetfakes" 6 "net/http" 7 8 "github.com/onsi/gomega/ghttp" 9 "github.com/pivotal-cf/go-pivnet/v6" 10 "github.com/pivotal-cf/go-pivnet/v6/logger" 11 "github.com/pivotal-cf/go-pivnet/v6/logger/loggerfakes" 12 13 . "github.com/onsi/ginkgo" 14 . "github.com/onsi/gomega" 15 ) 16 17 type pivnetErr struct { 18 Status int `json:"status"` 19 Message string `json:"message"` 20 } 21 22 var _ = Describe("PivnetClient", func() { 23 var ( 24 server *ghttp.Server 25 client pivnet.Client 26 userAgent string 27 token string 28 29 releases pivnet.ReleasesResponse 30 31 newClientConfig pivnet.ClientConfig 32 fakeLogger logger.Logger 33 fakeAccessTokenService *gopivnetfakes.FakeAccessTokenService 34 ) 35 36 BeforeEach(func() { 37 releases = pivnet.ReleasesResponse{Releases: []pivnet.Release{ 38 { 39 ID: 1, 40 Version: "1234", 41 }, 42 { 43 ID: 99, 44 Version: "some-other-version", 45 }, 46 }} 47 48 server = ghttp.NewServer() 49 token = "my-auth-token" 50 userAgent = "pivnet-resource/0.1.0 (some-url)" 51 52 fakeLogger = &loggerfakes.FakeLogger{} 53 fakeAccessTokenService = &gopivnetfakes.FakeAccessTokenService{} 54 newClientConfig = pivnet.ClientConfig{ 55 Host: server.URL(), 56 UserAgent: userAgent, 57 } 58 client = pivnet.NewClient(fakeAccessTokenService, newClientConfig, fakeLogger) 59 }) 60 61 JustBeforeEach(func() { 62 fakeAccessTokenService.AccessTokenReturns(token, nil) 63 }) 64 65 AfterEach(func() { 66 server.Close() 67 }) 68 69 Context("when using a pivnet API token", func() { 70 BeforeEach(func() { 71 newClientConfig = pivnet.ClientConfig{ 72 Host: server.URL(), 73 UserAgent: userAgent, 74 } 75 client = pivnet.NewClient(fakeAccessTokenService, newClientConfig, fakeLogger) 76 }) 77 It("uses token authentication header if configured with a pivnet api token", func() { 78 server.AppendHandlers( 79 ghttp.CombineHandlers( 80 ghttp.VerifyRequest( 81 "GET", 82 fmt.Sprintf("%s/foo", apiPrefix), 83 ), 84 ghttp.VerifyHeaderKV("Authorization", fmt.Sprintf("Token %s", token)), 85 ghttp.RespondWithJSONEncoded(http.StatusOK, releases), 86 ), 87 ) 88 89 _, err := client.MakeRequest( 90 "GET", 91 "/foo", 92 http.StatusOK, 93 nil, 94 ) 95 Expect(err).NotTo(HaveOccurred()) 96 }) 97 }) 98 99 Context("when using a UAA refreshToken", func() { 100 BeforeEach(func() { 101 token = "my-auth-token-that-is-longer-than-a-legacy-token" 102 newClientConfig = pivnet.ClientConfig{ 103 Host: server.URL(), 104 UserAgent: userAgent, 105 } 106 client = pivnet.NewClient(fakeAccessTokenService, newClientConfig, fakeLogger) 107 }) 108 109 It("uses bearer authentication header", func() { 110 server.AppendHandlers( 111 ghttp.CombineHandlers( 112 ghttp.VerifyRequest( 113 "GET", 114 fmt.Sprintf("%s/foo", apiPrefix), 115 ), 116 ghttp.VerifyHeaderKV("Authorization", fmt.Sprintf("Bearer %s", token)), 117 ghttp.RespondWithJSONEncoded(http.StatusOK, releases), 118 ), 119 ) 120 121 _, err := client.MakeRequest( 122 "GET", 123 "/foo", 124 http.StatusOK, 125 nil, 126 ) 127 Expect(err).NotTo(HaveOccurred()) 128 }) 129 }) 130 131 It("sets custom user agent", func() { 132 server.AppendHandlers( 133 ghttp.CombineHandlers( 134 ghttp.VerifyRequest( 135 "GET", 136 fmt.Sprintf("%s/foo", apiPrefix), 137 ), 138 ghttp.VerifyHeaderKV("Authorization", fmt.Sprintf("Token %s", token)), 139 ghttp.VerifyHeaderKV("User-Agent", userAgent), 140 ghttp.RespondWithJSONEncoded(http.StatusOK, releases), 141 ), 142 ) 143 144 _, err := client.MakeRequest( 145 "GET", 146 "/foo", 147 http.StatusOK, 148 nil, 149 ) 150 Expect(err).NotTo(HaveOccurred()) 151 }) 152 153 It("sets Content-Type application/json", func() { 154 server.AppendHandlers( 155 ghttp.CombineHandlers( 156 ghttp.VerifyRequest( 157 "GET", 158 fmt.Sprintf("%s/foo", apiPrefix), 159 ), 160 ghttp.VerifyHeaderKV("Content-Type", "application/json"), 161 ghttp.RespondWithJSONEncoded(http.StatusOK, releases), 162 ), 163 ) 164 165 _, err := client.MakeRequest( 166 "GET", 167 "/foo", 168 http.StatusOK, 169 nil, 170 ) 171 Expect(err).NotTo(HaveOccurred()) 172 }) 173 174 Context("when parsing the url fails with error", func() { 175 It("forwards the error", func() { 176 newClientConfig.Host = "%%%" 177 client = pivnet.NewClient(fakeAccessTokenService, newClientConfig, fakeLogger) 178 179 _, err := client.MakeRequest( 180 "GET", 181 "/foo", 182 http.StatusOK, 183 nil, 184 ) 185 Expect(err).To(HaveOccurred()) 186 Expect(err.Error()).To(ContainSubstring("%%%")) 187 }) 188 }) 189 190 Context("when Pivnet returns a 401", func() { 191 var ( 192 body []byte 193 ) 194 195 JustBeforeEach(func() { 196 server.AppendHandlers( 197 ghttp.CombineHandlers( 198 ghttp.VerifyRequest( 199 "GET", 200 fmt.Sprintf("%s/foo", apiPrefix), 201 ), 202 ghttp.RespondWith(http.StatusUnauthorized, body), 203 ), 204 ) 205 }) 206 207 Context("when Pivnet returns JSON", func() { 208 BeforeEach(func() { 209 body = []byte(`{"message":"foo message"}`) 210 }) 211 212 It("returns an ErrUnauthorized error with message from Pivnet", func() { 213 _, err := client.MakeRequest( 214 "GET", 215 "/foo", 216 http.StatusOK, 217 nil, 218 ) 219 Expect(err).To(HaveOccurred()) 220 Expect(err).To(MatchError( 221 pivnet.ErrUnauthorized{ 222 ResponseCode: http.StatusUnauthorized, 223 Message: "foo message", 224 }, 225 )) 226 }) 227 }) 228 229 Context("when Pivnet returns a non JSON", func() { 230 BeforeEach(func() { 231 body = []byte("Forbidden") 232 }) 233 234 It("returns a well formatted error", func() { 235 _, err := client.MakeRequest( 236 "GET", 237 "/foo", 238 http.StatusOK, 239 nil, 240 ) 241 Expect(err).To(HaveOccurred()) 242 Expect(err.Error()).To(ContainSubstring("could not parse json [\"Forbidden\"]")) 243 }) 244 }) 245 }) 246 247 Context("when Pivnet returns a 429", func() { 248 var ( 249 body []byte 250 ) 251 252 BeforeEach(func() { 253 body = []byte(`Retry later`) 254 }) 255 256 It("returns an ErrUnauthorized error with message from Pivnet", func() { 257 server.AppendHandlers( 258 ghttp.CombineHandlers( 259 ghttp.VerifyRequest( 260 "GET", 261 fmt.Sprintf("%s/foo", apiPrefix), 262 ), 263 ghttp.RespondWith(http.StatusTooManyRequests, body), 264 ), 265 ) 266 267 _, err := client.MakeRequest( 268 "GET", 269 "/foo", 270 http.StatusOK, 271 nil, 272 ) 273 Expect(err).To(HaveOccurred()) 274 Expect(err).To(MatchError( 275 pivnet.ErrTooManyRequests{ 276 ResponseCode: http.StatusTooManyRequests, 277 Message: "You have hit a rate limit for this request", 278 }, 279 )) 280 }) 281 }) 282 283 Context("when Pivnet returns a 451", func() { 284 var ( 285 body []byte 286 ) 287 288 BeforeEach(func() { 289 body = []byte(`{"message":"I should be visible to the user"}`) 290 }) 291 292 It("returns an ErrUnavailableForLegalReasons error with message from Pivnet", func() { 293 server.AppendHandlers( 294 ghttp.CombineHandlers( 295 ghttp.VerifyRequest( 296 "GET", 297 fmt.Sprintf("%s/foo", apiPrefix), 298 ), 299 ghttp.RespondWith(http.StatusUnavailableForLegalReasons, body), 300 ), 301 ) 302 303 _, err := client.MakeRequest( 304 "GET", 305 "/foo", 306 http.StatusOK, 307 nil, 308 ) 309 Expect(err).To(HaveOccurred()) 310 Expect(err).To(MatchError( 311 pivnet.ErrUnavailableForLegalReasons{ 312 ResponseCode: http.StatusUnavailableForLegalReasons, 313 Message: "I should be visible to the user", 314 }, 315 )) 316 }) 317 }) 318 319 Context("when Pivnet returns a 404", func() { 320 var ( 321 body []byte 322 ) 323 324 BeforeEach(func() { 325 body = []byte(`{"message":"foo message"}`) 326 }) 327 328 It("returns an ErrNotFound error with message from Pivnet", func() { 329 server.AppendHandlers( 330 ghttp.CombineHandlers( 331 ghttp.VerifyRequest( 332 "GET", 333 fmt.Sprintf("%s/foo", apiPrefix), 334 ), 335 ghttp.RespondWith(http.StatusNotFound, body), 336 ), 337 ) 338 339 _, err := client.MakeRequest( 340 "GET", 341 "/foo", 342 http.StatusOK, 343 nil, 344 ) 345 Expect(err).To(HaveOccurred()) 346 Expect(err).To(MatchError( 347 pivnet.ErrNotFound{ 348 ResponseCode: http.StatusNotFound, 349 Message: "foo message", 350 }, 351 )) 352 }) 353 }) 354 355 Context("when Pivnet returns a 500", func() { 356 var ( 357 body []byte 358 ) 359 360 BeforeEach(func() { 361 body = []byte(`{"status":"500","error":"foo message"}`) 362 }) 363 364 It("returns an error", func() { 365 server.AppendHandlers( 366 ghttp.CombineHandlers( 367 ghttp.VerifyRequest( 368 "GET", 369 fmt.Sprintf("%s/foo", apiPrefix), 370 ), 371 ghttp.RespondWith(http.StatusInternalServerError, body), 372 ), 373 ) 374 375 _, err := client.MakeRequest( 376 "GET", 377 "/foo", 378 http.StatusOK, 379 nil, 380 ) 381 Expect(err).To(HaveOccurred()) 382 Expect(err).To(MatchError( 383 pivnet.ErrPivnetOther{ 384 ResponseCode: http.StatusInternalServerError, 385 Message: "foo message", 386 }, 387 )) 388 }) 389 390 Context("when unmarshalling the response from Pivnet returns an error", func() { 391 BeforeEach(func() { 392 body = []byte(`{"error":1234}`) 393 }) 394 395 It("returns an error", func() { 396 server.AppendHandlers( 397 ghttp.CombineHandlers( 398 ghttp.VerifyRequest( 399 "GET", 400 fmt.Sprintf("%s/foo", apiPrefix), 401 ), 402 ghttp.RespondWith(http.StatusInternalServerError, body), 403 ), 404 ) 405 406 _, err := client.MakeRequest( 407 "GET", 408 "/foo", 409 http.StatusOK, 410 nil, 411 ) 412 Expect(err).To(HaveOccurred()) 413 Expect(err.Error()).To(ContainSubstring("json: cannot unmarshal")) 414 }) 415 }) 416 }) 417 418 Context("when an unexpected status code comes back from Pivnet", func() { 419 var ( 420 body []byte 421 ) 422 423 BeforeEach(func() { 424 body = []byte(`{"message":"foo message"}`) 425 }) 426 427 It("returns an error", func() { 428 server.AppendHandlers( 429 ghttp.CombineHandlers( 430 ghttp.VerifyRequest( 431 "GET", 432 fmt.Sprintf("%s/foo", apiPrefix), 433 ), 434 ghttp.RespondWith(http.StatusTeapot, body), 435 ), 436 ) 437 438 _, err := client.MakeRequest( 439 "GET", 440 "/foo", 441 http.StatusOK, 442 nil, 443 ) 444 Expect(err).To(HaveOccurred()) 445 Expect(err.Error()).To(ContainSubstring("foo message")) 446 }) 447 448 Context("when unmarshalling the response from Pivnet returns an error", func() { 449 It("returns an error", func() { 450 server.AppendHandlers( 451 ghttp.CombineHandlers( 452 ghttp.VerifyRequest( 453 "GET", 454 fmt.Sprintf("%s/foo", apiPrefix), 455 ), 456 ghttp.RespondWith(http.StatusTeapot, nil), 457 ), 458 ) 459 460 _, err := client.MakeRequest( 461 "GET", 462 "/foo", 463 http.StatusOK, 464 nil, 465 ) 466 Expect(err).To(HaveOccurred()) 467 Expect(err.Error()).To(ContainSubstring("JSON")) 468 }) 469 }) 470 471 Context("when an expectedResponseCode of 0 is provided", func() { 472 It("does not return an error", func() { 473 server.AppendHandlers( 474 ghttp.CombineHandlers( 475 ghttp.VerifyRequest( 476 "GET", 477 fmt.Sprintf("%s/foo", apiPrefix), 478 ), 479 ghttp.RespondWith(http.StatusTeapot, body), 480 ), 481 ) 482 483 _, err := client.MakeRequest( 484 "GET", 485 "/foo", 486 0, 487 nil, 488 ) 489 Expect(err).NotTo(HaveOccurred()) 490 }) 491 }) 492 493 }) 494 495 Describe("CreateRequest", func() { 496 It("strips the host prefix if present", func() { 497 req, err := client.CreateRequest( 498 "GET", 499 fmt.Sprintf("https://example.com/%s/foo/bar", "api/v2"), 500 nil, 501 ) 502 503 Expect(err).NotTo(HaveOccurred()) 504 Expect(req.URL.Path).To(Equal("/api/v2/foo/bar")) 505 }) 506 507 It("does not add auth header if versions endpoint", func() { 508 req, err := client.CreateRequest( 509 "GET", 510 "/versions", 511 nil, 512 ) 513 514 Expect(err).NotTo(HaveOccurred()) 515 Expect(req.Header.Get("Authorization")).To(Equal("")) 516 Expect(req.Header.Get("Content-Type")).To(Equal("application/json")) 517 }) 518 }) 519 })