github.com/wanddynosios/cli/v8@v8.7.9-0.20240221182337-1a92e3a7017f/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 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 When("an Set-Cookie header is in the request", func() { 113 BeforeEach(func() { 114 request.Header = http.Header{"Set-Cookie": []string{"should not be shown"}} 115 }) 116 117 It("redacts the contents of the Set-Cookie header", func() { 118 Expect(makeErr).NotTo(HaveOccurred()) 119 Expect(fakeOutput.DisplayHeaderCallCount()).To(Equal(1)) 120 key, value := fakeOutput.DisplayHeaderArgsForCall(0) 121 Expect(key).To(Equal("Set-Cookie")) 122 Expect(value).To(Equal("[PRIVATE DATA HIDDEN]")) 123 }) 124 }) 125 126 When("passed a body", func() { 127 When("the request's Content-Type is application/json", func() { 128 BeforeEach(func() { 129 request.Header.Set("Content-Type", "application/json") 130 }) 131 132 It("outputs the body", func() { 133 Expect(makeErr).NotTo(HaveOccurred()) 134 135 Expect(fakeOutput.DisplayJSONBodyCallCount()).To(BeNumerically(">=", 1)) 136 Expect(fakeOutput.DisplayJSONBodyArgsForCall(0)).To(Equal([]byte("foo"))) 137 138 bytes, err := ioutil.ReadAll(request.Body) 139 Expect(err).NotTo(HaveOccurred()) 140 Expect(bytes).To(Equal([]byte("foo"))) 141 }) 142 }) 143 144 When("the request's Content-Type is application/x-www-form-urlencoded", func() { 145 BeforeEach(func() { 146 request.Header.Set("Content-Type", "application/x-www-form-urlencoded") 147 }) 148 149 It("outputs the body", func() { 150 Expect(makeErr).NotTo(HaveOccurred()) 151 152 bytes, err := ioutil.ReadAll(request.Body) 153 Expect(err).NotTo(HaveOccurred()) 154 Expect(bytes).To(Equal([]byte("foo"))) 155 Expect(fakeOutput.DisplayMessageCallCount()).To(Equal(1)) 156 Expect(fakeOutput.DisplayMessageArgsForCall(0)).To(Equal("[application/x-www-form-urlencoded foo]")) 157 }) 158 }) 159 160 When("request's Content-Type is anything else", func() { 161 BeforeEach(func() { 162 request.Header.Set("Content-Type", "banana;rama") 163 }) 164 165 It("does not display the body", func() { 166 Expect(makeErr).NotTo(HaveOccurred()) 167 Expect(fakeOutput.DisplayJSONBodyCallCount()).To(Equal(1)) // Once for response body only 168 Expect(fakeOutput.DisplayMessageCallCount()).To(Equal(1)) 169 Expect(fakeOutput.DisplayMessageArgsForCall(0)).To(Equal("[banana Content Hidden]")) 170 }) 171 }) 172 }) 173 174 When("an error occurs while trying to log the request", func() { 175 var expectedErr error 176 177 BeforeEach(func() { 178 expectedErr = errors.New("this should never block the request") 179 180 calledOnce := false 181 fakeOutput.StartStub = func() error { 182 if !calledOnce { 183 calledOnce = true 184 return expectedErr 185 } 186 return nil 187 } 188 }) 189 190 It("should display the error and continue on", func() { 191 Expect(makeErr).NotTo(HaveOccurred()) 192 193 Expect(fakeOutput.HandleInternalErrorCallCount()).To(Equal(1)) 194 Expect(fakeOutput.HandleInternalErrorArgsForCall(0)).To(MatchError(expectedErr)) 195 }) 196 }) 197 198 When("the request is successful", func() { 199 When("the response body is not YAML", func() { 200 BeforeEach(func() { 201 response = &cloudcontroller.Response{ 202 RawResponse: []byte("some-response-body"), 203 HTTPResponse: &http.Response{ 204 Proto: "HTTP/1.1", 205 Status: "200 OK", 206 Header: http.Header{ 207 "BBBBB": {"second"}, 208 "AAAAA": {"first"}, 209 "CCCCC": {"third"}, 210 }, 211 }, 212 } 213 }) 214 215 It("outputs the response", func() { 216 Expect(makeErr).NotTo(HaveOccurred()) 217 218 Expect(fakeOutput.DisplayTypeCallCount()).To(Equal(2)) 219 name, date := fakeOutput.DisplayTypeArgsForCall(1) 220 Expect(name).To(Equal("RESPONSE")) 221 Expect(date).To(BeTemporally("~", time.Now(), time.Second)) 222 223 Expect(fakeOutput.DisplayResponseHeaderCallCount()).To(Equal(1)) 224 protocol, status := fakeOutput.DisplayResponseHeaderArgsForCall(0) 225 Expect(protocol).To(Equal("HTTP/1.1")) 226 Expect(status).To(Equal("200 OK")) 227 228 Expect(fakeOutput.DisplayHeaderCallCount()).To(BeNumerically(">=", 6)) 229 name, value := fakeOutput.DisplayHeaderArgsForCall(3) 230 Expect(name).To(Equal("AAAAA")) 231 Expect(value).To(Equal("first")) 232 name, value = fakeOutput.DisplayHeaderArgsForCall(4) 233 Expect(name).To(Equal("BBBBB")) 234 Expect(value).To(Equal("second")) 235 name, value = fakeOutput.DisplayHeaderArgsForCall(5) 236 Expect(name).To(Equal("CCCCC")) 237 Expect(value).To(Equal("third")) 238 239 Expect(fakeOutput.DisplayMessageCallCount()).To(BeNumerically("==", 0)) 240 241 Expect(fakeOutput.DisplayJSONBodyCallCount()).To(BeNumerically(">=", 1)) 242 Expect(fakeOutput.DisplayJSONBodyArgsForCall(0)).To(Equal([]byte("some-response-body"))) 243 }) 244 }) 245 246 When("the response body is YAML", func() { 247 BeforeEach(func() { 248 response = &cloudcontroller.Response{ 249 RawResponse: []byte(`---\n- some-response-body`), 250 HTTPResponse: &http.Response{ 251 Proto: "HTTP/1.1", 252 Status: "200 OK", 253 Header: http.Header{ 254 "Content-Type": {"application/x-yaml; charset=utf-16"}, 255 }, 256 }, 257 } 258 }) 259 260 It("redacts the body to prevent leaking manifest credentials", func() { 261 Expect(makeErr).NotTo(HaveOccurred()) 262 263 Expect(fakeOutput.DisplayTypeCallCount()).To(Equal(2)) 264 name, date := fakeOutput.DisplayTypeArgsForCall(1) 265 Expect(name).To(Equal("RESPONSE")) 266 Expect(date).To(BeTemporally("~", time.Now(), time.Second)) 267 268 Expect(fakeOutput.DisplayResponseHeaderCallCount()).To(Equal(1)) 269 protocol, status := fakeOutput.DisplayResponseHeaderArgsForCall(0) 270 Expect(protocol).To(Equal("HTTP/1.1")) 271 Expect(status).To(Equal("200 OK")) 272 273 Expect(fakeOutput.DisplayHeaderCallCount()).To(BeNumerically(">=", 4)) 274 name, value := fakeOutput.DisplayHeaderArgsForCall(3) 275 Expect(name).To(Equal("Content-Type")) 276 Expect(value).To(Equal("application/x-yaml; charset=utf-16")) 277 278 Expect(fakeOutput.DisplayMessageCallCount()).To(BeNumerically(">=", 1)) 279 Expect(fakeOutput.DisplayMessageArgsForCall(0)).To(Equal("[application/x-yaml Content Hidden]")) 280 281 Expect(fakeOutput.DisplayJSONBodyCallCount()).To(BeNumerically("==", 0)) 282 }) 283 }) 284 }) 285 286 When("the request is unsuccessful", func() { 287 var expectedErr error 288 289 BeforeEach(func() { 290 expectedErr = errors.New("banana") 291 fakeConnection.MakeReturns(expectedErr) 292 }) 293 294 When("the http response is not set", func() { 295 BeforeEach(func() { 296 response = &cloudcontroller.Response{} 297 }) 298 299 It("outputs nothing", func() { 300 Expect(makeErr).To(MatchError(expectedErr)) 301 Expect(fakeOutput.DisplayResponseHeaderCallCount()).To(Equal(0)) 302 }) 303 }) 304 305 When("the http response is set", func() { 306 BeforeEach(func() { 307 response = &cloudcontroller.Response{ 308 RawResponse: []byte("some-error-body"), 309 HTTPResponse: &http.Response{ 310 Proto: "HTTP/1.1", 311 Status: "200 OK", 312 Header: http.Header{ 313 "BBBBB": {"second"}, 314 "AAAAA": {"first"}, 315 "CCCCC": {"third"}, 316 }, 317 }, 318 } 319 }) 320 321 It("outputs the response", func() { 322 Expect(makeErr).To(MatchError(expectedErr)) 323 324 Expect(fakeOutput.DisplayTypeCallCount()).To(Equal(2)) 325 name, date := fakeOutput.DisplayTypeArgsForCall(1) 326 Expect(name).To(Equal("RESPONSE")) 327 Expect(date).To(BeTemporally("~", time.Now(), time.Second)) 328 329 Expect(fakeOutput.DisplayResponseHeaderCallCount()).To(Equal(1)) 330 protocol, status := fakeOutput.DisplayResponseHeaderArgsForCall(0) 331 Expect(protocol).To(Equal("HTTP/1.1")) 332 Expect(status).To(Equal("200 OK")) 333 334 Expect(fakeOutput.DisplayHeaderCallCount()).To(BeNumerically(">=", 6)) 335 name, value := fakeOutput.DisplayHeaderArgsForCall(3) 336 Expect(name).To(Equal("AAAAA")) 337 Expect(value).To(Equal("first")) 338 name, value = fakeOutput.DisplayHeaderArgsForCall(4) 339 Expect(name).To(Equal("BBBBB")) 340 Expect(value).To(Equal("second")) 341 name, value = fakeOutput.DisplayHeaderArgsForCall(5) 342 Expect(name).To(Equal("CCCCC")) 343 Expect(value).To(Equal("third")) 344 345 Expect(fakeOutput.DisplayJSONBodyCallCount()).To(BeNumerically(">=", 1)) 346 Expect(fakeOutput.DisplayJSONBodyArgsForCall(0)).To(Equal([]byte("some-error-body"))) 347 }) 348 }) 349 }) 350 351 When("an error occurs while trying to log the response", func() { 352 var ( 353 originalErr error 354 expectedErr error 355 ) 356 357 BeforeEach(func() { 358 originalErr = errors.New("this error should not be overwritten") 359 fakeConnection.MakeReturns(originalErr) 360 361 expectedErr = errors.New("this should never block the request") 362 363 calledOnce := false 364 fakeOutput.StartStub = func() error { 365 if !calledOnce { 366 calledOnce = true 367 return nil 368 } 369 return expectedErr 370 } 371 }) 372 373 It("should display the error and continue on", func() { 374 Expect(makeErr).To(MatchError(originalErr)) 375 376 Expect(fakeOutput.HandleInternalErrorCallCount()).To(Equal(1)) 377 Expect(fakeOutput.HandleInternalErrorArgsForCall(0)).To(MatchError(expectedErr)) 378 }) 379 }) 380 381 It("starts and stops the output", func() { 382 Expect(fakeOutput.StartCallCount()).To(Equal(2)) 383 Expect(fakeOutput.StopCallCount()).To(Equal(2)) 384 }) 385 386 When("displaying the logs have an error", func() { 387 var expectedErr error 388 BeforeEach(func() { 389 expectedErr = errors.New("Display error on request") 390 fakeOutput.StartReturns(expectedErr) 391 }) 392 393 It("calls handle internal error", func() { 394 Expect(makeErr).ToNot(HaveOccurred()) 395 396 Expect(fakeOutput.HandleInternalErrorCallCount()).To(Equal(2)) 397 Expect(fakeOutput.HandleInternalErrorArgsForCall(0)).To(MatchError(expectedErr)) 398 Expect(fakeOutput.HandleInternalErrorArgsForCall(1)).To(MatchError(expectedErr)) 399 }) 400 }) 401 }) 402 })