github.com/mh-cbon/go@v0.0.0-20160603070303-9e112a3fe4c0/src/net/http/httptest/recorder_test.go (about)

     1  // Copyright 2012 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package httptest
     6  
     7  import (
     8  	"fmt"
     9  	"io"
    10  	"net/http"
    11  	"testing"
    12  )
    13  
    14  func TestRecorder(t *testing.T) {
    15  	type checkFunc func(*ResponseRecorder) error
    16  	check := func(fns ...checkFunc) []checkFunc { return fns }
    17  
    18  	hasStatus := func(wantCode int) checkFunc {
    19  		return func(rec *ResponseRecorder) error {
    20  			if rec.Code != wantCode {
    21  				return fmt.Errorf("Status = %d; want %d", rec.Code, wantCode)
    22  			}
    23  			return nil
    24  		}
    25  	}
    26  	hasResultStatus := func(wantCode int) checkFunc {
    27  		return func(rec *ResponseRecorder) error {
    28  			if rec.Result().StatusCode != wantCode {
    29  				return fmt.Errorf("Result().StatusCode = %d; want %d", rec.Result().StatusCode, wantCode)
    30  			}
    31  			return nil
    32  		}
    33  	}
    34  	hasContents := func(want string) checkFunc {
    35  		return func(rec *ResponseRecorder) error {
    36  			if rec.Body.String() != want {
    37  				return fmt.Errorf("wrote = %q; want %q", rec.Body.String(), want)
    38  			}
    39  			return nil
    40  		}
    41  	}
    42  	hasFlush := func(want bool) checkFunc {
    43  		return func(rec *ResponseRecorder) error {
    44  			if rec.Flushed != want {
    45  				return fmt.Errorf("Flushed = %v; want %v", rec.Flushed, want)
    46  			}
    47  			return nil
    48  		}
    49  	}
    50  	hasOldHeader := func(key, want string) checkFunc {
    51  		return func(rec *ResponseRecorder) error {
    52  			if got := rec.HeaderMap.Get(key); got != want {
    53  				return fmt.Errorf("HeaderMap header %s = %q; want %q", key, got, want)
    54  			}
    55  			return nil
    56  		}
    57  	}
    58  	hasHeader := func(key, want string) checkFunc {
    59  		return func(rec *ResponseRecorder) error {
    60  			if got := rec.Result().Header.Get(key); got != want {
    61  				return fmt.Errorf("final header %s = %q; want %q", key, got, want)
    62  			}
    63  			return nil
    64  		}
    65  	}
    66  	hasNotHeaders := func(keys ...string) checkFunc {
    67  		return func(rec *ResponseRecorder) error {
    68  			for _, k := range keys {
    69  				v, ok := rec.Result().Header[http.CanonicalHeaderKey(k)]
    70  				if ok {
    71  					return fmt.Errorf("unexpected header %s with value %q", k, v)
    72  				}
    73  			}
    74  			return nil
    75  		}
    76  	}
    77  	hasTrailer := func(key, want string) checkFunc {
    78  		return func(rec *ResponseRecorder) error {
    79  			if got := rec.Result().Trailer.Get(key); got != want {
    80  				return fmt.Errorf("trailer %s = %q; want %q", key, got, want)
    81  			}
    82  			return nil
    83  		}
    84  	}
    85  	hasNotTrailers := func(keys ...string) checkFunc {
    86  		return func(rec *ResponseRecorder) error {
    87  			trailers := rec.Result().Trailer
    88  			for _, k := range keys {
    89  				_, ok := trailers[http.CanonicalHeaderKey(k)]
    90  				if ok {
    91  					return fmt.Errorf("unexpected trailer %s", k)
    92  				}
    93  			}
    94  			return nil
    95  		}
    96  	}
    97  
    98  	tests := []struct {
    99  		name   string
   100  		h      func(w http.ResponseWriter, r *http.Request)
   101  		checks []checkFunc
   102  	}{
   103  		{
   104  			"200 default",
   105  			func(w http.ResponseWriter, r *http.Request) {},
   106  			check(hasStatus(200), hasContents("")),
   107  		},
   108  		{
   109  			"first code only",
   110  			func(w http.ResponseWriter, r *http.Request) {
   111  				w.WriteHeader(201)
   112  				w.WriteHeader(202)
   113  				w.Write([]byte("hi"))
   114  			},
   115  			check(hasStatus(201), hasContents("hi")),
   116  		},
   117  		{
   118  			"write sends 200",
   119  			func(w http.ResponseWriter, r *http.Request) {
   120  				w.Write([]byte("hi first"))
   121  				w.WriteHeader(201)
   122  				w.WriteHeader(202)
   123  			},
   124  			check(hasStatus(200), hasContents("hi first"), hasFlush(false)),
   125  		},
   126  		{
   127  			"write string",
   128  			func(w http.ResponseWriter, r *http.Request) {
   129  				io.WriteString(w, "hi first")
   130  			},
   131  			check(
   132  				hasStatus(200),
   133  				hasContents("hi first"),
   134  				hasFlush(false),
   135  				hasHeader("Content-Type", "text/plain; charset=utf-8"),
   136  			),
   137  		},
   138  		{
   139  			"flush",
   140  			func(w http.ResponseWriter, r *http.Request) {
   141  				w.(http.Flusher).Flush() // also sends a 200
   142  				w.WriteHeader(201)
   143  			},
   144  			check(hasStatus(200), hasFlush(true)),
   145  		},
   146  		{
   147  			"Content-Type detection",
   148  			func(w http.ResponseWriter, r *http.Request) {
   149  				io.WriteString(w, "<html>")
   150  			},
   151  			check(hasHeader("Content-Type", "text/html; charset=utf-8")),
   152  		},
   153  		{
   154  			"no Content-Type detection with Transfer-Encoding",
   155  			func(w http.ResponseWriter, r *http.Request) {
   156  				w.Header().Set("Transfer-Encoding", "some encoding")
   157  				io.WriteString(w, "<html>")
   158  			},
   159  			check(hasHeader("Content-Type", "")), // no header
   160  		},
   161  		{
   162  			"no Content-Type detection if set explicitly",
   163  			func(w http.ResponseWriter, r *http.Request) {
   164  				w.Header().Set("Content-Type", "some/type")
   165  				io.WriteString(w, "<html>")
   166  			},
   167  			check(hasHeader("Content-Type", "some/type")),
   168  		},
   169  		{
   170  			"Content-Type detection doesn't crash if HeaderMap is nil",
   171  			func(w http.ResponseWriter, r *http.Request) {
   172  				// Act as if the user wrote new(httptest.ResponseRecorder)
   173  				// rather than using NewRecorder (which initializes
   174  				// HeaderMap)
   175  				w.(*ResponseRecorder).HeaderMap = nil
   176  				io.WriteString(w, "<html>")
   177  			},
   178  			check(hasHeader("Content-Type", "text/html; charset=utf-8")),
   179  		},
   180  		{
   181  			"Header is not changed after write",
   182  			func(w http.ResponseWriter, r *http.Request) {
   183  				hdr := w.Header()
   184  				hdr.Set("Key", "correct")
   185  				w.WriteHeader(200)
   186  				hdr.Set("Key", "incorrect")
   187  			},
   188  			check(hasHeader("Key", "correct")),
   189  		},
   190  		{
   191  			"Trailer headers are correctly recorded",
   192  			func(w http.ResponseWriter, r *http.Request) {
   193  				w.Header().Set("Non-Trailer", "correct")
   194  				w.Header().Set("Trailer", "Trailer-A")
   195  				w.Header().Add("Trailer", "Trailer-B")
   196  				w.Header().Add("Trailer", "Trailer-C")
   197  				io.WriteString(w, "<html>")
   198  				w.Header().Set("Non-Trailer", "incorrect")
   199  				w.Header().Set("Trailer-A", "valuea")
   200  				w.Header().Set("Trailer-C", "valuec")
   201  				w.Header().Set("Trailer-NotDeclared", "should be omitted")
   202  			},
   203  			check(
   204  				hasStatus(200),
   205  				hasHeader("Content-Type", "text/html; charset=utf-8"),
   206  				hasHeader("Non-Trailer", "correct"),
   207  				hasNotHeaders("Trailer-A", "Trailer-B", "Trailer-C", "Trailer-NotDeclared"),
   208  				hasTrailer("Trailer-A", "valuea"),
   209  				hasTrailer("Trailer-C", "valuec"),
   210  				hasNotTrailers("Non-Trailer", "Trailer-B", "Trailer-NotDeclared"),
   211  			),
   212  		},
   213  		{
   214  			"Header set without any write", // Issue 15560
   215  			func(w http.ResponseWriter, r *http.Request) {
   216  				w.Header().Set("X-Foo", "1")
   217  
   218  				// Simulate somebody using
   219  				// new(ResponseRecorder) instead of
   220  				// using the constructor which sets
   221  				// this to 200
   222  				w.(*ResponseRecorder).Code = 0
   223  			},
   224  			check(
   225  				hasOldHeader("X-Foo", "1"),
   226  				hasStatus(0),
   227  				hasHeader("X-Foo", "1"),
   228  				hasResultStatus(200),
   229  			),
   230  		},
   231  		{
   232  			"HeaderMap vs FinalHeaders", // more for Issue 15560
   233  			func(w http.ResponseWriter, r *http.Request) {
   234  				h := w.Header()
   235  				h.Set("X-Foo", "1")
   236  				w.Write([]byte("hi"))
   237  				h.Set("X-Foo", "2")
   238  				h.Set("X-Bar", "2")
   239  			},
   240  			check(
   241  				hasOldHeader("X-Foo", "2"),
   242  				hasOldHeader("X-Bar", "2"),
   243  				hasHeader("X-Foo", "1"),
   244  				hasNotHeaders("X-Bar"),
   245  			),
   246  		},
   247  	}
   248  	r, _ := http.NewRequest("GET", "http://foo.com/", nil)
   249  	for _, tt := range tests {
   250  		h := http.HandlerFunc(tt.h)
   251  		rec := NewRecorder()
   252  		h.ServeHTTP(rec, r)
   253  		for _, check := range tt.checks {
   254  			if err := check(rec); err != nil {
   255  				t.Errorf("%s: %v", tt.name, err)
   256  			}
   257  		}
   258  	}
   259  }