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