github.com/jenspinney/cli@v6.42.1-0.20190207184520-7450c600020e+incompatible/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("passed a body", func() {
   110  			var originalBody io.ReadCloser
   111  
   112  			When("the body is not JSON", func() {
   113  				BeforeEach(func() {
   114  					originalBody = ioutil.NopCloser(bytes.NewReader([]byte("foo")))
   115  					request.Body = originalBody
   116  				})
   117  
   118  				It("outputs the body", func() {
   119  					Expect(makeErr).NotTo(HaveOccurred())
   120  
   121  					Expect(fakeOutput.DisplayBodyCallCount()).To(BeNumerically(">=", 1))
   122  					Expect(fakeOutput.DisplayBodyArgsForCall(0)).To(Equal([]byte("foo")))
   123  
   124  					bytes, err := ioutil.ReadAll(request.Body)
   125  					Expect(err).NotTo(HaveOccurred())
   126  					Expect(bytes).To(Equal([]byte("foo")))
   127  				})
   128  			})
   129  
   130  			When("the body is JSON", func() {
   131  				var jsonBody string
   132  
   133  				BeforeEach(func() {
   134  					jsonBody = `{"some-key": "some-value"}`
   135  					originalBody = ioutil.NopCloser(bytes.NewReader([]byte(jsonBody)))
   136  					request.Body = originalBody
   137  					request.Header.Add("Content-Type", "application/json")
   138  				})
   139  
   140  				It("properly displays the JSON body", func() {
   141  					Expect(makeErr).NotTo(HaveOccurred())
   142  
   143  					Expect(fakeOutput.DisplayJSONBodyCallCount()).To(BeNumerically(">=", 1))
   144  					Expect(fakeOutput.DisplayJSONBodyArgsForCall(0)).To(Equal([]byte(jsonBody)))
   145  
   146  					bytes, err := ioutil.ReadAll(request.Body)
   147  					Expect(err).NotTo(HaveOccurred())
   148  					Expect(bytes).To(Equal([]byte(jsonBody)))
   149  				})
   150  			})
   151  		})
   152  
   153  		When("an error occures while trying to log the request", func() {
   154  			var expectedErr error
   155  
   156  			BeforeEach(func() {
   157  				expectedErr = errors.New("this should never block the request")
   158  
   159  				calledOnce := false
   160  				fakeOutput.StartStub = func() error {
   161  					if !calledOnce {
   162  						calledOnce = true
   163  						return expectedErr
   164  					}
   165  					return nil
   166  				}
   167  			})
   168  
   169  			It("should display the error and continue on", func() {
   170  				Expect(makeErr).NotTo(HaveOccurred())
   171  
   172  				Expect(fakeOutput.HandleInternalErrorCallCount()).To(Equal(1))
   173  				Expect(fakeOutput.HandleInternalErrorArgsForCall(0)).To(MatchError(expectedErr))
   174  			})
   175  		})
   176  
   177  		When("the request is successful", func() {
   178  			BeforeEach(func() {
   179  				response = &uaa.Response{
   180  					RawResponse: []byte("some-response-body"),
   181  					HTTPResponse: &http.Response{
   182  						Proto:  "HTTP/1.1",
   183  						Status: "200 OK",
   184  						Header: http.Header{
   185  							"BBBBB": {"second"},
   186  							"AAAAA": {"first"},
   187  							"CCCCC": {"third"},
   188  						},
   189  					},
   190  				}
   191  			})
   192  
   193  			It("outputs the response", func() {
   194  				Expect(makeErr).NotTo(HaveOccurred())
   195  
   196  				Expect(fakeOutput.DisplayTypeCallCount()).To(Equal(2))
   197  				name, date := fakeOutput.DisplayTypeArgsForCall(1)
   198  				Expect(name).To(Equal("RESPONSE"))
   199  				Expect(date).To(BeTemporally("~", time.Now(), time.Second))
   200  
   201  				Expect(fakeOutput.DisplayResponseHeaderCallCount()).To(Equal(1))
   202  				protocol, status := fakeOutput.DisplayResponseHeaderArgsForCall(0)
   203  				Expect(protocol).To(Equal("HTTP/1.1"))
   204  				Expect(status).To(Equal("200 OK"))
   205  
   206  				Expect(fakeOutput.DisplayHeaderCallCount()).To(BeNumerically(">=", 6))
   207  				name, value := fakeOutput.DisplayHeaderArgsForCall(3)
   208  				Expect(name).To(Equal("AAAAA"))
   209  				Expect(value).To(Equal("first"))
   210  				name, value = fakeOutput.DisplayHeaderArgsForCall(4)
   211  				Expect(name).To(Equal("BBBBB"))
   212  				Expect(value).To(Equal("second"))
   213  				name, value = fakeOutput.DisplayHeaderArgsForCall(5)
   214  				Expect(name).To(Equal("CCCCC"))
   215  				Expect(value).To(Equal("third"))
   216  
   217  				Expect(fakeOutput.DisplayJSONBodyCallCount()).To(BeNumerically(">=", 1))
   218  				Expect(fakeOutput.DisplayJSONBodyArgsForCall(0)).To(Equal([]byte("some-response-body")))
   219  			})
   220  		})
   221  
   222  		When("the request is unsuccessful", func() {
   223  			var expectedErr error
   224  
   225  			BeforeEach(func() {
   226  				expectedErr = errors.New("banana")
   227  				fakeConnection.MakeReturns(expectedErr)
   228  			})
   229  
   230  			When("the http response is not set", func() {
   231  				BeforeEach(func() {
   232  					response = &uaa.Response{}
   233  				})
   234  
   235  				It("outputs nothing", func() {
   236  					Expect(makeErr).To(MatchError(expectedErr))
   237  					Expect(fakeOutput.DisplayResponseHeaderCallCount()).To(Equal(0))
   238  				})
   239  			})
   240  
   241  			When("the http response is set", func() {
   242  				BeforeEach(func() {
   243  					response = &uaa.Response{
   244  						RawResponse: []byte("some-error-body"),
   245  						HTTPResponse: &http.Response{
   246  							Proto:  "HTTP/1.1",
   247  							Status: "200 OK",
   248  							Header: http.Header{
   249  								"BBBBB": {"second"},
   250  								"AAAAA": {"first"},
   251  								"CCCCC": {"third"},
   252  							},
   253  						},
   254  					}
   255  				})
   256  
   257  				It("outputs the response", func() {
   258  					Expect(makeErr).To(MatchError(expectedErr))
   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.DisplayJSONBodyCallCount()).To(BeNumerically(">=", 1))
   282  					Expect(fakeOutput.DisplayJSONBodyArgsForCall(0)).To(Equal([]byte("some-error-body")))
   283  				})
   284  			})
   285  		})
   286  
   287  		When("an error occures while trying to log the response", func() {
   288  			var (
   289  				originalErr error
   290  				expectedErr error
   291  			)
   292  
   293  			BeforeEach(func() {
   294  				originalErr = errors.New("this error should not be overwritten")
   295  				fakeConnection.MakeReturns(originalErr)
   296  
   297  				expectedErr = errors.New("this should never block the request")
   298  
   299  				calledOnce := false
   300  				fakeOutput.StartStub = func() error {
   301  					if !calledOnce {
   302  						calledOnce = true
   303  						return nil
   304  					}
   305  					return expectedErr
   306  				}
   307  			})
   308  
   309  			It("should display the error and continue on", func() {
   310  				Expect(makeErr).To(MatchError(originalErr))
   311  
   312  				Expect(fakeOutput.HandleInternalErrorCallCount()).To(Equal(1))
   313  				Expect(fakeOutput.HandleInternalErrorArgsForCall(0)).To(MatchError(expectedErr))
   314  			})
   315  		})
   316  
   317  		It("starts and stops the output", func() {
   318  			Expect(makeErr).ToNot(HaveOccurred())
   319  			Expect(fakeOutput.StartCallCount()).To(Equal(2))
   320  			Expect(fakeOutput.StopCallCount()).To(Equal(2))
   321  		})
   322  
   323  		When("displaying the logs have an error", func() {
   324  			var expectedErr error
   325  
   326  			BeforeEach(func() {
   327  				expectedErr = errors.New("Display error on request")
   328  				fakeOutput.StartReturns(expectedErr)
   329  			})
   330  
   331  			It("calls handle internal error", func() {
   332  				Expect(makeErr).ToNot(HaveOccurred())
   333  
   334  				Expect(fakeOutput.HandleInternalErrorCallCount()).To(Equal(2))
   335  				Expect(fakeOutput.HandleInternalErrorArgsForCall(0)).To(MatchError(expectedErr))
   336  				Expect(fakeOutput.HandleInternalErrorArgsForCall(1)).To(MatchError(expectedErr))
   337  			})
   338  		})
   339  	})
   340  })