github.com/wanddynosios/cli/v8@v8.7.9-0.20240221182337-1a92e3a7017f/api/uaa/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/uaa" 13 "code.cloudfoundry.org/cli/api/uaa/uaafakes" 14 . "code.cloudfoundry.org/cli/api/uaa/wrapper" 15 "code.cloudfoundry.org/cli/api/uaa/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 *uaafakes.FakeConnection 24 fakeOutput *wrapperfakes.FakeRequestLoggerOutput 25 26 wrapper uaa.Connection 27 28 request *http.Request 29 response *uaa.Response 30 makeErr error 31 ) 32 33 BeforeEach(func() { 34 fakeConnection = new(uaafakes.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 = &uaa.Response{ 55 RawResponse: []byte("some-response-body"), 56 HTTPResponse: &http.Response{}, 57 } 58 }) 59 60 JustBeforeEach(func() { 61 makeErr = wrapper.Make(request, response) 62 }) 63 64 Describe("Make", func() { 65 It("outputs the request", func() { 66 Expect(makeErr).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 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(makeErr).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 When("an Set-Cookie header is in the request", func() { 110 BeforeEach(func() { 111 request.Header = http.Header{"Set-Cookie": []string{"should not be shown"}} 112 }) 113 114 It("redacts the contents of the Set-Cookie header", func() { 115 Expect(makeErr).NotTo(HaveOccurred()) 116 Expect(fakeOutput.DisplayHeaderCallCount()).To(Equal(1)) 117 key, value := fakeOutput.DisplayHeaderArgsForCall(0) 118 Expect(key).To(Equal("Set-Cookie")) 119 Expect(value).To(Equal("[PRIVATE DATA HIDDEN]")) 120 }) 121 }) 122 123 When("an Location header is in the request", func() { 124 When("an Location header has an ssh code in the request", func() { 125 BeforeEach(func() { 126 request.Header = http.Header{"Location": []string{"foo.bar/login?code=pleaseRedact"}} 127 }) 128 129 It("redacts the code query parameter of the Location header", func() { 130 Expect(makeErr).NotTo(HaveOccurred()) 131 Expect(fakeOutput.DisplayHeaderCallCount()).To(Equal(1)) 132 key, value := fakeOutput.DisplayHeaderArgsForCall(0) 133 Expect(key).To(Equal("Location")) 134 Expect(value).To(ContainSubstring("[PRIVATE DATA HIDDEN]")) 135 Expect(value).ToNot(ContainSubstring("pleaseRedact")) 136 }) 137 }) 138 139 When("an Location header has a random query param in the request", func() { 140 BeforeEach(func() { 141 request.Header = http.Header{"Location": []string{"foo.bar/login?param=pleasePersist"}} 142 }) 143 144 It("persists the query parameter of the Location header", func() { 145 Expect(makeErr).NotTo(HaveOccurred()) 146 Expect(fakeOutput.DisplayHeaderCallCount()).To(Equal(1)) 147 key, value := fakeOutput.DisplayHeaderArgsForCall(0) 148 Expect(key).To(Equal("Location")) 149 Expect(value).To(ContainSubstring("pleasePersist")) 150 }) 151 }) 152 }) 153 154 When("passed a body", func() { 155 var originalBody io.ReadCloser 156 157 When("the body is not JSON", func() { 158 BeforeEach(func() { 159 originalBody = ioutil.NopCloser(bytes.NewReader([]byte("foo"))) 160 request.Body = originalBody 161 }) 162 163 It("outputs the body", func() { 164 Expect(makeErr).NotTo(HaveOccurred()) 165 166 Expect(fakeOutput.DisplayBodyCallCount()).To(BeNumerically(">=", 1)) 167 Expect(fakeOutput.DisplayBodyArgsForCall(0)).To(Equal([]byte("foo"))) 168 169 bytes, err := ioutil.ReadAll(request.Body) 170 Expect(err).NotTo(HaveOccurred()) 171 Expect(bytes).To(Equal([]byte("foo"))) 172 }) 173 }) 174 175 When("the body is JSON", func() { 176 var jsonBody string 177 178 BeforeEach(func() { 179 jsonBody = `{"some-key": "some-value"}` 180 originalBody = ioutil.NopCloser(bytes.NewReader([]byte(jsonBody))) 181 request.Body = originalBody 182 request.Header.Add("Content-Type", "application/json") 183 }) 184 185 It("properly displays the JSON body", func() { 186 Expect(makeErr).NotTo(HaveOccurred()) 187 188 Expect(fakeOutput.DisplayJSONBodyCallCount()).To(BeNumerically(">=", 1)) 189 Expect(fakeOutput.DisplayJSONBodyArgsForCall(0)).To(Equal([]byte(jsonBody))) 190 191 bytes, err := ioutil.ReadAll(request.Body) 192 Expect(err).NotTo(HaveOccurred()) 193 Expect(bytes).To(Equal([]byte(jsonBody))) 194 }) 195 }) 196 }) 197 198 When("an error occurs while trying to log the request", func() { 199 var expectedErr error 200 201 BeforeEach(func() { 202 expectedErr = errors.New("this should never block the request") 203 204 calledOnce := false 205 fakeOutput.StartStub = func() error { 206 if !calledOnce { 207 calledOnce = true 208 return expectedErr 209 } 210 return nil 211 } 212 }) 213 214 It("should display the error and continue on", func() { 215 Expect(makeErr).NotTo(HaveOccurred()) 216 217 Expect(fakeOutput.HandleInternalErrorCallCount()).To(Equal(1)) 218 Expect(fakeOutput.HandleInternalErrorArgsForCall(0)).To(MatchError(expectedErr)) 219 }) 220 }) 221 222 When("the request is successful", func() { 223 BeforeEach(func() { 224 response = &uaa.Response{ 225 RawResponse: []byte("some-response-body"), 226 HTTPResponse: &http.Response{ 227 Proto: "HTTP/1.1", 228 Status: "200 OK", 229 Header: http.Header{ 230 "BBBBB": {"second"}, 231 "AAAAA": {"first"}, 232 "CCCCC": {"third"}, 233 }, 234 }, 235 } 236 }) 237 238 It("outputs the response", func() { 239 Expect(makeErr).NotTo(HaveOccurred()) 240 241 Expect(fakeOutput.DisplayTypeCallCount()).To(Equal(2)) 242 name, date := fakeOutput.DisplayTypeArgsForCall(1) 243 Expect(name).To(Equal("RESPONSE")) 244 Expect(date).To(BeTemporally("~", time.Now(), time.Second)) 245 246 Expect(fakeOutput.DisplayResponseHeaderCallCount()).To(Equal(1)) 247 protocol, status := fakeOutput.DisplayResponseHeaderArgsForCall(0) 248 Expect(protocol).To(Equal("HTTP/1.1")) 249 Expect(status).To(Equal("200 OK")) 250 251 Expect(fakeOutput.DisplayHeaderCallCount()).To(BeNumerically(">=", 6)) 252 name, value := fakeOutput.DisplayHeaderArgsForCall(3) 253 Expect(name).To(Equal("AAAAA")) 254 Expect(value).To(Equal("first")) 255 name, value = fakeOutput.DisplayHeaderArgsForCall(4) 256 Expect(name).To(Equal("BBBBB")) 257 Expect(value).To(Equal("second")) 258 name, value = fakeOutput.DisplayHeaderArgsForCall(5) 259 Expect(name).To(Equal("CCCCC")) 260 Expect(value).To(Equal("third")) 261 262 Expect(fakeOutput.DisplayJSONBodyCallCount()).To(BeNumerically(">=", 1)) 263 Expect(fakeOutput.DisplayJSONBodyArgsForCall(0)).To(Equal([]byte("some-response-body"))) 264 }) 265 }) 266 267 When("the request is unsuccessful", func() { 268 var expectedErr error 269 270 BeforeEach(func() { 271 expectedErr = errors.New("banana") 272 fakeConnection.MakeReturns(expectedErr) 273 }) 274 275 When("the http response is not set", func() { 276 BeforeEach(func() { 277 response = &uaa.Response{} 278 }) 279 280 It("outputs nothing", func() { 281 Expect(makeErr).To(MatchError(expectedErr)) 282 Expect(fakeOutput.DisplayResponseHeaderCallCount()).To(Equal(0)) 283 }) 284 }) 285 286 When("the http response is set", func() { 287 BeforeEach(func() { 288 response = &uaa.Response{ 289 RawResponse: []byte("some-error-body"), 290 HTTPResponse: &http.Response{ 291 Proto: "HTTP/1.1", 292 Status: "200 OK", 293 Header: http.Header{ 294 "BBBBB": {"second"}, 295 "AAAAA": {"first"}, 296 "CCCCC": {"third"}, 297 }, 298 }, 299 } 300 }) 301 302 It("outputs the response", func() { 303 Expect(makeErr).To(MatchError(expectedErr)) 304 305 Expect(fakeOutput.DisplayTypeCallCount()).To(Equal(2)) 306 name, date := fakeOutput.DisplayTypeArgsForCall(1) 307 Expect(name).To(Equal("RESPONSE")) 308 Expect(date).To(BeTemporally("~", time.Now(), time.Second)) 309 310 Expect(fakeOutput.DisplayResponseHeaderCallCount()).To(Equal(1)) 311 protocol, status := fakeOutput.DisplayResponseHeaderArgsForCall(0) 312 Expect(protocol).To(Equal("HTTP/1.1")) 313 Expect(status).To(Equal("200 OK")) 314 315 Expect(fakeOutput.DisplayHeaderCallCount()).To(BeNumerically(">=", 6)) 316 name, value := fakeOutput.DisplayHeaderArgsForCall(3) 317 Expect(name).To(Equal("AAAAA")) 318 Expect(value).To(Equal("first")) 319 name, value = fakeOutput.DisplayHeaderArgsForCall(4) 320 Expect(name).To(Equal("BBBBB")) 321 Expect(value).To(Equal("second")) 322 name, value = fakeOutput.DisplayHeaderArgsForCall(5) 323 Expect(name).To(Equal("CCCCC")) 324 Expect(value).To(Equal("third")) 325 326 Expect(fakeOutput.DisplayJSONBodyCallCount()).To(BeNumerically(">=", 1)) 327 Expect(fakeOutput.DisplayJSONBodyArgsForCall(0)).To(Equal([]byte("some-error-body"))) 328 }) 329 }) 330 }) 331 332 When("an error occurs while trying to log the response", func() { 333 var ( 334 originalErr error 335 expectedErr error 336 ) 337 338 BeforeEach(func() { 339 originalErr = errors.New("this error should not be overwritten") 340 fakeConnection.MakeReturns(originalErr) 341 342 expectedErr = errors.New("this should never block the request") 343 344 calledOnce := false 345 fakeOutput.StartStub = func() error { 346 if !calledOnce { 347 calledOnce = true 348 return nil 349 } 350 return expectedErr 351 } 352 }) 353 354 It("should display the error and continue on", func() { 355 Expect(makeErr).To(MatchError(originalErr)) 356 357 Expect(fakeOutput.HandleInternalErrorCallCount()).To(Equal(1)) 358 Expect(fakeOutput.HandleInternalErrorArgsForCall(0)).To(MatchError(expectedErr)) 359 }) 360 }) 361 362 It("starts and stops the output", func() { 363 Expect(makeErr).ToNot(HaveOccurred()) 364 Expect(fakeOutput.StartCallCount()).To(Equal(2)) 365 Expect(fakeOutput.StopCallCount()).To(Equal(2)) 366 }) 367 368 When("displaying the logs have an error", func() { 369 var expectedErr error 370 371 BeforeEach(func() { 372 expectedErr = errors.New("Display error on request") 373 fakeOutput.StartReturns(expectedErr) 374 }) 375 376 It("calls handle internal error", func() { 377 Expect(makeErr).ToNot(HaveOccurred()) 378 379 Expect(fakeOutput.HandleInternalErrorCallCount()).To(Equal(2)) 380 Expect(fakeOutput.HandleInternalErrorArgsForCall(0)).To(MatchError(expectedErr)) 381 Expect(fakeOutput.HandleInternalErrorArgsForCall(1)).To(MatchError(expectedErr)) 382 }) 383 }) 384 }) 385 })