github.com/wanddynosios/cli/v8@v8.7.9-0.20240221182337-1a92e3a7017f/api/plugin/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/plugin" 13 "code.cloudfoundry.org/cli/api/plugin/pluginfakes" 14 . "code.cloudfoundry.org/cli/api/plugin/wrapper" 15 "code.cloudfoundry.org/cli/api/plugin/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 *pluginfakes.FakeConnection 24 fakeOutput *wrapperfakes.FakeRequestLoggerOutput 25 fakeProxyReader *pluginfakes.FakeProxyReader 26 27 wrapper plugin.Connection 28 29 request *http.Request 30 response *plugin.Response 31 makeErr error 32 ) 33 34 BeforeEach(func() { 35 fakeConnection = new(pluginfakes.FakeConnection) 36 fakeOutput = new(wrapperfakes.FakeRequestLoggerOutput) 37 fakeProxyReader = new(pluginfakes.FakeProxyReader) 38 39 wrapper = NewRequestLogger(fakeOutput).Wrap(fakeConnection) 40 41 var err error 42 request, err = http.NewRequest(http.MethodGet, "https://foo.bar.com/banana", nil) 43 Expect(err).NotTo(HaveOccurred()) 44 45 request.URL.RawQuery = url.Values{ 46 "query1": {"a"}, 47 "query2": {"b"}, 48 }.Encode() 49 50 headers := http.Header{} 51 headers.Add("Aghi", "bar") 52 headers.Add("Abc", "json") 53 headers.Add("Adef", "application/json") 54 request.Header = headers 55 56 response = &plugin.Response{ 57 RawResponse: []byte("some-response-body"), 58 HTTPResponse: &http.Response{}, 59 } 60 }) 61 62 JustBeforeEach(func() { 63 makeErr = wrapper.Make(request, response, fakeProxyReader) 64 }) 65 66 Describe("Make", func() { 67 It("outputs the request", func() { 68 Expect(makeErr).NotTo(HaveOccurred()) 69 70 Expect(fakeOutput.DisplayTypeCallCount()).To(BeNumerically(">=", 1)) 71 name, date := fakeOutput.DisplayTypeArgsForCall(0) 72 Expect(name).To(Equal("REQUEST")) 73 Expect(date).To(BeTemporally("~", time.Now(), time.Second)) 74 75 Expect(fakeOutput.DisplayRequestHeaderCallCount()).To(Equal(1)) 76 method, uri, protocol := fakeOutput.DisplayRequestHeaderArgsForCall(0) 77 Expect(method).To(Equal(http.MethodGet)) 78 Expect(uri).To(MatchRegexp("/banana\\?(?:query1=a&query2=b|query2=b&query1=a)")) 79 Expect(protocol).To(Equal("HTTP/1.1")) 80 81 Expect(fakeOutput.DisplayHostCallCount()).To(Equal(1)) 82 host := fakeOutput.DisplayHostArgsForCall(0) 83 Expect(host).To(Equal("foo.bar.com")) 84 85 Expect(fakeOutput.DisplayHeaderCallCount()).To(BeNumerically(">=", 3)) 86 name, value := fakeOutput.DisplayHeaderArgsForCall(0) 87 Expect(name).To(Equal("Abc")) 88 Expect(value).To(Equal("json")) 89 name, value = fakeOutput.DisplayHeaderArgsForCall(1) 90 Expect(name).To(Equal("Adef")) 91 Expect(value).To(Equal("application/json")) 92 name, value = fakeOutput.DisplayHeaderArgsForCall(2) 93 Expect(name).To(Equal("Aghi")) 94 Expect(value).To(Equal("bar")) 95 96 Expect(fakeConnection.MakeCallCount()).To(Equal(1)) 97 _, _, proxyReader := fakeConnection.MakeArgsForCall(0) 98 Expect(proxyReader).To(Equal(fakeProxyReader)) 99 }) 100 101 When("an authorization header is in the request", func() { 102 BeforeEach(func() { 103 request.Header = http.Header{"Authorization": []string{"should not be shown"}} 104 }) 105 106 It("redacts the contents of the authorization header", func() { 107 Expect(makeErr).NotTo(HaveOccurred()) 108 Expect(fakeOutput.DisplayHeaderCallCount()).To(Equal(1)) 109 key, value := fakeOutput.DisplayHeaderArgsForCall(0) 110 Expect(key).To(Equal("Authorization")) 111 Expect(value).To(Equal("[PRIVATE DATA HIDDEN]")) 112 }) 113 }) 114 115 When("a set-cookie header is in the request", func() { 116 BeforeEach(func() { 117 request.Header = http.Header{"Set-Cookie": []string{"should not be shown"}} 118 }) 119 120 It("redacts the contents of the set-cookie header", func() { 121 Expect(makeErr).NotTo(HaveOccurred()) 122 Expect(fakeOutput.DisplayHeaderCallCount()).To(Equal(1)) 123 key, value := fakeOutput.DisplayHeaderArgsForCall(0) 124 Expect(key).To(Equal("Set-Cookie")) 125 Expect(value).To(Equal("[PRIVATE DATA HIDDEN]")) 126 }) 127 }) 128 129 When("passed a body", func() { 130 When("the request's Content-Type is application/json", func() { 131 var originalBody io.ReadCloser 132 133 BeforeEach(func() { 134 request.Header.Set("Content-Type", "application/json") 135 originalBody = ioutil.NopCloser(bytes.NewReader([]byte("foo"))) 136 request.Body = originalBody 137 }) 138 139 It("outputs the body", func() { 140 Expect(makeErr).NotTo(HaveOccurred()) 141 142 Expect(fakeOutput.DisplayJSONBodyCallCount()).To(BeNumerically(">=", 1)) 143 Expect(fakeOutput.DisplayJSONBodyArgsForCall(0)).To(Equal([]byte("foo"))) 144 145 bytes, err := ioutil.ReadAll(request.Body) 146 Expect(err).NotTo(HaveOccurred()) 147 Expect(bytes).To(Equal([]byte("foo"))) 148 }) 149 }) 150 151 When("request's Content-Type is anything else", func() { 152 BeforeEach(func() { 153 request.Header.Set("Content-Type", "banana") 154 }) 155 156 It("does not display the request body", func() { 157 Expect(makeErr).NotTo(HaveOccurred()) 158 Expect(fakeOutput.DisplayJSONBodyCallCount()).To(Equal(0)) 159 }) 160 }) 161 }) 162 163 When("an error occurs while trying to log the request", func() { 164 var expectedErr error 165 166 BeforeEach(func() { 167 expectedErr = errors.New("this should never block the request") 168 169 calledOnce := false 170 fakeOutput.StartStub = func() error { 171 if !calledOnce { 172 calledOnce = true 173 return expectedErr 174 } 175 return nil 176 } 177 }) 178 179 It("should display the error and continue on", func() { 180 Expect(makeErr).NotTo(HaveOccurred()) 181 182 Expect(fakeOutput.HandleInternalErrorCallCount()).To(Equal(1)) 183 Expect(fakeOutput.HandleInternalErrorArgsForCall(0)).To(MatchError(expectedErr)) 184 }) 185 }) 186 187 When("the request is successful", func() { 188 When("the response is JSON", func() { 189 BeforeEach(func() { 190 response = &plugin.Response{ 191 RawResponse: []byte(`{"some-key":"some-value"}`), 192 HTTPResponse: &http.Response{ 193 Proto: "HTTP/1.1", 194 Status: "200 OK", 195 Header: http.Header{ 196 "Content-Type": {"application/json"}, 197 "BBBBB": {"second"}, 198 "AAAAA": {"first"}, 199 "CCCCC": {"third"}, 200 }, 201 Body: ioutil.NopCloser(bytes.NewReader([]byte(`{"some-key":"some-value"}`))), 202 }, 203 } 204 }) 205 206 It("outputs the response", func() { 207 Expect(makeErr).NotTo(HaveOccurred()) 208 209 Expect(fakeOutput.DisplayTypeCallCount()).To(Equal(2)) 210 name, date := fakeOutput.DisplayTypeArgsForCall(1) 211 Expect(name).To(Equal("RESPONSE")) 212 Expect(date).To(BeTemporally("~", time.Now(), time.Second)) 213 214 Expect(fakeOutput.DisplayResponseHeaderCallCount()).To(Equal(1)) 215 protocol, status := fakeOutput.DisplayResponseHeaderArgsForCall(0) 216 Expect(protocol).To(Equal("HTTP/1.1")) 217 Expect(status).To(Equal("200 OK")) 218 219 Expect(fakeOutput.DisplayHeaderCallCount()).To(BeNumerically(">=", 7)) 220 name, value := fakeOutput.DisplayHeaderArgsForCall(3) 221 Expect(name).To(Equal("AAAAA")) 222 Expect(value).To(Equal("first")) 223 name, value = fakeOutput.DisplayHeaderArgsForCall(4) 224 Expect(name).To(Equal("BBBBB")) 225 Expect(value).To(Equal("second")) 226 name, value = fakeOutput.DisplayHeaderArgsForCall(5) 227 Expect(name).To(Equal("CCCCC")) 228 Expect(value).To(Equal("third")) 229 name, value = fakeOutput.DisplayHeaderArgsForCall(6) 230 Expect(name).To(Equal("Content-Type")) 231 Expect(value).To(Equal("application/json")) 232 233 Expect(fakeOutput.DisplayJSONBodyCallCount()).To(BeNumerically(">=", 1)) 234 Expect(fakeOutput.DisplayJSONBodyArgsForCall(0)).To(Equal(response.RawResponse)) 235 236 Expect(fakeOutput.DisplayDumpCallCount()).To(Equal(0)) 237 }) 238 }) 239 240 When("the response is not JSON", func() { 241 BeforeEach(func() { 242 response = &plugin.Response{ 243 RawResponse: []byte(`not JSON`), 244 HTTPResponse: &http.Response{ 245 Proto: "HTTP/1.1", 246 Status: "200 OK", 247 Header: http.Header{ 248 "BBBBB": {"second"}, 249 "AAAAA": {"first"}, 250 "CCCCC": {"third"}, 251 }, 252 Body: ioutil.NopCloser(bytes.NewReader([]byte(`not JSON`))), 253 }, 254 } 255 }) 256 257 It("outputs the response", func() { 258 Expect(makeErr).NotTo(HaveOccurred()) 259 260 Expect(fakeOutput.DisplayTypeCallCount()).To(Equal(2)) 261 name, date := fakeOutput.DisplayTypeArgsForCall(1) 262 Expect(name).To(Equal("RESPONSE")) 263 Expect(date).To(BeTemporally("~", time.Now(), time.Second)) 264 265 Expect(fakeOutput.DisplayResponseHeaderCallCount()).To(Equal(1)) 266 protocol, status := fakeOutput.DisplayResponseHeaderArgsForCall(0) 267 Expect(protocol).To(Equal("HTTP/1.1")) 268 Expect(status).To(Equal("200 OK")) 269 270 Expect(fakeOutput.DisplayHeaderCallCount()).To(BeNumerically(">=", 6)) 271 name, value := fakeOutput.DisplayHeaderArgsForCall(3) 272 Expect(name).To(Equal("AAAAA")) 273 Expect(value).To(Equal("first")) 274 name, value = fakeOutput.DisplayHeaderArgsForCall(4) 275 Expect(name).To(Equal("BBBBB")) 276 Expect(value).To(Equal("second")) 277 name, value = fakeOutput.DisplayHeaderArgsForCall(5) 278 Expect(name).To(Equal("CCCCC")) 279 Expect(value).To(Equal("third")) 280 281 Expect(fakeOutput.DisplayDumpCallCount()).To(Equal(1)) 282 text := fakeOutput.DisplayDumpArgsForCall(0) 283 Expect(text).To(Equal("[NON-JSON BODY CONTENT HIDDEN]")) 284 285 Expect(fakeOutput.DisplayJSONBodyCallCount()).To(Equal(0)) 286 }) 287 }) 288 }) 289 290 When("the request is unsuccessful", func() { 291 var expectedErr error 292 293 BeforeEach(func() { 294 expectedErr = errors.New("banana") 295 fakeConnection.MakeReturns(expectedErr) 296 }) 297 298 When("the http response is not set", func() { 299 BeforeEach(func() { 300 response = &plugin.Response{} 301 }) 302 303 It("outputs nothing", func() { 304 Expect(makeErr).To(MatchError(expectedErr)) 305 Expect(fakeOutput.DisplayResponseHeaderCallCount()).To(Equal(0)) 306 }) 307 }) 308 309 When("the http response body is nil", func() { 310 BeforeEach(func() { 311 response = &plugin.Response{ 312 HTTPResponse: &http.Response{Body: nil}, 313 } 314 }) 315 316 It("does not output the response body", func() { 317 Expect(makeErr).To(MatchError(expectedErr)) 318 Expect(fakeOutput.DisplayResponseHeaderCallCount()).To(Equal(1)) 319 320 Expect(fakeOutput.DisplayJSONBodyCallCount()).To(Equal(0)) 321 Expect(fakeOutput.DisplayDumpCallCount()).To(Equal(0)) 322 }) 323 }) 324 325 When("the http response is set", func() { 326 BeforeEach(func() { 327 response = &plugin.Response{ 328 RawResponse: []byte("some-error-body"), 329 HTTPResponse: &http.Response{ 330 Proto: "HTTP/1.1", 331 Status: "200 OK", 332 Header: http.Header{ 333 "Content-Type": {"application/json"}, 334 "BBBBB": {"second"}, 335 "AAAAA": {"first"}, 336 "CCCCC": {"third"}, 337 }, 338 Body: ioutil.NopCloser(bytes.NewReader([]byte(`some-error-body`))), 339 }, 340 } 341 }) 342 343 It("outputs the response", func() { 344 Expect(makeErr).To(MatchError(expectedErr)) 345 346 Expect(fakeOutput.DisplayTypeCallCount()).To(Equal(2)) 347 name, date := fakeOutput.DisplayTypeArgsForCall(1) 348 Expect(name).To(Equal("RESPONSE")) 349 Expect(date).To(BeTemporally("~", time.Now(), time.Second)) 350 351 Expect(fakeOutput.DisplayResponseHeaderCallCount()).To(Equal(1)) 352 protocol, status := fakeOutput.DisplayResponseHeaderArgsForCall(0) 353 Expect(protocol).To(Equal("HTTP/1.1")) 354 Expect(status).To(Equal("200 OK")) 355 356 Expect(fakeOutput.DisplayHeaderCallCount()).To(BeNumerically(">=", 7)) 357 name, value := fakeOutput.DisplayHeaderArgsForCall(3) 358 Expect(name).To(Equal("AAAAA")) 359 Expect(value).To(Equal("first")) 360 name, value = fakeOutput.DisplayHeaderArgsForCall(4) 361 Expect(name).To(Equal("BBBBB")) 362 Expect(value).To(Equal("second")) 363 name, value = fakeOutput.DisplayHeaderArgsForCall(5) 364 Expect(name).To(Equal("CCCCC")) 365 Expect(value).To(Equal("third")) 366 name, value = fakeOutput.DisplayHeaderArgsForCall(6) 367 Expect(name).To(Equal("Content-Type")) 368 Expect(value).To(Equal("application/json")) 369 370 Expect(fakeOutput.DisplayJSONBodyCallCount()).To(BeNumerically(">=", 1)) 371 Expect(fakeOutput.DisplayJSONBodyArgsForCall(0)).To(Equal([]byte("some-error-body"))) 372 }) 373 }) 374 }) 375 376 When("an error occurs while trying to log the response", func() { 377 var ( 378 originalErr error 379 expectedErr error 380 ) 381 382 BeforeEach(func() { 383 originalErr = errors.New("this error should not be overwritten") 384 fakeConnection.MakeReturns(originalErr) 385 386 expectedErr = errors.New("this should never block the request") 387 388 calledOnce := false 389 fakeOutput.StartStub = func() error { 390 if !calledOnce { 391 calledOnce = true 392 return nil 393 } 394 return expectedErr 395 } 396 }) 397 398 It("should display the error and continue on", func() { 399 Expect(makeErr).To(MatchError(originalErr)) 400 401 Expect(fakeOutput.HandleInternalErrorCallCount()).To(Equal(1)) 402 Expect(fakeOutput.HandleInternalErrorArgsForCall(0)).To(MatchError(expectedErr)) 403 }) 404 }) 405 406 It("starts and stops the output", func() { 407 Expect(makeErr).ToNot(HaveOccurred()) 408 Expect(fakeOutput.StartCallCount()).To(Equal(2)) 409 Expect(fakeOutput.StopCallCount()).To(Equal(2)) 410 }) 411 412 When("displaying the logs have an error", func() { 413 var expectedErr error 414 415 BeforeEach(func() { 416 expectedErr = errors.New("Display error on request") 417 fakeOutput.StartReturns(expectedErr) 418 }) 419 420 It("calls handle internal error", func() { 421 Expect(makeErr).ToNot(HaveOccurred()) 422 423 Expect(fakeOutput.HandleInternalErrorCallCount()).To(Equal(2)) 424 Expect(fakeOutput.HandleInternalErrorArgsForCall(0)).To(MatchError(expectedErr)) 425 Expect(fakeOutput.HandleInternalErrorArgsForCall(1)).To(MatchError(expectedErr)) 426 }) 427 }) 428 }) 429 })