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  })