github.com/unionj-cloud/go-doudou@v1.3.8-0.20221011095552-0088008e5b31/framework/http/httprouter/router_test.go (about)

     1  // Copyright 2013 Julien Schmidt. All rights reserved.
     2  // Use of this source code is governed by a BSD-style license that can be found
     3  // in the LICENSE file.
     4  
     5  package httprouter
     6  
     7  import (
     8  	"net/http"
     9  	"net/http/httptest"
    10  	"testing"
    11  )
    12  
    13  type mockResponseWriter struct{}
    14  
    15  func (m *mockResponseWriter) Header() (h http.Header) {
    16  	return http.Header{}
    17  }
    18  
    19  func (m *mockResponseWriter) Write(p []byte) (n int, err error) {
    20  	return len(p), nil
    21  }
    22  
    23  func (m *mockResponseWriter) WriteString(s string) (n int, err error) {
    24  	return len(s), nil
    25  }
    26  
    27  func (m *mockResponseWriter) WriteHeader(int) {}
    28  
    29  func TestParams(t *testing.T) {
    30  	ps := Params{
    31  		Param{"param1", "value1"},
    32  		Param{"param2", "value2"},
    33  		Param{"param3", "value3"},
    34  	}
    35  	for i := range ps {
    36  		if val := ps.ByName(ps[i].Key); val != ps[i].Value {
    37  			t.Errorf("Wrong value for %s: Got %s; Want %s", ps[i].Key, val, ps[i].Value)
    38  		}
    39  	}
    40  	if val := ps.ByName("noKey"); val != "" {
    41  		t.Errorf("Expected empty string for not found key; got: %s", val)
    42  	}
    43  }
    44  
    45  type handlerStruct struct {
    46  	handled *bool
    47  }
    48  
    49  func (h handlerStruct) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    50  	*h.handled = true
    51  }
    52  
    53  func TestRouterAPI(t *testing.T) {
    54  	var get, head, options, post, put, patch, delete, handler, handlerFunc bool
    55  
    56  	httpHandler := handlerStruct{&handler}
    57  
    58  	router := New()
    59  	router.GET("/GET", func(w http.ResponseWriter, r *http.Request, _ Params) {
    60  		get = true
    61  	})
    62  	router.HEAD("/GET", func(w http.ResponseWriter, r *http.Request, _ Params) {
    63  		head = true
    64  	})
    65  	router.OPTIONS("/GET", func(w http.ResponseWriter, r *http.Request, _ Params) {
    66  		options = true
    67  	})
    68  	router.POST("/POST", func(w http.ResponseWriter, r *http.Request, _ Params) {
    69  		post = true
    70  	})
    71  	router.PUT("/PUT", func(w http.ResponseWriter, r *http.Request, _ Params) {
    72  		put = true
    73  	})
    74  	router.PATCH("/PATCH", func(w http.ResponseWriter, r *http.Request, _ Params) {
    75  		patch = true
    76  	})
    77  	router.DELETE("/DELETE", func(w http.ResponseWriter, r *http.Request, _ Params) {
    78  		delete = true
    79  	})
    80  	router.Handler(http.MethodGet, "/Handler", httpHandler)
    81  	router.HandlerFunc(http.MethodGet, "/HandlerFunc", func(w http.ResponseWriter, r *http.Request) {
    82  		handlerFunc = true
    83  	})
    84  
    85  	w := new(mockResponseWriter)
    86  
    87  	r, _ := http.NewRequest(http.MethodGet, "/GET", nil)
    88  	router.ServeHTTP(w, r)
    89  	if !get {
    90  		t.Error("routing GET failed")
    91  	}
    92  
    93  	r, _ = http.NewRequest(http.MethodHead, "/GET", nil)
    94  	router.ServeHTTP(w, r)
    95  	if !head {
    96  		t.Error("routing HEAD failed")
    97  	}
    98  
    99  	r, _ = http.NewRequest(http.MethodOptions, "/GET", nil)
   100  	router.ServeHTTP(w, r)
   101  	if !options {
   102  		t.Error("routing OPTIONS failed")
   103  	}
   104  
   105  	r, _ = http.NewRequest(http.MethodPost, "/POST", nil)
   106  	router.ServeHTTP(w, r)
   107  	if !post {
   108  		t.Error("routing POST failed")
   109  	}
   110  
   111  	r, _ = http.NewRequest(http.MethodPut, "/PUT", nil)
   112  	router.ServeHTTP(w, r)
   113  	if !put {
   114  		t.Error("routing PUT failed")
   115  	}
   116  
   117  	r, _ = http.NewRequest(http.MethodPatch, "/PATCH", nil)
   118  	router.ServeHTTP(w, r)
   119  	if !patch {
   120  		t.Error("routing PATCH failed")
   121  	}
   122  
   123  	r, _ = http.NewRequest(http.MethodDelete, "/DELETE", nil)
   124  	router.ServeHTTP(w, r)
   125  	if !delete {
   126  		t.Error("routing DELETE failed")
   127  	}
   128  
   129  	r, _ = http.NewRequest(http.MethodGet, "/Handler", nil)
   130  	router.ServeHTTP(w, r)
   131  	if !handler {
   132  		t.Error("routing Handler failed")
   133  	}
   134  
   135  	r, _ = http.NewRequest(http.MethodGet, "/HandlerFunc", nil)
   136  	router.ServeHTTP(w, r)
   137  	if !handlerFunc {
   138  		t.Error("routing HandlerFunc failed")
   139  	}
   140  }
   141  
   142  func TestRouterChaining(t *testing.T) {
   143  	router1 := New()
   144  	router2 := New()
   145  	router1.NotFound = router2
   146  
   147  	fooHit := false
   148  	router1.POST("/foo", func(w http.ResponseWriter, req *http.Request, _ Params) {
   149  		fooHit = true
   150  		w.WriteHeader(http.StatusOK)
   151  	})
   152  
   153  	barHit := false
   154  	router2.POST("/bar", func(w http.ResponseWriter, req *http.Request, _ Params) {
   155  		barHit = true
   156  		w.WriteHeader(http.StatusOK)
   157  	})
   158  
   159  	r, _ := http.NewRequest(http.MethodPost, "/foo", nil)
   160  	w := httptest.NewRecorder()
   161  	router1.ServeHTTP(w, r)
   162  	if !(w.Code == http.StatusOK && fooHit) {
   163  		t.Errorf("Regular routing failed with router chaining.")
   164  		t.FailNow()
   165  	}
   166  
   167  	r, _ = http.NewRequest(http.MethodPost, "/bar", nil)
   168  	w = httptest.NewRecorder()
   169  	router1.ServeHTTP(w, r)
   170  	if !(w.Code == http.StatusOK && barHit) {
   171  		t.Errorf("Chained routing failed with router chaining.")
   172  		t.FailNow()
   173  	}
   174  
   175  	r, _ = http.NewRequest(http.MethodPost, "/qax", nil)
   176  	w = httptest.NewRecorder()
   177  	router1.ServeHTTP(w, r)
   178  	if !(w.Code == http.StatusNotFound) {
   179  		t.Errorf("NotFound behavior failed with router chaining.")
   180  		t.FailNow()
   181  	}
   182  }
   183  
   184  func BenchmarkAllowed(b *testing.B) {
   185  	handlerFunc := func(_ http.ResponseWriter, _ *http.Request, _ Params) {}
   186  
   187  	router := New()
   188  	router.POST("/path", handlerFunc)
   189  	router.GET("/path", handlerFunc)
   190  
   191  	b.Run("Global", func(b *testing.B) {
   192  		b.ReportAllocs()
   193  		for i := 0; i < b.N; i++ {
   194  			_ = router.allowed("*", http.MethodOptions)
   195  		}
   196  	})
   197  	b.Run("Path", func(b *testing.B) {
   198  		b.ReportAllocs()
   199  		for i := 0; i < b.N; i++ {
   200  			_ = router.allowed("/path", http.MethodOptions)
   201  		}
   202  	})
   203  }
   204  
   205  func TestRouterOPTIONS(t *testing.T) {
   206  	handlerFunc := func(_ http.ResponseWriter, _ *http.Request, _ Params) {}
   207  
   208  	router := New()
   209  	router.POST("/path", handlerFunc)
   210  
   211  	// test not allowed
   212  	// * (server)
   213  	r, _ := http.NewRequest(http.MethodOptions, "*", nil)
   214  	w := httptest.NewRecorder()
   215  	router.ServeHTTP(w, r)
   216  	if !(w.Code == http.StatusOK) {
   217  		t.Errorf("OPTIONS handling failed: Code=%d, Header=%v", w.Code, w.Header())
   218  	} else if allow := w.Header().Get("Allow"); allow != "OPTIONS, POST" {
   219  		t.Error("unexpected Allow header value: " + allow)
   220  	}
   221  
   222  	// path
   223  	r, _ = http.NewRequest(http.MethodOptions, "/path", nil)
   224  	w = httptest.NewRecorder()
   225  	router.ServeHTTP(w, r)
   226  	if !(w.Code == http.StatusOK) {
   227  		t.Errorf("OPTIONS handling failed: Code=%d, Header=%v", w.Code, w.Header())
   228  	} else if allow := w.Header().Get("Allow"); allow != "OPTIONS, POST" {
   229  		t.Error("unexpected Allow header value: " + allow)
   230  	}
   231  
   232  	r, _ = http.NewRequest(http.MethodOptions, "/doesnotexist", nil)
   233  	w = httptest.NewRecorder()
   234  	router.ServeHTTP(w, r)
   235  	if !(w.Code == http.StatusNotFound) {
   236  		t.Errorf("OPTIONS handling failed: Code=%d, Header=%v", w.Code, w.Header())
   237  	}
   238  
   239  	// add another method
   240  	router.GET("/path", handlerFunc)
   241  
   242  	// set a global OPTIONS handler
   243  	router.GlobalOPTIONS = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   244  		// Adjust status code to 204
   245  		w.WriteHeader(http.StatusNoContent)
   246  	})
   247  
   248  	// test again
   249  	// * (server)
   250  	r, _ = http.NewRequest(http.MethodOptions, "*", nil)
   251  	w = httptest.NewRecorder()
   252  	router.ServeHTTP(w, r)
   253  	if !(w.Code == http.StatusNoContent) {
   254  		t.Errorf("OPTIONS handling failed: Code=%d, Header=%v", w.Code, w.Header())
   255  	} else if allow := w.Header().Get("Allow"); allow != "GET, OPTIONS, POST" {
   256  		t.Error("unexpected Allow header value: " + allow)
   257  	}
   258  
   259  	// path
   260  	r, _ = http.NewRequest(http.MethodOptions, "/path", nil)
   261  	w = httptest.NewRecorder()
   262  	router.ServeHTTP(w, r)
   263  	if !(w.Code == http.StatusNoContent) {
   264  		t.Errorf("OPTIONS handling failed: Code=%d, Header=%v", w.Code, w.Header())
   265  	} else if allow := w.Header().Get("Allow"); allow != "GET, OPTIONS, POST" {
   266  		t.Error("unexpected Allow header value: " + allow)
   267  	}
   268  
   269  	// custom handler
   270  	var custom bool
   271  	router.OPTIONS("/path", func(w http.ResponseWriter, r *http.Request, _ Params) {
   272  		custom = true
   273  	})
   274  
   275  	// test again
   276  	// * (server)
   277  	r, _ = http.NewRequest(http.MethodOptions, "*", nil)
   278  	w = httptest.NewRecorder()
   279  	router.ServeHTTP(w, r)
   280  	if !(w.Code == http.StatusNoContent) {
   281  		t.Errorf("OPTIONS handling failed: Code=%d, Header=%v", w.Code, w.Header())
   282  	} else if allow := w.Header().Get("Allow"); allow != "GET, OPTIONS, POST" {
   283  		t.Error("unexpected Allow header value: " + allow)
   284  	}
   285  	if custom {
   286  		t.Error("custom handler called on *")
   287  	}
   288  
   289  	// path
   290  	r, _ = http.NewRequest(http.MethodOptions, "/path", nil)
   291  	w = httptest.NewRecorder()
   292  	router.ServeHTTP(w, r)
   293  	if !(w.Code == http.StatusOK) {
   294  		t.Errorf("OPTIONS handling failed: Code=%d, Header=%v", w.Code, w.Header())
   295  	}
   296  	if !custom {
   297  		t.Error("custom handler not called")
   298  	}
   299  }
   300  
   301  func TestRouterNotAllowed(t *testing.T) {
   302  	handlerFunc := func(_ http.ResponseWriter, _ *http.Request, _ Params) {}
   303  
   304  	router := New()
   305  	router.POST("/path", handlerFunc)
   306  
   307  	// test not allowed
   308  	r, _ := http.NewRequest(http.MethodGet, "/path", nil)
   309  	w := httptest.NewRecorder()
   310  	router.ServeHTTP(w, r)
   311  	if !(w.Code == http.StatusMethodNotAllowed) {
   312  		t.Errorf("NotAllowed handling failed: Code=%d, Header=%v", w.Code, w.Header())
   313  	} else if allow := w.Header().Get("Allow"); allow != "OPTIONS, POST" {
   314  		t.Error("unexpected Allow header value: " + allow)
   315  	}
   316  
   317  	// add another method
   318  	router.DELETE("/path", handlerFunc)
   319  	router.OPTIONS("/path", handlerFunc) // must be ignored
   320  
   321  	// test again
   322  	r, _ = http.NewRequest(http.MethodGet, "/path", nil)
   323  	w = httptest.NewRecorder()
   324  	router.ServeHTTP(w, r)
   325  	if !(w.Code == http.StatusMethodNotAllowed) {
   326  		t.Errorf("NotAllowed handling failed: Code=%d, Header=%v", w.Code, w.Header())
   327  	} else if allow := w.Header().Get("Allow"); allow != "DELETE, OPTIONS, POST" {
   328  		t.Error("unexpected Allow header value: " + allow)
   329  	}
   330  
   331  	// test custom handler
   332  	w = httptest.NewRecorder()
   333  	responseText := "custom method"
   334  	router.MethodNotAllowed = http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   335  		w.WriteHeader(http.StatusTeapot)
   336  		w.Write([]byte(responseText))
   337  	})
   338  	router.ServeHTTP(w, r)
   339  	if got := w.Body.String(); !(got == responseText) {
   340  		t.Errorf("unexpected response got %q want %q", got, responseText)
   341  	}
   342  	if w.Code != http.StatusTeapot {
   343  		t.Errorf("unexpected response code %d want %d", w.Code, http.StatusTeapot)
   344  	}
   345  	if allow := w.Header().Get("Allow"); allow != "DELETE, OPTIONS, POST" {
   346  		t.Error("unexpected Allow header value: " + allow)
   347  	}
   348  }