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