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