...

Source file src/cloud.google.com/go/httpreplay/internal/proxy/log_test.go

Documentation: cloud.google.com/go/httpreplay/internal/proxy

     1  // Copyright 2018 Google Inc. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package proxy
    16  
    17  import (
    18  	"io"
    19  	"net/http"
    20  	"net/url"
    21  	"strings"
    22  	"testing"
    23  
    24  	"cloud.google.com/go/internal/testutil"
    25  	"github.com/google/go-cmp/cmp/cmpopts"
    26  	"github.com/google/martian/v3"
    27  )
    28  
    29  func TestLogger(t *testing.T) {
    30  	req := &http.Request{
    31  		Method: "POST",
    32  		URL: &url.URL{
    33  			Scheme: "https",
    34  			Host:   "example.com",
    35  			Path:   "a/b/c",
    36  		},
    37  		Header:  http.Header{"H1": {"v1", "v2"}, "Content-Type": {"text/plain"}},
    38  		Body:    io.NopCloser(strings.NewReader("hello")),
    39  		Trailer: http.Header{"T1": {"v3", "v4"}},
    40  	}
    41  	res := &http.Response{
    42  		Request:    req,
    43  		StatusCode: 204,
    44  		Body:       io.NopCloser(strings.NewReader("goodbye")),
    45  		Header:     http.Header{"H2": {"v5"}},
    46  		Trailer:    http.Header{"T2": {"v6", "v7"}},
    47  	}
    48  	l := newLogger()
    49  	_, remove, err := martian.TestContext(req, nil, nil)
    50  	if err != nil {
    51  		t.Fatal(err)
    52  	}
    53  	defer remove()
    54  	if err := l.ModifyRequest(req); err != nil {
    55  		t.Fatal(err)
    56  	}
    57  	if err := l.ModifyResponse(res); err != nil {
    58  		t.Fatal(err)
    59  	}
    60  	lg := l.Extract()
    61  	want := []*Entry{
    62  		{
    63  			ID: lg.Entries[0].ID,
    64  			Request: &Request{
    65  				Method:    "POST",
    66  				URL:       "https://example.com/a/b/c",
    67  				Header:    http.Header{"H1": {"v1", "v2"}},
    68  				MediaType: "text/plain",
    69  				BodyParts: [][]byte{[]byte("hello")},
    70  				Trailer:   http.Header{"T1": {"v3", "v4"}},
    71  			},
    72  			Response: &Response{
    73  				StatusCode: 204,
    74  				Body:       []byte("goodbye"),
    75  				Header:     http.Header{"H2": {"v5"}},
    76  				Trailer:    http.Header{"T2": {"v6", "v7"}},
    77  			},
    78  		},
    79  	}
    80  	if diff := testutil.Diff(lg.Entries, want); diff != "" {
    81  		t.Error(diff)
    82  	}
    83  }
    84  
    85  func TestToHTTPResponse(t *testing.T) {
    86  	for _, test := range []struct {
    87  		desc string
    88  		lr   *Response
    89  		req  *http.Request
    90  		want *http.Response
    91  	}{
    92  		{
    93  			desc: "GET request",
    94  			lr: &Response{
    95  				StatusCode: 201,
    96  				Proto:      "1.1",
    97  				Header:     http.Header{"h": {"v"}},
    98  				Body:       []byte("text"),
    99  			},
   100  			req: &http.Request{Method: "GET"},
   101  			want: &http.Response{
   102  				Request:       &http.Request{Method: "GET"},
   103  				StatusCode:    201,
   104  				Proto:         "1.1",
   105  				Header:        http.Header{"h": {"v"}},
   106  				ContentLength: 4,
   107  			},
   108  		},
   109  		{
   110  			desc: "HEAD request with no Content-Length header",
   111  			lr: &Response{
   112  				StatusCode: 201,
   113  				Proto:      "1.1",
   114  				Header:     http.Header{"h": {"v"}},
   115  				Body:       []byte("text"),
   116  			},
   117  			req: &http.Request{Method: "HEAD"},
   118  			want: &http.Response{
   119  				Request:       &http.Request{Method: "HEAD"},
   120  				StatusCode:    201,
   121  				Proto:         "1.1",
   122  				Header:        http.Header{"h": {"v"}},
   123  				ContentLength: -1,
   124  			},
   125  		},
   126  		{
   127  			desc: "HEAD request with Content-Length header",
   128  			lr: &Response{
   129  				StatusCode: 201,
   130  				Proto:      "1.1",
   131  				Header:     http.Header{"h": {"v"}, "Content-Length": {"17"}},
   132  				Body:       []byte("text"),
   133  			},
   134  			req: &http.Request{Method: "HEAD"},
   135  			want: &http.Response{
   136  				Request:       &http.Request{Method: "HEAD"},
   137  				StatusCode:    201,
   138  				Proto:         "1.1",
   139  				Header:        http.Header{"h": {"v"}, "Content-Length": {"17"}},
   140  				ContentLength: 17,
   141  			},
   142  		},
   143  	} {
   144  		got := toHTTPResponse(test.lr, test.req)
   145  		got.Body = nil
   146  		if diff := testutil.Diff(got, test.want, cmpopts.IgnoreUnexported(http.Request{})); diff != "" {
   147  			t.Errorf("%s: %s", test.desc, diff)
   148  		}
   149  	}
   150  }
   151  
   152  func TestEmptyBody(t *testing.T) {
   153  	// Verify that a zero-length body is nil after logging.
   154  	// That will ensure that net/http sends a "Content-Length: 0" header.
   155  	req := &http.Request{
   156  		Method: "POST",
   157  		URL: &url.URL{
   158  			Scheme: "https",
   159  			Host:   "example.com",
   160  			Path:   "a/b/c",
   161  		},
   162  		Body: io.NopCloser(strings.NewReader("")),
   163  	}
   164  	l := newLogger()
   165  	_, remove, err := martian.TestContext(req, nil, nil)
   166  	if err != nil {
   167  		t.Fatal(err)
   168  	}
   169  	defer remove()
   170  	if err := l.ModifyRequest(req); err != nil {
   171  		t.Fatal(err)
   172  	}
   173  	if req.Body != nil {
   174  		t.Error("got non-nil req.Body, want nil")
   175  	}
   176  }
   177  

View as plain text