blob: d4e7137913e4924f0b1f4db5ad4e666961c5e98d [file] [log] [blame]
// Copyright 2012 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package httptest
import (
"fmt"
"io"
"net/http"
"testing"
)
func TestRecorder(t *testing.T) {
type checkFunc func(*ResponseRecorder) error
check := func(fns ...checkFunc) []checkFunc { return fns }
hasStatus := func(wantCode int) checkFunc {
return func(rec *ResponseRecorder) error {
if rec.Code != wantCode {
return fmt.Errorf("Status = %d; want %d", rec.Code, wantCode)
}
return nil
}
}
hasResultStatus := func(wantCode int) checkFunc {
return func(rec *ResponseRecorder) error {
if rec.Result().StatusCode != wantCode {
return fmt.Errorf("Result().StatusCode = %d; want %d", rec.Result().StatusCode, wantCode)
}
return nil
}
}
hasContents := func(want string) checkFunc {
return func(rec *ResponseRecorder) error {
if rec.Body.String() != want {
return fmt.Errorf("wrote = %q; want %q", rec.Body.String(), want)
}
return nil
}
}
hasFlush := func(want bool) checkFunc {
return func(rec *ResponseRecorder) error {
if rec.Flushed != want {
return fmt.Errorf("Flushed = %v; want %v", rec.Flushed, want)
}
return nil
}
}
hasOldHeader := func(key, want string) checkFunc {
return func(rec *ResponseRecorder) error {
if got := rec.HeaderMap.Get(key); got != want {
return fmt.Errorf("HeaderMap header %s = %q; want %q", key, got, want)
}
return nil
}
}
hasHeader := func(key, want string) checkFunc {
return func(rec *ResponseRecorder) error {
if got := rec.Result().Header.Get(key); got != want {
return fmt.Errorf("final header %s = %q; want %q", key, got, want)
}
return nil
}
}
hasNotHeaders := func(keys ...string) checkFunc {
return func(rec *ResponseRecorder) error {
for _, k := range keys {
v, ok := rec.Result().Header[http.CanonicalHeaderKey(k)]
if ok {
return fmt.Errorf("unexpected header %s with value %q", k, v)
}
}
return nil
}
}
hasTrailer := func(key, want string) checkFunc {
return func(rec *ResponseRecorder) error {
if got := rec.Result().Trailer.Get(key); got != want {
return fmt.Errorf("trailer %s = %q; want %q", key, got, want)
}
return nil
}
}
hasNotTrailers := func(keys ...string) checkFunc {
return func(rec *ResponseRecorder) error {
trailers := rec.Result().Trailer
for _, k := range keys {
_, ok := trailers[http.CanonicalHeaderKey(k)]
if ok {
return fmt.Errorf("unexpected trailer %s", k)
}
}
return nil
}
}
tests := []struct {
name string
h func(w http.ResponseWriter, r *http.Request)
checks []checkFunc
}{
{
"200 default",
func(w http.ResponseWriter, r *http.Request) {},
check(hasStatus(200), hasContents("")),
},
{
"first code only",
func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(201)
w.WriteHeader(202)
w.Write([]byte("hi"))
},
check(hasStatus(201), hasContents("hi")),
},
{
"write sends 200",
func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hi first"))
w.WriteHeader(201)
w.WriteHeader(202)
},
check(hasStatus(200), hasContents("hi first"), hasFlush(false)),
},
{
"write string",
func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "hi first")
},
check(
hasStatus(200),
hasContents("hi first"),
hasFlush(false),
hasHeader("Content-Type", "text/plain; charset=utf-8"),
),
},
{
"flush",
func(w http.ResponseWriter, r *http.Request) {
w.(http.Flusher).Flush() // also sends a 200
w.WriteHeader(201)
},
check(hasStatus(200), hasFlush(true)),
},
{
"Content-Type detection",
func(w http.ResponseWriter, r *http.Request) {
io.WriteString(w, "<html>")
},
check(hasHeader("Content-Type", "text/html; charset=utf-8")),
},
{
"no Content-Type detection with Transfer-Encoding",
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Transfer-Encoding", "some encoding")
io.WriteString(w, "<html>")
},
check(hasHeader("Content-Type", "")), // no header
},
{
"no Content-Type detection if set explicitly",
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "some/type")
io.WriteString(w, "<html>")
},
check(hasHeader("Content-Type", "some/type")),
},
{
"Content-Type detection doesn't crash if HeaderMap is nil",
func(w http.ResponseWriter, r *http.Request) {
// Act as if the user wrote new(httptest.ResponseRecorder)
// rather than using NewRecorder (which initializes
// HeaderMap)
w.(*ResponseRecorder).HeaderMap = nil
io.WriteString(w, "<html>")
},
check(hasHeader("Content-Type", "text/html; charset=utf-8")),
},
{
"Header is not changed after write",
func(w http.ResponseWriter, r *http.Request) {
hdr := w.Header()
hdr.Set("Key", "correct")
w.WriteHeader(200)
hdr.Set("Key", "incorrect")
},
check(hasHeader("Key", "correct")),
},
{
"Trailer headers are correctly recorded",
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Non-Trailer", "correct")
w.Header().Set("Trailer", "Trailer-A")
w.Header().Add("Trailer", "Trailer-B")
w.Header().Add("Trailer", "Trailer-C")
io.WriteString(w, "<html>")
w.Header().Set("Non-Trailer", "incorrect")
w.Header().Set("Trailer-A", "valuea")
w.Header().Set("Trailer-C", "valuec")
w.Header().Set("Trailer-NotDeclared", "should be omitted")
},
check(
hasStatus(200),
hasHeader("Content-Type", "text/html; charset=utf-8"),
hasHeader("Non-Trailer", "correct"),
hasNotHeaders("Trailer-A", "Trailer-B", "Trailer-C", "Trailer-NotDeclared"),
hasTrailer("Trailer-A", "valuea"),
hasTrailer("Trailer-C", "valuec"),
hasNotTrailers("Non-Trailer", "Trailer-B", "Trailer-NotDeclared"),
),
},
{
"Header set without any write", // Issue 15560
func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Foo", "1")
// Simulate somebody using
// new(ResponseRecorder) instead of
// using the constructor which sets
// this to 200
w.(*ResponseRecorder).Code = 0
},
check(
hasOldHeader("X-Foo", "1"),
hasStatus(0),
hasHeader("X-Foo", "1"),
hasResultStatus(200),
),
},
{
"HeaderMap vs FinalHeaders", // more for Issue 15560
func(w http.ResponseWriter, r *http.Request) {
h := w.Header()
h.Set("X-Foo", "1")
w.Write([]byte("hi"))
h.Set("X-Foo", "2")
h.Set("X-Bar", "2")
},
check(
hasOldHeader("X-Foo", "2"),
hasOldHeader("X-Bar", "2"),
hasHeader("X-Foo", "1"),
hasNotHeaders("X-Bar"),
),
},
}
r, _ := http.NewRequest("GET", "http://foo.com/", nil)
for _, tt := range tests {
h := http.HandlerFunc(tt.h)
rec := NewRecorder()
h.ServeHTTP(rec, r)
for _, check := range tt.checks {
if err := check(rec); err != nil {
t.Errorf("%s: %v", tt.name, err)
}
}
}
}