github.com/arunkumar7540/cli@v6.45.0+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  			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  						DecodeJSONResponseInto: &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  						DecodeJSONResponseInto: &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  			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.DecodeJSONResponseInto).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  				When("there are warnings", func() {
   141  					BeforeEach(func() {
   142  						server.AppendHandlers(
   143  							CombineHandlers(
   144  								VerifyRequest(http.MethodGet, "/v2/foo"),
   145  								RespondWith(http.StatusOK, "{}", http.Header{"X-Cf-Warnings": {"42,+Ed+McMann,+the+1942+doggers,a%2Cb"}}),
   146  							),
   147  						)
   148  					})
   149  
   150  					It("returns them in Response", func() {
   151  						req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/v2/foo", server.URL()), nil)
   152  						Expect(err).ToNot(HaveOccurred())
   153  						request := &Request{Request: req}
   154  
   155  						var response Response
   156  						err = connection.Make(request, &response)
   157  						Expect(err).NotTo(HaveOccurred())
   158  
   159  						Expect(server.ReceivedRequests()).To(HaveLen(1))
   160  
   161  						warnings := response.Warnings
   162  						Expect(warnings).ToNot(BeNil())
   163  						Expect(warnings).To(HaveLen(4))
   164  						Expect(warnings).To(ContainElement("42"))
   165  						Expect(warnings).To(ContainElement("Ed McMann"))
   166  						Expect(warnings).To(ContainElement("the 1942 doggers"))
   167  						Expect(warnings).To(ContainElement("a,b"))
   168  					})
   169  				})
   170  
   171  				When("there are no warnings", func() {
   172  					BeforeEach(func() {
   173  						server.AppendHandlers(
   174  							CombineHandlers(
   175  								VerifyRequest(http.MethodGet, "/v2/foo"),
   176  								RespondWith(http.StatusOK, "{}"),
   177  							),
   178  						)
   179  					})
   180  
   181  					It("returns them in Response", func() {
   182  						req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/v2/foo", server.URL()), nil)
   183  						Expect(err).ToNot(HaveOccurred())
   184  						request := &Request{Request: req}
   185  
   186  						var response Response
   187  						err = connection.Make(request, &response)
   188  						Expect(err).NotTo(HaveOccurred())
   189  
   190  						Expect(response.Warnings).To(BeEmpty())
   191  						Expect(server.ReceivedRequests()).To(HaveLen(1))
   192  					})
   193  				})
   194  			})
   195  		})
   196  
   197  		Describe("Errors", func() {
   198  			When("the server does not exist", func() {
   199  				BeforeEach(func() {
   200  					connection = NewConnection(Config{})
   201  				})
   202  
   203  				It("returns a RequestError", func() {
   204  					req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/v2/foo", "http://garbledyguk.com"), 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(HaveOccurred())
   211  
   212  					requestErr, ok := err.(ccerror.RequestError)
   213  					Expect(ok).To(BeTrue())
   214  					Expect(requestErr.Error()).To(MatchRegexp(".*http://garbledyguk.com/v2/foo.*[nN]o such host"))
   215  				})
   216  			})
   217  
   218  			When("the server does not have a verified certificate", func() {
   219  				Context("skipSSLValidation is false", func() {
   220  					BeforeEach(func() {
   221  						server.AppendHandlers(
   222  							CombineHandlers(
   223  								VerifyRequest(http.MethodGet, "/v2/foo"),
   224  							),
   225  						)
   226  
   227  						connection = NewConnection(Config{})
   228  					})
   229  
   230  					It("returns a UnverifiedServerError", func() {
   231  						req, err := http.NewRequest(http.MethodGet, server.URL(), nil)
   232  						Expect(err).ToNot(HaveOccurred())
   233  						request := &Request{Request: req}
   234  
   235  						var response Response
   236  						err = connection.Make(request, &response)
   237  						Expect(err).To(MatchError(ccerror.UnverifiedServerError{URL: server.URL()}))
   238  					})
   239  				})
   240  			})
   241  
   242  			When("the server's certificate does not match the hostname", func() {
   243  				Context("skipSSLValidation is false", func() {
   244  					BeforeEach(func() {
   245  						if runtime.GOOS == "windows" {
   246  							Skip("ssl validation has a different order on windows, will not be returned properly")
   247  						}
   248  						server.AppendHandlers(
   249  							CombineHandlers(
   250  								VerifyRequest(http.MethodGet, "/"),
   251  							),
   252  						)
   253  
   254  						connection = NewConnection(Config{})
   255  					})
   256  
   257  					// loopback.cli.fun is a custom DNS record setup to point to 127.0.0.1
   258  					It("returns a SSLValidationHostnameError", func() {
   259  						altHostURL := strings.Replace(server.URL(), "127.0.0.1", "loopback.cli.fun", -1)
   260  						req, err := http.NewRequest(http.MethodGet, altHostURL, nil)
   261  						Expect(err).ToNot(HaveOccurred())
   262  						request := &Request{Request: req}
   263  
   264  						var response Response
   265  						err = connection.Make(request, &response)
   266  						Expect(err).To(MatchError(ccerror.SSLValidationHostnameError{
   267  							Message: "x509: certificate is valid for example.com, not loopback.cli.fun",
   268  						}))
   269  					})
   270  				})
   271  			})
   272  
   273  			Describe("RawHTTPStatusError", func() {
   274  				var ccResponse string
   275  				BeforeEach(func() {
   276  					ccResponse = `{
   277  						"code": 90004,
   278  						"description": "The service binding could not be found: some-guid",
   279  						"error_code": "CF-ServiceBindingNotFound"
   280  					}`
   281  
   282  					server.AppendHandlers(
   283  						CombineHandlers(
   284  							VerifyRequest(http.MethodGet, "/v2/foo"),
   285  							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"}}),
   286  						),
   287  					)
   288  				})
   289  
   290  				It("returns a CCRawResponse", func() {
   291  					req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/v2/foo", server.URL()), nil)
   292  					Expect(err).ToNot(HaveOccurred())
   293  					request := &Request{Request: req}
   294  
   295  					var response Response
   296  					err = connection.Make(request, &response)
   297  					Expect(err).To(MatchError(ccerror.RawHTTPStatusError{
   298  						StatusCode:  http.StatusNotFound,
   299  						RawResponse: []byte(ccResponse),
   300  						RequestIDs:  []string{"6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95", "6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95::7445d9db-c31e-410d-8dc5-9f79ec3fc26f"},
   301  					}))
   302  
   303  					Expect(server.ReceivedRequests()).To(HaveLen(1))
   304  				})
   305  			})
   306  		})
   307  	})
   308  })