github.com/loggregator/cli@v6.33.1-0.20180224010324-82334f081791+incompatible/api/cloudcontroller/cloud_controller_connection_test.go (about)

     1  package cloudcontroller_test
     2  
     3  import (
     4  	"fmt"
     5  	"net/http"
     6  	"runtime"
     7  	"strings"
     8  
     9  	. "code.cloudfoundry.org/cli/api/cloudcontroller"
    10  	"code.cloudfoundry.org/cli/api/cloudcontroller/ccerror"
    11  	. "github.com/onsi/ginkgo"
    12  	. "github.com/onsi/gomega"
    13  	. "github.com/onsi/gomega/ghttp"
    14  )
    15  
    16  type DummyResponse struct {
    17  	Val1 string      `json:"val1"`
    18  	Val2 int         `json:"val2"`
    19  	Val3 interface{} `json:"val3,omitempty"`
    20  }
    21  
    22  var _ = Describe("Cloud Controller Connection", func() {
    23  	var connection *CloudControllerConnection
    24  
    25  	BeforeEach(func() {
    26  		connection = NewConnection(Config{SkipSSLValidation: true})
    27  	})
    28  
    29  	Describe("Make", func() {
    30  		Describe("Data Unmarshalling", func() {
    31  			var request *Request
    32  
    33  			BeforeEach(func() {
    34  				response := `{
    35  					"val1":"2.59.0",
    36  					"val2":2,
    37  					"val3":1111111111111111111
    38  				}`
    39  				server.AppendHandlers(
    40  					CombineHandlers(
    41  						VerifyRequest(http.MethodGet, "/v2/foo", ""),
    42  						RespondWith(http.StatusOK, response),
    43  					),
    44  				)
    45  
    46  				req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/v2/foo", server.URL()), nil)
    47  				Expect(err).ToNot(HaveOccurred())
    48  				request = &Request{Request: req}
    49  			})
    50  
    51  			Context("when passed a response with a result set", func() {
    52  				It("unmarshals the data into a struct", func() {
    53  					var body DummyResponse
    54  					response := Response{
    55  						Result: &body,
    56  					}
    57  
    58  					err := connection.Make(request, &response)
    59  					Expect(err).NotTo(HaveOccurred())
    60  
    61  					Expect(body.Val1).To(Equal("2.59.0"))
    62  					Expect(body.Val2).To(Equal(2))
    63  				})
    64  
    65  				It("keeps numbers unmarshalled to interfaces as interfaces", func() {
    66  					var body DummyResponse
    67  					response := Response{
    68  						Result: &body,
    69  					}
    70  
    71  					err := connection.Make(request, &response)
    72  					Expect(err).NotTo(HaveOccurred())
    73  					Expect(fmt.Sprint(body.Val3)).To(Equal("1111111111111111111"))
    74  				})
    75  			})
    76  
    77  			Context("when passed an empty response", func() {
    78  				It("skips the unmarshalling step", func() {
    79  					var response Response
    80  					err := connection.Make(request, &response)
    81  					Expect(err).NotTo(HaveOccurred())
    82  					Expect(response.Result).To(BeNil())
    83  				})
    84  			})
    85  		})
    86  
    87  		Describe("HTTP Response", func() {
    88  			var request *Request
    89  
    90  			BeforeEach(func() {
    91  				response := `{}`
    92  				server.AppendHandlers(
    93  					CombineHandlers(
    94  						VerifyRequest(http.MethodGet, "/v2/foo", ""),
    95  						RespondWith(http.StatusOK, response),
    96  					),
    97  				)
    98  
    99  				req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/v2/foo", server.URL()), nil)
   100  				Expect(err).ToNot(HaveOccurred())
   101  				request = &Request{Request: req}
   102  			})
   103  
   104  			It("returns the status", func() {
   105  				response := Response{}
   106  
   107  				err := connection.Make(request, &response)
   108  				Expect(err).NotTo(HaveOccurred())
   109  
   110  				Expect(response.HTTPResponse.Status).To(Equal("200 OK"))
   111  			})
   112  		})
   113  
   114  		Describe("Response Headers", func() {
   115  			Describe("Location", func() {
   116  				BeforeEach(func() {
   117  					server.AppendHandlers(
   118  						CombineHandlers(
   119  							VerifyRequest(http.MethodGet, "/v2/foo"),
   120  							RespondWith(http.StatusAccepted, "{}", http.Header{"Location": {"/v2/some-location"}}),
   121  						),
   122  					)
   123  				})
   124  
   125  				It("returns the location in the ResourceLocationURL", func() {
   126  					req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/v2/foo", server.URL()), nil)
   127  					Expect(err).ToNot(HaveOccurred())
   128  					request := &Request{Request: req}
   129  
   130  					var response Response
   131  					err = connection.Make(request, &response)
   132  					Expect(err).NotTo(HaveOccurred())
   133  
   134  					Expect(server.ReceivedRequests()).To(HaveLen(1))
   135  					Expect(response.ResourceLocationURL).To(Equal("/v2/some-location"))
   136  				})
   137  			})
   138  
   139  			Describe("X-Cf-Warnings", func() {
   140  				BeforeEach(func() {
   141  					server.AppendHandlers(
   142  						CombineHandlers(
   143  							VerifyRequest(http.MethodGet, "/v2/foo"),
   144  							RespondWith(http.StatusOK, "{}", http.Header{"X-Cf-Warnings": {"42, Ed McMann, the 1942 doggers"}}),
   145  						),
   146  					)
   147  				})
   148  
   149  				It("returns them in Response", func() {
   150  					req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/v2/foo", server.URL()), nil)
   151  					Expect(err).ToNot(HaveOccurred())
   152  					request := &Request{Request: req}
   153  
   154  					var response Response
   155  					err = connection.Make(request, &response)
   156  					Expect(err).NotTo(HaveOccurred())
   157  
   158  					Expect(server.ReceivedRequests()).To(HaveLen(1))
   159  
   160  					warnings := response.Warnings
   161  					Expect(warnings).ToNot(BeNil())
   162  					Expect(warnings).To(HaveLen(3))
   163  					Expect(warnings).To(ContainElement("42"))
   164  					Expect(warnings).To(ContainElement("Ed McMann"))
   165  					Expect(warnings).To(ContainElement("the 1942 doggers"))
   166  				})
   167  			})
   168  		})
   169  
   170  		Describe("Errors", func() {
   171  			Context("when the server does not exist", func() {
   172  				BeforeEach(func() {
   173  					connection = NewConnection(Config{})
   174  				})
   175  
   176  				It("returns a RequestError", func() {
   177  					req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/v2/foo", "http://garbledyguk.com"), nil)
   178  					Expect(err).ToNot(HaveOccurred())
   179  					request := &Request{Request: req}
   180  
   181  					var response Response
   182  					err = connection.Make(request, &response)
   183  					Expect(err).To(HaveOccurred())
   184  
   185  					requestErr, ok := err.(ccerror.RequestError)
   186  					Expect(ok).To(BeTrue())
   187  					Expect(requestErr.Error()).To(MatchRegexp(".*http://garbledyguk.com/v2/foo.*[nN]o such host"))
   188  				})
   189  			})
   190  
   191  			Context("when the server does not have a verified certificate", func() {
   192  				Context("skipSSLValidation is false", func() {
   193  					BeforeEach(func() {
   194  						server.AppendHandlers(
   195  							CombineHandlers(
   196  								VerifyRequest(http.MethodGet, "/v2/foo"),
   197  							),
   198  						)
   199  
   200  						connection = NewConnection(Config{})
   201  					})
   202  
   203  					It("returns a UnverifiedServerError", func() {
   204  						req, err := http.NewRequest(http.MethodGet, server.URL(), nil)
   205  						Expect(err).ToNot(HaveOccurred())
   206  						request := &Request{Request: req}
   207  
   208  						var response Response
   209  						err = connection.Make(request, &response)
   210  						Expect(err).To(MatchError(ccerror.UnverifiedServerError{URL: server.URL()}))
   211  					})
   212  				})
   213  			})
   214  
   215  			Context("when the server's certificate does not match the hostname", func() {
   216  				Context("skipSSLValidation is false", func() {
   217  					BeforeEach(func() {
   218  						if runtime.GOOS == "windows" {
   219  							Skip("ssl validation has a different order on windows, will not be returned properly")
   220  						}
   221  						server.AppendHandlers(
   222  							CombineHandlers(
   223  								VerifyRequest(http.MethodGet, "/"),
   224  							),
   225  						)
   226  
   227  						connection = NewConnection(Config{})
   228  					})
   229  
   230  					// loopback.cli.ci.cf-app.com is a custom DNS record setup to point to 127.0.0.1
   231  					It("returns a SSLValidationHostnameError", func() {
   232  						altHostURL := strings.Replace(server.URL(), "127.0.0.1", "loopback.cli.ci.cf-app.com", -1)
   233  						req, err := http.NewRequest(http.MethodGet, altHostURL, nil)
   234  						Expect(err).ToNot(HaveOccurred())
   235  						request := &Request{Request: req}
   236  
   237  						var response Response
   238  						err = connection.Make(request, &response)
   239  						Expect(err).To(MatchError(ccerror.SSLValidationHostnameError{
   240  							Message: "x509: certificate is valid for example.com, not loopback.cli.ci.cf-app.com",
   241  						}))
   242  					})
   243  				})
   244  			})
   245  
   246  			Describe("RawHTTPStatusError", func() {
   247  				var ccResponse string
   248  				BeforeEach(func() {
   249  					ccResponse = `{
   250  						"code": 90004,
   251  						"description": "The service binding could not be found: some-guid",
   252  						"error_code": "CF-ServiceBindingNotFound"
   253  					}`
   254  
   255  					server.AppendHandlers(
   256  						CombineHandlers(
   257  							VerifyRequest(http.MethodGet, "/v2/foo"),
   258  							RespondWith(http.StatusNotFound, ccResponse, http.Header{"X-Vcap-Request-Id": {"6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95", "6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95::7445d9db-c31e-410d-8dc5-9f79ec3fc26f"}}),
   259  						),
   260  					)
   261  				})
   262  
   263  				It("returns a CCRawResponse", func() {
   264  					req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/v2/foo", server.URL()), nil)
   265  					Expect(err).ToNot(HaveOccurred())
   266  					request := &Request{Request: req}
   267  
   268  					var response Response
   269  					err = connection.Make(request, &response)
   270  					Expect(err).To(MatchError(ccerror.RawHTTPStatusError{
   271  						StatusCode:  http.StatusNotFound,
   272  						RawResponse: []byte(ccResponse),
   273  						RequestIDs:  []string{"6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95", "6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95::7445d9db-c31e-410d-8dc5-9f79ec3fc26f"},
   274  					}))
   275  
   276  					Expect(server.ReceivedRequests()).To(HaveLen(1))
   277  				})
   278  			})
   279  		})
   280  	})
   281  })