blob: bb8aa0272d4aa5aa7c0d87b45d464d64feffa7f3 [file] [log] [blame]
// Copyright 2013 Julien Schmidt. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be found
// in the LICENSE file.
package httprouter
import (
"errors"
"fmt"
"net/http"
"net/http/httptest"
"reflect"
"testing"
)
type mockResponseWriter struct{}
func (m *mockResponseWriter) Header() (h http.Header) {
return http.Header{}
}
func (m *mockResponseWriter) Write(p []byte) (n int, err error) {
return len(p), nil
}
func (m *mockResponseWriter) WriteString(s string) (n int, err error) {
return len(s), nil
}
func (m *mockResponseWriter) WriteHeader(int) {}
func TestRouter(t *testing.T) {
router := New()
routed := false
router.Handle("GET", "/user/:name", func(w http.ResponseWriter, r *http.Request, vars map[string]string) {
routed = true
want := map[string]string{"name": "gopher"}
if !reflect.DeepEqual(vars, want) {
t.Fatalf("wrong wildcard values: want %v, got %v", want, vars)
}
})
w := new(mockResponseWriter)
req, _ := http.NewRequest("GET", "/user/gopher", nil)
router.ServeHTTP(w, req)
if !routed {
t.Fatal("routing failed")
}
}
func TestRouterAPI(t *testing.T) {
var get, head, post, put, patch, delete, handlerFunc bool
router := New()
router.GET("/GET", func(w http.ResponseWriter, r *http.Request, _ map[string]string) {
get = true
})
router.HEAD("/GET", func(w http.ResponseWriter, r *http.Request, _ map[string]string) {
head = true
})
router.POST("/POST", func(w http.ResponseWriter, r *http.Request, _ map[string]string) {
post = true
})
router.PUT("/PUT", func(w http.ResponseWriter, r *http.Request, _ map[string]string) {
put = true
})
router.PATCH("/PATCH", func(w http.ResponseWriter, r *http.Request, _ map[string]string) {
patch = true
})
router.DELETE("/DELETE", func(w http.ResponseWriter, r *http.Request, _ map[string]string) {
delete = true
})
router.HandlerFunc("GET", "/HandlerFunc", func(w http.ResponseWriter, r *http.Request) {
handlerFunc = true
})
w := new(mockResponseWriter)
r, _ := http.NewRequest("GET", "/GET", nil)
router.ServeHTTP(w, r)
if !get {
t.Error("routing GET failed")
}
r, _ = http.NewRequest("HEAD", "/GET", nil)
router.ServeHTTP(w, r)
if !head {
t.Error("routing HEAD failed")
}
r, _ = http.NewRequest("POST", "/POST", nil)
router.ServeHTTP(w, r)
if !post {
t.Error("routing POST failed")
}
r, _ = http.NewRequest("PUT", "/PUT", nil)
router.ServeHTTP(w, r)
if !put {
t.Error("routing PUT failed")
}
r, _ = http.NewRequest("PATCH", "/PATCH", nil)
router.ServeHTTP(w, r)
if !patch {
t.Error("routing PATCH failed")
}
r, _ = http.NewRequest("DELETE", "/DELETE", nil)
router.ServeHTTP(w, r)
if !delete {
t.Error("routing DELETE failed")
}
r, _ = http.NewRequest("GET", "/HandlerFunc", nil)
router.ServeHTTP(w, r)
if !handlerFunc {
t.Error("routing HandlerFunc failed")
}
}
func TestRouterRoot(t *testing.T) {
router := New()
recv := catchPanic(func() {
router.GET("noSlashRoot", nil)
})
if recv == nil {
t.Fatal("registering path not beginning with '/' did not panic")
}
}
func TestRouterNotFound(t *testing.T) {
handlerFunc := func(_ http.ResponseWriter, _ *http.Request, _ map[string]string) {}
router := New()
router.GET("/path", handlerFunc)
router.GET("/dir/", handlerFunc)
testRoutes := []struct {
route string
handler http.HandlerFunc
code int
header string
}{
{"/path/", NotFound, 301, "map[Location:[/path]]"}, // TSR -/
{"/dir", NotFound, 301, "map[Location:[/dir/]]"}, // TSR +/
{"/../path", NotFound, 301, "map[Location:[/path]]"}, // CleanPath
{"/nope", NotFound, 404, ""}, // NotFound
{"/nope", nil, 404, ""}, // NotFound
}
for _, tr := range testRoutes {
router.NotFound = tr.handler
r, _ := http.NewRequest("GET", tr.route, nil)
w := httptest.NewRecorder()
router.ServeHTTP(w, r)
if !(w.Code == tr.code && (w.Code == 404 || fmt.Sprint(w.Header()) == tr.header)) {
t.Errorf("NotFound handling route %s failed: Code=%d, Header=%v", tr.route, w.Code, w.Header())
}
}
}
func TestRouterPanicHandler(t *testing.T) {
router := New()
panicHandled := false
router.PanicHandler = func(rw http.ResponseWriter, r *http.Request, p interface{}) {
panicHandled = true
}
router.Handle("PUT", "/user/:name", func(_ http.ResponseWriter, _ *http.Request, _ map[string]string) {
panic("oops!")
})
w := new(mockResponseWriter)
req, _ := http.NewRequest("PUT", "/user/gopher", nil)
defer func() {
if rcv := recover(); rcv != nil {
t.Fatal("handling panic failed")
}
}()
router.ServeHTTP(w, req)
if !panicHandled {
t.Fatal("simulating failed")
}
}
type mockFileSystem struct {
opened bool
}
func (mfs *mockFileSystem) Open(name string) (http.File, error) {
mfs.opened = true
return nil, errors.New("this is just a mock")
}
func TestRouterServeFiles(t *testing.T) {
router := New()
mfs := &mockFileSystem{}
recv := catchPanic(func() {
router.ServeFiles("/noFilepath", mfs)
})
if recv == nil {
t.Fatal("registering path not ending with '*filepath' did not panic")
}
router.ServeFiles("/*filepath", mfs)
w := new(mockResponseWriter)
r, _ := http.NewRequest("GET", "/favicon.ico", nil)
router.ServeHTTP(w, r)
if !mfs.opened {
t.Error("serving file failed")
}
}