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