github.com/wanddynosios/cli/v8@v8.7.9-0.20240221182337-1a92e3a7017f/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 Describe("X-Cf-Warnings", func() { 139 When("there are 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,a%2Cb"}}), 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(4)) 163 Expect(warnings).To(ContainElement("42")) 164 Expect(warnings).To(ContainElement("Ed McMann")) 165 Expect(warnings).To(ContainElement("the 1942 doggers")) 166 Expect(warnings).To(ContainElement("a,b")) 167 }) 168 }) 169 170 When("there are warnings using multi-value header", func() { 171 BeforeEach(func() { 172 server.AppendHandlers( 173 CombineHandlers( 174 VerifyRequest(http.MethodGet, "/v2/foo"), 175 RespondWith(http.StatusOK, "{}", http.Header{"X-Cf-Warnings": { 176 "42,+Ed+McMann,+the+1942+doggers,a%2Cb", 177 "something,simpler", 178 }}), 179 ), 180 ) 181 }) 182 183 It("returns them in Response", func() { 184 req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/v2/foo", server.URL()), nil) 185 Expect(err).ToNot(HaveOccurred()) 186 request := &Request{Request: req} 187 188 var response Response 189 err = connection.Make(request, &response) 190 Expect(err).NotTo(HaveOccurred()) 191 192 Expect(server.ReceivedRequests()).To(HaveLen(1)) 193 194 warnings := response.Warnings 195 Expect(warnings).ToNot(BeNil()) 196 Expect(warnings).To(HaveLen(6)) 197 Expect(warnings).To(ContainElement("42")) 198 Expect(warnings).To(ContainElement("Ed McMann")) 199 Expect(warnings).To(ContainElement("the 1942 doggers")) 200 Expect(warnings).To(ContainElement("a,b")) 201 Expect(warnings).To(ContainElement("something")) 202 Expect(warnings).To(ContainElement("simpler")) 203 }) 204 }) 205 206 When("there are no warnings", func() { 207 BeforeEach(func() { 208 server.AppendHandlers( 209 CombineHandlers( 210 VerifyRequest(http.MethodGet, "/v2/foo"), 211 RespondWith(http.StatusOK, "{}"), 212 ), 213 ) 214 }) 215 216 It("returns them in Response", func() { 217 req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/v2/foo", server.URL()), nil) 218 Expect(err).ToNot(HaveOccurred()) 219 request := &Request{Request: req} 220 221 var response Response 222 err = connection.Make(request, &response) 223 Expect(err).NotTo(HaveOccurred()) 224 225 Expect(response.Warnings).To(BeEmpty()) 226 Expect(server.ReceivedRequests()).To(HaveLen(1)) 227 }) 228 }) 229 }) 230 }) 231 232 Describe("Errors", func() { 233 When("the server does not exist", func() { 234 BeforeEach(func() { 235 connection = NewConnection(Config{}) 236 }) 237 238 It("returns a RequestError", func() { 239 req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/v2/foo", "http://garbledyguk.com"), nil) 240 Expect(err).ToNot(HaveOccurred()) 241 request := &Request{Request: req} 242 243 var response Response 244 err = connection.Make(request, &response) 245 Expect(err).To(HaveOccurred()) 246 247 requestErr, ok := err.(ccerror.RequestError) 248 Expect(ok).To(BeTrue()) 249 Expect(requestErr.Error()).To(MatchRegexp(".*http://garbledyguk.com/v2/foo.*[nN]o such host")) 250 }) 251 }) 252 253 When("the server does not have a verified certificate", func() { 254 Context("skipSSLValidation is false", func() { 255 BeforeEach(func() { 256 if runtime.GOOS == "darwin" { 257 Skip("ssl verification is different on darwin") 258 } 259 server.AppendHandlers( 260 CombineHandlers( 261 VerifyRequest(http.MethodGet, "/v2/foo"), 262 ), 263 ) 264 265 connection = NewConnection(Config{}) 266 }) 267 268 It("returns a UnverifiedServerError", func() { 269 req, err := http.NewRequest(http.MethodGet, server.URL(), nil) 270 Expect(err).ToNot(HaveOccurred()) 271 request := &Request{Request: req} 272 273 var response Response 274 err = connection.Make(request, &response) 275 Expect(err).To(MatchError(ccerror.UnverifiedServerError{URL: server.URL()})) 276 }) 277 }) 278 }) 279 280 When("the server's certificate does not match the hostname", func() { 281 Context("skipSSLValidation is false", func() { 282 BeforeEach(func() { 283 if runtime.GOOS == "windows" || runtime.GOOS == "darwin" { 284 Skip("ssl validation has a different order on windows/darwin, will not be returned properly") 285 } 286 server.AppendHandlers( 287 CombineHandlers( 288 VerifyRequest(http.MethodGet, "/"), 289 ), 290 ) 291 292 connection = NewConnection(Config{}) 293 }) 294 295 // loopback.cli.fun is a custom DNS record setup to point to 127.0.0.1 296 It("returns a SSLValidationHostnameError", func() { 297 altHostURL := strings.Replace(server.URL(), "127.0.0.1", "loopback.cli.fun", -1) 298 req, err := http.NewRequest(http.MethodGet, altHostURL, nil) 299 Expect(err).ToNot(HaveOccurred()) 300 request := &Request{Request: req} 301 302 var response Response 303 err = connection.Make(request, &response) 304 Expect(err).To(MatchError(ccerror.SSLValidationHostnameError{ 305 Message: "x509: certificate is valid for example.com, not loopback.cli.fun", 306 })) 307 }) 308 }) 309 }) 310 311 Describe("RawHTTPStatusError", func() { 312 var ccResponse string 313 BeforeEach(func() { 314 ccResponse = `{ 315 "code": 90004, 316 "description": "The service binding could not be found: some-guid", 317 "error_code": "CF-ServiceBindingNotFound" 318 }` 319 320 server.AppendHandlers( 321 CombineHandlers( 322 VerifyRequest(http.MethodGet, "/v2/foo"), 323 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"}}), 324 ), 325 ) 326 }) 327 328 It("returns a CCRawResponse", func() { 329 req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/v2/foo", server.URL()), nil) 330 Expect(err).ToNot(HaveOccurred()) 331 request := &Request{Request: req} 332 333 var response Response 334 err = connection.Make(request, &response) 335 Expect(err).To(MatchError(ccerror.RawHTTPStatusError{ 336 StatusCode: http.StatusNotFound, 337 RawResponse: []byte(ccResponse), 338 RequestIDs: []string{"6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95", "6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95::7445d9db-c31e-410d-8dc5-9f79ec3fc26f"}, 339 })) 340 341 Expect(server.ReceivedRequests()).To(HaveLen(1)) 342 }) 343 }) 344 }) 345 }) 346 })