github.com/loggregator/cli@v6.33.1-0.20180224010324-82334f081791+incompatible/api/cloudcontroller/wrapper/request_logger_test.go (about) 1 package wrapper_test 2 3 import ( 4 "bytes" 5 "errors" 6 "io/ioutil" 7 "net/http" 8 "net/url" 9 "time" 10 11 "code.cloudfoundry.org/cli/api/cloudcontroller" 12 "code.cloudfoundry.org/cli/api/cloudcontroller/cloudcontrollerfakes" 13 . "code.cloudfoundry.org/cli/api/cloudcontroller/wrapper" 14 "code.cloudfoundry.org/cli/api/cloudcontroller/wrapper/wrapperfakes" 15 16 . "github.com/onsi/ginkgo" 17 . "github.com/onsi/gomega" 18 ) 19 20 var _ = Describe("Request Logger", func() { 21 var ( 22 fakeConnection *cloudcontrollerfakes.FakeConnection 23 fakeOutput *wrapperfakes.FakeRequestLoggerOutput 24 25 wrapper cloudcontroller.Connection 26 27 request *cloudcontroller.Request 28 response *cloudcontroller.Response 29 makeErr error 30 ) 31 32 BeforeEach(func() { 33 fakeConnection = new(cloudcontrollerfakes.FakeConnection) 34 fakeOutput = new(wrapperfakes.FakeRequestLoggerOutput) 35 36 wrapper = NewRequestLogger(fakeOutput).Wrap(fakeConnection) 37 38 body := bytes.NewReader([]byte("foo")) 39 40 req, err := http.NewRequest(http.MethodGet, "https://foo.bar.com/banana", body) 41 Expect(err).NotTo(HaveOccurred()) 42 43 req.URL.RawQuery = url.Values{ 44 "query1": {"a"}, 45 "query2": {"b"}, 46 }.Encode() 47 48 headers := http.Header{} 49 headers.Add("Aghi", "bar") 50 headers.Add("Abc", "json") 51 headers.Add("Adef", "application/json") 52 req.Header = headers 53 54 response = &cloudcontroller.Response{ 55 RawResponse: []byte("some-response-body"), 56 HTTPResponse: &http.Response{}, 57 } 58 request = cloudcontroller.NewRequest(req, body) 59 }) 60 61 JustBeforeEach(func() { 62 makeErr = wrapper.Make(request, response) 63 }) 64 65 Describe("Make", func() { 66 It("outputs the request", func() { 67 Expect(makeErr).NotTo(HaveOccurred()) 68 69 Expect(fakeOutput.DisplayTypeCallCount()).To(BeNumerically(">=", 1)) 70 name, date := fakeOutput.DisplayTypeArgsForCall(0) 71 Expect(name).To(Equal("REQUEST")) 72 Expect(date).To(BeTemporally("~", time.Now(), time.Second)) 73 74 Expect(fakeOutput.DisplayRequestHeaderCallCount()).To(Equal(1)) 75 method, uri, protocol := fakeOutput.DisplayRequestHeaderArgsForCall(0) 76 Expect(method).To(Equal(http.MethodGet)) 77 Expect(uri).To(MatchRegexp("/banana\\?(?:query1=a&query2=b|query2=b&query1=a)")) 78 Expect(protocol).To(Equal("HTTP/1.1")) 79 80 Expect(fakeOutput.DisplayHostCallCount()).To(Equal(1)) 81 host := fakeOutput.DisplayHostArgsForCall(0) 82 Expect(host).To(Equal("foo.bar.com")) 83 84 Expect(fakeOutput.DisplayHeaderCallCount()).To(BeNumerically(">=", 3)) 85 name, value := fakeOutput.DisplayHeaderArgsForCall(0) 86 Expect(name).To(Equal("Abc")) 87 Expect(value).To(Equal("json")) 88 name, value = fakeOutput.DisplayHeaderArgsForCall(1) 89 Expect(name).To(Equal("Adef")) 90 Expect(value).To(Equal("application/json")) 91 name, value = fakeOutput.DisplayHeaderArgsForCall(2) 92 Expect(name).To(Equal("Aghi")) 93 Expect(value).To(Equal("bar")) 94 95 Expect(fakeOutput.DisplayMessageCallCount()).To(Equal(0)) 96 }) 97 98 Context("when an authorization header is in the request", func() { 99 BeforeEach(func() { 100 request.Header = http.Header{"Authorization": []string{"should not be shown"}} 101 }) 102 103 It("redacts the contents of the authorization header", func() { 104 Expect(makeErr).NotTo(HaveOccurred()) 105 Expect(fakeOutput.DisplayHeaderCallCount()).To(Equal(1)) 106 key, value := fakeOutput.DisplayHeaderArgsForCall(0) 107 Expect(key).To(Equal("Authorization")) 108 Expect(value).To(Equal("[PRIVATE DATA HIDDEN]")) 109 }) 110 }) 111 112 Context("when passed a body", func() { 113 Context("when the request's Content-Type is application/json", func() { 114 BeforeEach(func() { 115 request.Header.Set("Content-Type", "application/json") 116 }) 117 118 It("outputs the body", func() { 119 Expect(makeErr).NotTo(HaveOccurred()) 120 121 Expect(fakeOutput.DisplayJSONBodyCallCount()).To(BeNumerically(">=", 1)) 122 Expect(fakeOutput.DisplayJSONBodyArgsForCall(0)).To(Equal([]byte("foo"))) 123 124 bytes, err := ioutil.ReadAll(request.Body) 125 Expect(err).NotTo(HaveOccurred()) 126 Expect(bytes).To(Equal([]byte("foo"))) 127 }) 128 }) 129 130 Context("when the request's Content-Type is application/x-www-form-urlencoded", func() { 131 BeforeEach(func() { 132 request.Header.Set("Content-Type", "application/x-www-form-urlencoded") 133 }) 134 135 It("outputs the body", func() { 136 Expect(makeErr).NotTo(HaveOccurred()) 137 138 bytes, err := ioutil.ReadAll(request.Body) 139 Expect(err).NotTo(HaveOccurred()) 140 Expect(bytes).To(Equal([]byte("foo"))) 141 Expect(fakeOutput.DisplayMessageCallCount()).To(Equal(1)) 142 Expect(fakeOutput.DisplayMessageArgsForCall(0)).To(Equal("[application/x-www-form-urlencoded foo]")) 143 }) 144 }) 145 146 Context("when request's Content-Type is anything else", func() { 147 BeforeEach(func() { 148 request.Header.Set("Content-Type", "banana;rama") 149 }) 150 151 It("does not display the body", func() { 152 Expect(makeErr).NotTo(HaveOccurred()) 153 Expect(fakeOutput.DisplayJSONBodyCallCount()).To(Equal(1)) // Once for response body only 154 Expect(fakeOutput.DisplayMessageCallCount()).To(Equal(1)) 155 Expect(fakeOutput.DisplayMessageArgsForCall(0)).To(Equal("[banana Content Hidden]")) 156 }) 157 }) 158 }) 159 160 Context("when an error occures while trying to log the request", func() { 161 var expectedErr error 162 163 BeforeEach(func() { 164 expectedErr = errors.New("this should never block the request") 165 166 calledOnce := false 167 fakeOutput.StartStub = func() error { 168 if !calledOnce { 169 calledOnce = true 170 return expectedErr 171 } 172 return nil 173 } 174 }) 175 176 It("should display the error and continue on", func() { 177 Expect(makeErr).NotTo(HaveOccurred()) 178 179 Expect(fakeOutput.HandleInternalErrorCallCount()).To(Equal(1)) 180 Expect(fakeOutput.HandleInternalErrorArgsForCall(0)).To(MatchError(expectedErr)) 181 }) 182 }) 183 184 Context("when the request is successful", func() { 185 BeforeEach(func() { 186 response = &cloudcontroller.Response{ 187 RawResponse: []byte("some-response-body"), 188 HTTPResponse: &http.Response{ 189 Proto: "HTTP/1.1", 190 Status: "200 OK", 191 Header: http.Header{ 192 "BBBBB": {"second"}, 193 "AAAAA": {"first"}, 194 "CCCCC": {"third"}, 195 }, 196 }, 197 } 198 }) 199 200 It("outputs the response", func() { 201 Expect(makeErr).NotTo(HaveOccurred()) 202 203 Expect(fakeOutput.DisplayTypeCallCount()).To(Equal(2)) 204 name, date := fakeOutput.DisplayTypeArgsForCall(1) 205 Expect(name).To(Equal("RESPONSE")) 206 Expect(date).To(BeTemporally("~", time.Now(), time.Second)) 207 208 Expect(fakeOutput.DisplayResponseHeaderCallCount()).To(Equal(1)) 209 protocol, status := fakeOutput.DisplayResponseHeaderArgsForCall(0) 210 Expect(protocol).To(Equal("HTTP/1.1")) 211 Expect(status).To(Equal("200 OK")) 212 213 Expect(fakeOutput.DisplayHeaderCallCount()).To(BeNumerically(">=", 6)) 214 name, value := fakeOutput.DisplayHeaderArgsForCall(3) 215 Expect(name).To(Equal("AAAAA")) 216 Expect(value).To(Equal("first")) 217 name, value = fakeOutput.DisplayHeaderArgsForCall(4) 218 Expect(name).To(Equal("BBBBB")) 219 Expect(value).To(Equal("second")) 220 name, value = fakeOutput.DisplayHeaderArgsForCall(5) 221 Expect(name).To(Equal("CCCCC")) 222 Expect(value).To(Equal("third")) 223 224 Expect(fakeOutput.DisplayJSONBodyCallCount()).To(BeNumerically(">=", 1)) 225 Expect(fakeOutput.DisplayJSONBodyArgsForCall(0)).To(Equal([]byte("some-response-body"))) 226 }) 227 }) 228 229 Context("when the request is unsuccessful", func() { 230 var expectedErr error 231 232 BeforeEach(func() { 233 expectedErr = errors.New("banana") 234 fakeConnection.MakeReturns(expectedErr) 235 }) 236 237 Context("when the http response is not set", func() { 238 BeforeEach(func() { 239 response = &cloudcontroller.Response{} 240 }) 241 242 It("outputs nothing", func() { 243 Expect(makeErr).To(MatchError(expectedErr)) 244 Expect(fakeOutput.DisplayResponseHeaderCallCount()).To(Equal(0)) 245 }) 246 }) 247 248 Context("when the http response is set", func() { 249 BeforeEach(func() { 250 response = &cloudcontroller.Response{ 251 RawResponse: []byte("some-error-body"), 252 HTTPResponse: &http.Response{ 253 Proto: "HTTP/1.1", 254 Status: "200 OK", 255 Header: http.Header{ 256 "BBBBB": {"second"}, 257 "AAAAA": {"first"}, 258 "CCCCC": {"third"}, 259 }, 260 }, 261 } 262 }) 263 264 It("outputs the response", func() { 265 Expect(makeErr).To(MatchError(expectedErr)) 266 267 Expect(fakeOutput.DisplayTypeCallCount()).To(Equal(2)) 268 name, date := fakeOutput.DisplayTypeArgsForCall(1) 269 Expect(name).To(Equal("RESPONSE")) 270 Expect(date).To(BeTemporally("~", time.Now(), time.Second)) 271 272 Expect(fakeOutput.DisplayResponseHeaderCallCount()).To(Equal(1)) 273 protocol, status := fakeOutput.DisplayResponseHeaderArgsForCall(0) 274 Expect(protocol).To(Equal("HTTP/1.1")) 275 Expect(status).To(Equal("200 OK")) 276 277 Expect(fakeOutput.DisplayHeaderCallCount()).To(BeNumerically(">=", 6)) 278 name, value := fakeOutput.DisplayHeaderArgsForCall(3) 279 Expect(name).To(Equal("AAAAA")) 280 Expect(value).To(Equal("first")) 281 name, value = fakeOutput.DisplayHeaderArgsForCall(4) 282 Expect(name).To(Equal("BBBBB")) 283 Expect(value).To(Equal("second")) 284 name, value = fakeOutput.DisplayHeaderArgsForCall(5) 285 Expect(name).To(Equal("CCCCC")) 286 Expect(value).To(Equal("third")) 287 288 Expect(fakeOutput.DisplayJSONBodyCallCount()).To(BeNumerically(">=", 1)) 289 Expect(fakeOutput.DisplayJSONBodyArgsForCall(0)).To(Equal([]byte("some-error-body"))) 290 }) 291 }) 292 }) 293 294 Context("when an error occures while trying to log the response", func() { 295 var ( 296 originalErr error 297 expectedErr error 298 ) 299 300 BeforeEach(func() { 301 originalErr = errors.New("this error should not be overwritten") 302 fakeConnection.MakeReturns(originalErr) 303 304 expectedErr = errors.New("this should never block the request") 305 306 calledOnce := false 307 fakeOutput.StartStub = func() error { 308 if !calledOnce { 309 calledOnce = true 310 return nil 311 } 312 return expectedErr 313 } 314 }) 315 316 It("should display the error and continue on", func() { 317 Expect(makeErr).To(MatchError(originalErr)) 318 319 Expect(fakeOutput.HandleInternalErrorCallCount()).To(Equal(1)) 320 Expect(fakeOutput.HandleInternalErrorArgsForCall(0)).To(MatchError(expectedErr)) 321 }) 322 }) 323 324 It("starts and stops the output", func() { 325 Expect(fakeOutput.StartCallCount()).To(Equal(2)) 326 Expect(fakeOutput.StopCallCount()).To(Equal(2)) 327 }) 328 329 Context("when displaying the logs have an error", func() { 330 var expectedErr error 331 BeforeEach(func() { 332 expectedErr = errors.New("Display error on request") 333 fakeOutput.StartReturns(expectedErr) 334 }) 335 336 It("calls handle internal error", func() { 337 Expect(makeErr).ToNot(HaveOccurred()) 338 339 Expect(fakeOutput.HandleInternalErrorCallCount()).To(Equal(2)) 340 Expect(fakeOutput.HandleInternalErrorArgsForCall(0)).To(MatchError(expectedErr)) 341 Expect(fakeOutput.HandleInternalErrorArgsForCall(1)).To(MatchError(expectedErr)) 342 }) 343 }) 344 }) 345 })