github.com/wanddynosios/cli/v8@v8.7.9-0.20240221182337-1a92e3a7017f/api/router/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/router"
    12  	"code.cloudfoundry.org/cli/api/router/routerfakes"
    13  	. "code.cloudfoundry.org/cli/api/router/wrapper"
    14  	"code.cloudfoundry.org/cli/api/router/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 *routerfakes.FakeConnection
    23  		fakeOutput     *wrapperfakes.FakeRequestLoggerOutput
    24  
    25  		wrapper router.Connection
    26  
    27  		request  *router.Request
    28  		response *router.Response
    29  		makeErr  error
    30  	)
    31  
    32  	BeforeEach(func() {
    33  		fakeConnection = new(routerfakes.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 = &router.Response{
    55  			RawResponse:  []byte("some-response-body"),
    56  			HTTPResponse: &http.Response{},
    57  		}
    58  		request = router.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("a 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  			BeforeEach(func() {
   200  				response = &router.Response{
   201  					RawResponse: []byte("some-response-body"),
   202  					HTTPResponse: &http.Response{
   203  						Proto:  "HTTP/1.1",
   204  						Status: "200 OK",
   205  						Header: http.Header{
   206  							"BBBBB": {"second"},
   207  							"AAAAA": {"first"},
   208  							"CCCCC": {"third"},
   209  						},
   210  					},
   211  				}
   212  			})
   213  
   214  			It("outputs the response", func() {
   215  				Expect(makeErr).NotTo(HaveOccurred())
   216  
   217  				Expect(fakeOutput.DisplayTypeCallCount()).To(Equal(2))
   218  				name, date := fakeOutput.DisplayTypeArgsForCall(1)
   219  				Expect(name).To(Equal("RESPONSE"))
   220  				Expect(date).To(BeTemporally("~", time.Now(), time.Second))
   221  
   222  				Expect(fakeOutput.DisplayResponseHeaderCallCount()).To(Equal(1))
   223  				protocol, status := fakeOutput.DisplayResponseHeaderArgsForCall(0)
   224  				Expect(protocol).To(Equal("HTTP/1.1"))
   225  				Expect(status).To(Equal("200 OK"))
   226  
   227  				Expect(fakeOutput.DisplayHeaderCallCount()).To(BeNumerically(">=", 6))
   228  				name, value := fakeOutput.DisplayHeaderArgsForCall(3)
   229  				Expect(name).To(Equal("AAAAA"))
   230  				Expect(value).To(Equal("first"))
   231  				name, value = fakeOutput.DisplayHeaderArgsForCall(4)
   232  				Expect(name).To(Equal("BBBBB"))
   233  				Expect(value).To(Equal("second"))
   234  				name, value = fakeOutput.DisplayHeaderArgsForCall(5)
   235  				Expect(name).To(Equal("CCCCC"))
   236  				Expect(value).To(Equal("third"))
   237  
   238  				Expect(fakeOutput.DisplayJSONBodyCallCount()).To(BeNumerically(">=", 1))
   239  				Expect(fakeOutput.DisplayJSONBodyArgsForCall(0)).To(Equal([]byte("some-response-body")))
   240  			})
   241  		})
   242  
   243  		When("the request is unsuccessful", func() {
   244  			var expectedErr error
   245  
   246  			BeforeEach(func() {
   247  				expectedErr = errors.New("banana")
   248  				fakeConnection.MakeReturns(expectedErr)
   249  			})
   250  
   251  			When("the http response is not set", func() {
   252  				BeforeEach(func() {
   253  					response = &router.Response{}
   254  				})
   255  
   256  				It("outputs nothing", func() {
   257  					Expect(makeErr).To(MatchError(expectedErr))
   258  					Expect(fakeOutput.DisplayResponseHeaderCallCount()).To(Equal(0))
   259  				})
   260  			})
   261  
   262  			When("the http response is set", func() {
   263  				BeforeEach(func() {
   264  					response = &router.Response{
   265  						RawResponse: []byte("some-error-body"),
   266  						HTTPResponse: &http.Response{
   267  							Proto:  "HTTP/1.1",
   268  							Status: "200 OK",
   269  							Header: http.Header{
   270  								"BBBBB": {"second"},
   271  								"AAAAA": {"first"},
   272  								"CCCCC": {"third"},
   273  							},
   274  						},
   275  					}
   276  				})
   277  
   278  				It("outputs the response", func() {
   279  					Expect(makeErr).To(MatchError(expectedErr))
   280  
   281  					Expect(fakeOutput.DisplayTypeCallCount()).To(Equal(2))
   282  					name, date := fakeOutput.DisplayTypeArgsForCall(1)
   283  					Expect(name).To(Equal("RESPONSE"))
   284  					Expect(date).To(BeTemporally("~", time.Now(), time.Second))
   285  
   286  					Expect(fakeOutput.DisplayResponseHeaderCallCount()).To(Equal(1))
   287  					protocol, status := fakeOutput.DisplayResponseHeaderArgsForCall(0)
   288  					Expect(protocol).To(Equal("HTTP/1.1"))
   289  					Expect(status).To(Equal("200 OK"))
   290  
   291  					Expect(fakeOutput.DisplayHeaderCallCount()).To(BeNumerically(">=", 6))
   292  					name, value := fakeOutput.DisplayHeaderArgsForCall(3)
   293  					Expect(name).To(Equal("AAAAA"))
   294  					Expect(value).To(Equal("first"))
   295  					name, value = fakeOutput.DisplayHeaderArgsForCall(4)
   296  					Expect(name).To(Equal("BBBBB"))
   297  					Expect(value).To(Equal("second"))
   298  					name, value = fakeOutput.DisplayHeaderArgsForCall(5)
   299  					Expect(name).To(Equal("CCCCC"))
   300  					Expect(value).To(Equal("third"))
   301  
   302  					Expect(fakeOutput.DisplayJSONBodyCallCount()).To(BeNumerically(">=", 1))
   303  					Expect(fakeOutput.DisplayJSONBodyArgsForCall(0)).To(Equal([]byte("some-error-body")))
   304  				})
   305  			})
   306  		})
   307  
   308  		When("an error occurs while trying to log the response", func() {
   309  			var (
   310  				originalErr error
   311  				expectedErr error
   312  			)
   313  
   314  			BeforeEach(func() {
   315  				originalErr = errors.New("this error should not be overwritten")
   316  				fakeConnection.MakeReturns(originalErr)
   317  
   318  				expectedErr = errors.New("this should never block the request")
   319  
   320  				calledOnce := false
   321  				fakeOutput.StartStub = func() error {
   322  					if !calledOnce {
   323  						calledOnce = true
   324  						return nil
   325  					}
   326  					return expectedErr
   327  				}
   328  			})
   329  
   330  			It("should display the error and continue on", func() {
   331  				Expect(makeErr).To(MatchError(originalErr))
   332  
   333  				Expect(fakeOutput.HandleInternalErrorCallCount()).To(Equal(1))
   334  				Expect(fakeOutput.HandleInternalErrorArgsForCall(0)).To(MatchError(expectedErr))
   335  			})
   336  		})
   337  
   338  		It("starts and stops the output", func() {
   339  			Expect(fakeOutput.StartCallCount()).To(Equal(2))
   340  			Expect(fakeOutput.StopCallCount()).To(Equal(2))
   341  		})
   342  
   343  		When("displaying the logs have an error", func() {
   344  			var expectedErr error
   345  			BeforeEach(func() {
   346  				expectedErr = errors.New("Display error on request")
   347  				fakeOutput.StartReturns(expectedErr)
   348  			})
   349  
   350  			It("calls handle internal error", func() {
   351  				Expect(makeErr).ToNot(HaveOccurred())
   352  
   353  				Expect(fakeOutput.HandleInternalErrorCallCount()).To(Equal(2))
   354  				Expect(fakeOutput.HandleInternalErrorArgsForCall(0)).To(MatchError(expectedErr))
   355  				Expect(fakeOutput.HandleInternalErrorArgsForCall(1)).To(MatchError(expectedErr))
   356  			})
   357  		})
   358  	})
   359  })