github.com/jghiloni/cli@v6.28.1-0.20170628223758-0ce05fe032a2+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("X-Cf-Warnings", func() { 116 BeforeEach(func() { 117 server.AppendHandlers( 118 CombineHandlers( 119 VerifyRequest(http.MethodGet, "/v2/foo"), 120 RespondWith(http.StatusOK, "{}", http.Header{"X-Cf-Warnings": {"42, Ed McMann, the 1942 doggers"}}), 121 ), 122 ) 123 }) 124 125 It("returns them in Response", 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 136 warnings := response.Warnings 137 Expect(warnings).ToNot(BeNil()) 138 Expect(warnings).To(HaveLen(3)) 139 Expect(warnings).To(ContainElement("42")) 140 Expect(warnings).To(ContainElement("Ed McMann")) 141 Expect(warnings).To(ContainElement("the 1942 doggers")) 142 }) 143 }) 144 }) 145 146 Describe("Errors", func() { 147 Context("when the server does not exist", func() { 148 BeforeEach(func() { 149 connection = NewConnection(Config{}) 150 }) 151 152 It("returns a RequestError", func() { 153 req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/v2/foo", "http://garbledyguk.com"), nil) 154 Expect(err).ToNot(HaveOccurred()) 155 request := &Request{Request: req} 156 157 var response Response 158 err = connection.Make(request, &response) 159 Expect(err).To(HaveOccurred()) 160 161 requestErr, ok := err.(ccerror.RequestError) 162 Expect(ok).To(BeTrue()) 163 Expect(requestErr.Error()).To(MatchRegexp(".*http://garbledyguk.com/v2/foo.*[nN]o such host")) 164 }) 165 }) 166 167 Context("when the server does not have a verified certificate", func() { 168 Context("skipSSLValidation is false", func() { 169 BeforeEach(func() { 170 server.AppendHandlers( 171 CombineHandlers( 172 VerifyRequest(http.MethodGet, "/v2/foo"), 173 ), 174 ) 175 176 connection = NewConnection(Config{}) 177 }) 178 179 It("returns a UnverifiedServerError", func() { 180 req, err := http.NewRequest(http.MethodGet, server.URL(), nil) 181 Expect(err).ToNot(HaveOccurred()) 182 request := &Request{Request: req} 183 184 var response Response 185 err = connection.Make(request, &response) 186 Expect(err).To(MatchError(ccerror.UnverifiedServerError{URL: server.URL()})) 187 }) 188 }) 189 }) 190 191 Context("when the server's certificate does not match the hostname", func() { 192 Context("skipSSLValidation is false", func() { 193 BeforeEach(func() { 194 if runtime.GOOS == "windows" { 195 Skip("ssl validation has a different order on windows, will not be returned properly") 196 } 197 server.AppendHandlers( 198 CombineHandlers( 199 VerifyRequest(http.MethodGet, "/"), 200 ), 201 ) 202 203 connection = NewConnection(Config{}) 204 }) 205 206 // loopback.cli.ci.cf-app.com is a custom DNS record setup to point to 127.0.0.1 207 It("returns a SSLValidationHostnameError", func() { 208 altHostURL := strings.Replace(server.URL(), "127.0.0.1", "loopback.cli.ci.cf-app.com", -1) 209 req, err := http.NewRequest(http.MethodGet, altHostURL, nil) 210 Expect(err).ToNot(HaveOccurred()) 211 request := &Request{Request: req} 212 213 var response Response 214 err = connection.Make(request, &response) 215 Expect(err).To(MatchError(ccerror.SSLValidationHostnameError{ 216 Message: "x509: certificate is valid for example.com, not loopback.cli.ci.cf-app.com", 217 })) 218 }) 219 }) 220 }) 221 222 Describe("RawHTTPStatusError", func() { 223 var ccResponse string 224 BeforeEach(func() { 225 ccResponse = `{ 226 "code": 90004, 227 "description": "The service binding could not be found: some-guid", 228 "error_code": "CF-ServiceBindingNotFound" 229 }` 230 231 server.AppendHandlers( 232 CombineHandlers( 233 VerifyRequest(http.MethodGet, "/v2/foo"), 234 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"}}), 235 ), 236 ) 237 }) 238 239 It("returns a CCRawResponse", func() { 240 req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("%s/v2/foo", server.URL()), nil) 241 Expect(err).ToNot(HaveOccurred()) 242 request := &Request{Request: req} 243 244 var response Response 245 err = connection.Make(request, &response) 246 Expect(err).To(MatchError(ccerror.RawHTTPStatusError{ 247 StatusCode: http.StatusNotFound, 248 RawResponse: []byte(ccResponse), 249 RequestIDs: []string{"6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95", "6e0b4379-f5f7-4b2b-56b0-9ab7e96eed95::7445d9db-c31e-410d-8dc5-9f79ec3fc26f"}, 250 })) 251 252 Expect(server.ReceivedRequests()).To(HaveLen(1)) 253 }) 254 }) 255 }) 256 }) 257 })