github.com/hxx258456/ccgo@v0.0.5-0.20230213014102-48b35f46f66f/mux/middleware_test.go (about)

     1  package mux
     2  
     3  import (
     4  	"bytes"
     5  	"testing"
     6  
     7  	http "github.com/hxx258456/ccgo/gmhttp"
     8  )
     9  
    10  type testMiddleware struct {
    11  	timesCalled uint
    12  }
    13  
    14  func (tm *testMiddleware) Middleware(h http.Handler) http.Handler {
    15  	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    16  		tm.timesCalled++
    17  		h.ServeHTTP(w, r)
    18  	})
    19  }
    20  
    21  func dummyHandler(w http.ResponseWriter, r *http.Request) {}
    22  
    23  func TestMiddlewareAdd(t *testing.T) {
    24  	router := NewRouter()
    25  	router.HandleFunc("/", dummyHandler).Methods("GET")
    26  
    27  	mw := &testMiddleware{}
    28  
    29  	router.useInterface(mw)
    30  	if len(router.middlewares) != 1 || router.middlewares[0] != mw {
    31  		t.Fatal("Middleware interface was not added correctly")
    32  	}
    33  
    34  	router.Use(mw.Middleware)
    35  	if len(router.middlewares) != 2 {
    36  		t.Fatal("Middleware method was not added correctly")
    37  	}
    38  
    39  	banalMw := func(handler http.Handler) http.Handler {
    40  		return handler
    41  	}
    42  	router.Use(banalMw)
    43  	if len(router.middlewares) != 3 {
    44  		t.Fatal("Middleware function was not added correctly")
    45  	}
    46  }
    47  
    48  func TestMiddleware(t *testing.T) {
    49  	router := NewRouter()
    50  	router.HandleFunc("/", dummyHandler).Methods("GET")
    51  
    52  	mw := &testMiddleware{}
    53  	router.useInterface(mw)
    54  
    55  	rw := NewRecorder()
    56  	req := newRequest("GET", "/")
    57  
    58  	t.Run("regular middleware call", func(t *testing.T) {
    59  		router.ServeHTTP(rw, req)
    60  		if mw.timesCalled != 1 {
    61  			t.Fatalf("Expected %d calls, but got only %d", 1, mw.timesCalled)
    62  		}
    63  	})
    64  
    65  	t.Run("not called for 404", func(t *testing.T) {
    66  		req = newRequest("GET", "/not/found")
    67  		router.ServeHTTP(rw, req)
    68  		if mw.timesCalled != 1 {
    69  			t.Fatalf("Expected %d calls, but got only %d", 1, mw.timesCalled)
    70  		}
    71  	})
    72  
    73  	t.Run("not called for method mismatch", func(t *testing.T) {
    74  		req = newRequest("POST", "/")
    75  		router.ServeHTTP(rw, req)
    76  		if mw.timesCalled != 1 {
    77  			t.Fatalf("Expected %d calls, but got only %d", 1, mw.timesCalled)
    78  		}
    79  	})
    80  
    81  	t.Run("regular call using function middleware", func(t *testing.T) {
    82  		router.Use(mw.Middleware)
    83  		req = newRequest("GET", "/")
    84  		router.ServeHTTP(rw, req)
    85  		if mw.timesCalled != 3 {
    86  			t.Fatalf("Expected %d calls, but got only %d", 3, mw.timesCalled)
    87  		}
    88  	})
    89  }
    90  
    91  func TestMiddlewareSubrouter(t *testing.T) {
    92  	router := NewRouter()
    93  	router.HandleFunc("/", dummyHandler).Methods("GET")
    94  
    95  	subrouter := router.PathPrefix("/sub").Subrouter()
    96  	subrouter.HandleFunc("/x", dummyHandler).Methods("GET")
    97  
    98  	mw := &testMiddleware{}
    99  	subrouter.useInterface(mw)
   100  
   101  	rw := NewRecorder()
   102  	req := newRequest("GET", "/")
   103  
   104  	t.Run("not called for route outside subrouter", func(t *testing.T) {
   105  		router.ServeHTTP(rw, req)
   106  		if mw.timesCalled != 0 {
   107  			t.Fatalf("Expected %d calls, but got only %d", 0, mw.timesCalled)
   108  		}
   109  	})
   110  
   111  	t.Run("not called for subrouter root 404", func(t *testing.T) {
   112  		req = newRequest("GET", "/sub/")
   113  		router.ServeHTTP(rw, req)
   114  		if mw.timesCalled != 0 {
   115  			t.Fatalf("Expected %d calls, but got only %d", 0, mw.timesCalled)
   116  		}
   117  	})
   118  
   119  	t.Run("called once for route inside subrouter", func(t *testing.T) {
   120  		req = newRequest("GET", "/sub/x")
   121  		router.ServeHTTP(rw, req)
   122  		if mw.timesCalled != 1 {
   123  			t.Fatalf("Expected %d calls, but got only %d", 1, mw.timesCalled)
   124  		}
   125  	})
   126  
   127  	t.Run("not called for 404 inside subrouter", func(t *testing.T) {
   128  		req = newRequest("GET", "/sub/not/found")
   129  		router.ServeHTTP(rw, req)
   130  		if mw.timesCalled != 1 {
   131  			t.Fatalf("Expected %d calls, but got only %d", 1, mw.timesCalled)
   132  		}
   133  	})
   134  
   135  	t.Run("middleware added to router", func(t *testing.T) {
   136  		router.useInterface(mw)
   137  
   138  		t.Run("called once for route outside subrouter", func(t *testing.T) {
   139  			req = newRequest("GET", "/")
   140  			router.ServeHTTP(rw, req)
   141  			if mw.timesCalled != 2 {
   142  				t.Fatalf("Expected %d calls, but got only %d", 2, mw.timesCalled)
   143  			}
   144  		})
   145  
   146  		t.Run("called twice for route inside subrouter", func(t *testing.T) {
   147  			req = newRequest("GET", "/sub/x")
   148  			router.ServeHTTP(rw, req)
   149  			if mw.timesCalled != 4 {
   150  				t.Fatalf("Expected %d calls, but got only %d", 4, mw.timesCalled)
   151  			}
   152  		})
   153  	})
   154  }
   155  
   156  func TestMiddlewareExecution(t *testing.T) {
   157  	mwStr := []byte("Middleware\n")
   158  	handlerStr := []byte("Logic\n")
   159  
   160  	router := NewRouter()
   161  	router.HandleFunc("/", func(w http.ResponseWriter, e *http.Request) {
   162  		w.Write(handlerStr)
   163  	})
   164  
   165  	t.Run("responds normally without middleware", func(t *testing.T) {
   166  		rw := NewRecorder()
   167  		req := newRequest("GET", "/")
   168  
   169  		router.ServeHTTP(rw, req)
   170  
   171  		if !bytes.Equal(rw.Body.Bytes(), handlerStr) {
   172  			t.Fatal("Handler response is not what it should be")
   173  		}
   174  	})
   175  
   176  	t.Run("responds with handler and middleware response", func(t *testing.T) {
   177  		rw := NewRecorder()
   178  		req := newRequest("GET", "/")
   179  
   180  		router.Use(func(h http.Handler) http.Handler {
   181  			return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   182  				w.Write(mwStr)
   183  				h.ServeHTTP(w, r)
   184  			})
   185  		})
   186  
   187  		router.ServeHTTP(rw, req)
   188  		if !bytes.Equal(rw.Body.Bytes(), append(mwStr, handlerStr...)) {
   189  			t.Fatal("Middleware + handler response is not what it should be")
   190  		}
   191  	})
   192  }
   193  
   194  func TestMiddlewareNotFound(t *testing.T) {
   195  	mwStr := []byte("Middleware\n")
   196  	handlerStr := []byte("Logic\n")
   197  
   198  	router := NewRouter()
   199  	router.HandleFunc("/", func(w http.ResponseWriter, e *http.Request) {
   200  		w.Write(handlerStr)
   201  	})
   202  	router.Use(func(h http.Handler) http.Handler {
   203  		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   204  			w.Write(mwStr)
   205  			h.ServeHTTP(w, r)
   206  		})
   207  	})
   208  
   209  	// Test not found call with default handler
   210  	t.Run("not called", func(t *testing.T) {
   211  		rw := NewRecorder()
   212  		req := newRequest("GET", "/notfound")
   213  
   214  		router.ServeHTTP(rw, req)
   215  		if bytes.Contains(rw.Body.Bytes(), mwStr) {
   216  			t.Fatal("Middleware was called for a 404")
   217  		}
   218  	})
   219  
   220  	t.Run("not called with custom not found handler", func(t *testing.T) {
   221  		rw := NewRecorder()
   222  		req := newRequest("GET", "/notfound")
   223  
   224  		router.NotFoundHandler = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
   225  			rw.Write([]byte("Custom 404 handler"))
   226  		})
   227  		router.ServeHTTP(rw, req)
   228  
   229  		if bytes.Contains(rw.Body.Bytes(), mwStr) {
   230  			t.Fatal("Middleware was called for a custom 404")
   231  		}
   232  	})
   233  }
   234  
   235  func TestMiddlewareMethodMismatch(t *testing.T) {
   236  	mwStr := []byte("Middleware\n")
   237  	handlerStr := []byte("Logic\n")
   238  
   239  	router := NewRouter()
   240  	router.HandleFunc("/", func(w http.ResponseWriter, e *http.Request) {
   241  		w.Write(handlerStr)
   242  	}).Methods("GET")
   243  
   244  	router.Use(func(h http.Handler) http.Handler {
   245  		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   246  			w.Write(mwStr)
   247  			h.ServeHTTP(w, r)
   248  		})
   249  	})
   250  
   251  	t.Run("not called", func(t *testing.T) {
   252  		rw := NewRecorder()
   253  		req := newRequest("POST", "/")
   254  
   255  		router.ServeHTTP(rw, req)
   256  		if bytes.Contains(rw.Body.Bytes(), mwStr) {
   257  			t.Fatal("Middleware was called for a method mismatch")
   258  		}
   259  	})
   260  
   261  	t.Run("not called with custom method not allowed handler", func(t *testing.T) {
   262  		rw := NewRecorder()
   263  		req := newRequest("POST", "/")
   264  
   265  		router.MethodNotAllowedHandler = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
   266  			rw.Write([]byte("Method not allowed"))
   267  		})
   268  		router.ServeHTTP(rw, req)
   269  
   270  		if bytes.Contains(rw.Body.Bytes(), mwStr) {
   271  			t.Fatal("Middleware was called for a method mismatch")
   272  		}
   273  	})
   274  }
   275  
   276  func TestMiddlewareNotFoundSubrouter(t *testing.T) {
   277  	mwStr := []byte("Middleware\n")
   278  	handlerStr := []byte("Logic\n")
   279  
   280  	router := NewRouter()
   281  	router.HandleFunc("/", func(w http.ResponseWriter, e *http.Request) {
   282  		w.Write(handlerStr)
   283  	})
   284  
   285  	subrouter := router.PathPrefix("/sub/").Subrouter()
   286  	subrouter.HandleFunc("/", func(w http.ResponseWriter, e *http.Request) {
   287  		w.Write(handlerStr)
   288  	})
   289  
   290  	router.Use(func(h http.Handler) http.Handler {
   291  		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   292  			w.Write(mwStr)
   293  			h.ServeHTTP(w, r)
   294  		})
   295  	})
   296  
   297  	t.Run("not called", func(t *testing.T) {
   298  		rw := NewRecorder()
   299  		req := newRequest("GET", "/sub/notfound")
   300  
   301  		router.ServeHTTP(rw, req)
   302  		if bytes.Contains(rw.Body.Bytes(), mwStr) {
   303  			t.Fatal("Middleware was called for a 404")
   304  		}
   305  	})
   306  
   307  	t.Run("not called with custom not found handler", func(t *testing.T) {
   308  		rw := NewRecorder()
   309  		req := newRequest("GET", "/sub/notfound")
   310  
   311  		subrouter.NotFoundHandler = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
   312  			rw.Write([]byte("Custom 404 handler"))
   313  		})
   314  		router.ServeHTTP(rw, req)
   315  
   316  		if bytes.Contains(rw.Body.Bytes(), mwStr) {
   317  			t.Fatal("Middleware was called for a custom 404")
   318  		}
   319  	})
   320  }
   321  
   322  func TestMiddlewareMethodMismatchSubrouter(t *testing.T) {
   323  	mwStr := []byte("Middleware\n")
   324  	handlerStr := []byte("Logic\n")
   325  
   326  	router := NewRouter()
   327  	router.HandleFunc("/", func(w http.ResponseWriter, e *http.Request) {
   328  		w.Write(handlerStr)
   329  	})
   330  
   331  	subrouter := router.PathPrefix("/sub/").Subrouter()
   332  	subrouter.HandleFunc("/", func(w http.ResponseWriter, e *http.Request) {
   333  		w.Write(handlerStr)
   334  	}).Methods("GET")
   335  
   336  	router.Use(func(h http.Handler) http.Handler {
   337  		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   338  			w.Write(mwStr)
   339  			h.ServeHTTP(w, r)
   340  		})
   341  	})
   342  
   343  	t.Run("not called", func(t *testing.T) {
   344  		rw := NewRecorder()
   345  		req := newRequest("POST", "/sub/")
   346  
   347  		router.ServeHTTP(rw, req)
   348  		if bytes.Contains(rw.Body.Bytes(), mwStr) {
   349  			t.Fatal("Middleware was called for a method mismatch")
   350  		}
   351  	})
   352  
   353  	t.Run("not called with custom method not allowed handler", func(t *testing.T) {
   354  		rw := NewRecorder()
   355  		req := newRequest("POST", "/sub/")
   356  
   357  		router.MethodNotAllowedHandler = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
   358  			rw.Write([]byte("Method not allowed"))
   359  		})
   360  		router.ServeHTTP(rw, req)
   361  
   362  		if bytes.Contains(rw.Body.Bytes(), mwStr) {
   363  			t.Fatal("Middleware was called for a method mismatch")
   364  		}
   365  	})
   366  }
   367  
   368  func TestCORSMethodMiddleware(t *testing.T) {
   369  	testCases := []struct {
   370  		name                                    string
   371  		registerRoutes                          func(r *Router)
   372  		requestHeader                           http.Header
   373  		requestMethod                           string
   374  		requestPath                             string
   375  		expectedAccessControlAllowMethodsHeader string
   376  		expectedResponse                        string
   377  	}{
   378  		{
   379  			name: "does not set without OPTIONS matcher",
   380  			registerRoutes: func(r *Router) {
   381  				r.HandleFunc("/foo", stringHandler("a")).Methods(http.MethodGet, http.MethodPut, http.MethodPatch)
   382  			},
   383  			requestMethod:                           "GET",
   384  			requestPath:                             "/foo",
   385  			expectedAccessControlAllowMethodsHeader: "",
   386  			expectedResponse:                        "a",
   387  		},
   388  		{
   389  			name: "sets on non OPTIONS",
   390  			registerRoutes: func(r *Router) {
   391  				r.HandleFunc("/foo", stringHandler("a")).Methods(http.MethodGet, http.MethodPut, http.MethodPatch)
   392  				r.HandleFunc("/foo", stringHandler("b")).Methods(http.MethodOptions)
   393  			},
   394  			requestMethod:                           "GET",
   395  			requestPath:                             "/foo",
   396  			expectedAccessControlAllowMethodsHeader: "GET,PUT,PATCH,OPTIONS",
   397  			expectedResponse:                        "a",
   398  		},
   399  		{
   400  			name: "sets without preflight headers",
   401  			registerRoutes: func(r *Router) {
   402  				r.HandleFunc("/foo", stringHandler("a")).Methods(http.MethodGet, http.MethodPut, http.MethodPatch)
   403  				r.HandleFunc("/foo", stringHandler("b")).Methods(http.MethodOptions)
   404  			},
   405  			requestMethod:                           "OPTIONS",
   406  			requestPath:                             "/foo",
   407  			expectedAccessControlAllowMethodsHeader: "GET,PUT,PATCH,OPTIONS",
   408  			expectedResponse:                        "b",
   409  		},
   410  		{
   411  			name: "does not set on error",
   412  			registerRoutes: func(r *Router) {
   413  				r.HandleFunc("/foo", stringHandler("a"))
   414  			},
   415  			requestMethod:                           "OPTIONS",
   416  			requestPath:                             "/foo",
   417  			expectedAccessControlAllowMethodsHeader: "",
   418  			expectedResponse:                        "a",
   419  		},
   420  		{
   421  			name: "sets header on valid preflight",
   422  			registerRoutes: func(r *Router) {
   423  				r.HandleFunc("/foo", stringHandler("a")).Methods(http.MethodGet, http.MethodPut, http.MethodPatch)
   424  				r.HandleFunc("/foo", stringHandler("b")).Methods(http.MethodOptions)
   425  			},
   426  			requestMethod: "OPTIONS",
   427  			requestPath:   "/foo",
   428  			requestHeader: http.Header{
   429  				"Access-Control-Request-Method":  []string{"GET"},
   430  				"Access-Control-Request-Headers": []string{"Authorization"},
   431  				"Origin":                         []string{"http://example.com"},
   432  			},
   433  			expectedAccessControlAllowMethodsHeader: "GET,PUT,PATCH,OPTIONS",
   434  			expectedResponse:                        "b",
   435  		},
   436  		{
   437  			name: "does not set methods from unmatching routes",
   438  			registerRoutes: func(r *Router) {
   439  				r.HandleFunc("/foo", stringHandler("c")).Methods(http.MethodDelete)
   440  				r.HandleFunc("/foo/bar", stringHandler("a")).Methods(http.MethodGet, http.MethodPut, http.MethodPatch)
   441  				r.HandleFunc("/foo/bar", stringHandler("b")).Methods(http.MethodOptions)
   442  			},
   443  			requestMethod: "OPTIONS",
   444  			requestPath:   "/foo/bar",
   445  			requestHeader: http.Header{
   446  				"Access-Control-Request-Method":  []string{"GET"},
   447  				"Access-Control-Request-Headers": []string{"Authorization"},
   448  				"Origin":                         []string{"http://example.com"},
   449  			},
   450  			expectedAccessControlAllowMethodsHeader: "GET,PUT,PATCH,OPTIONS",
   451  			expectedResponse:                        "b",
   452  		},
   453  	}
   454  
   455  	for _, tt := range testCases {
   456  		t.Run(tt.name, func(t *testing.T) {
   457  			router := NewRouter()
   458  
   459  			tt.registerRoutes(router)
   460  
   461  			router.Use(CORSMethodMiddleware(router))
   462  
   463  			rw := NewRecorder()
   464  			req := newRequest(tt.requestMethod, tt.requestPath)
   465  			req.Header = tt.requestHeader
   466  
   467  			router.ServeHTTP(rw, req)
   468  
   469  			actualMethodsHeader := rw.Header().Get("Access-Control-Allow-Methods")
   470  			if actualMethodsHeader != tt.expectedAccessControlAllowMethodsHeader {
   471  				t.Fatalf("Expected Access-Control-Allow-Methods to equal %s but got %s", tt.expectedAccessControlAllowMethodsHeader, actualMethodsHeader)
   472  			}
   473  
   474  			actualResponse := rw.Body.String()
   475  			if actualResponse != tt.expectedResponse {
   476  				t.Fatalf("Expected response to equal %s but got %s", tt.expectedResponse, actualResponse)
   477  			}
   478  		})
   479  	}
   480  }
   481  
   482  func TestCORSMethodMiddlewareSubrouter(t *testing.T) {
   483  	router := NewRouter().StrictSlash(true)
   484  
   485  	subrouter := router.PathPrefix("/test").Subrouter()
   486  	subrouter.HandleFunc("/hello", stringHandler("a")).Methods(http.MethodGet, http.MethodOptions, http.MethodPost)
   487  	subrouter.HandleFunc("/hello/{name}", stringHandler("b")).Methods(http.MethodGet, http.MethodOptions)
   488  
   489  	subrouter.Use(CORSMethodMiddleware(subrouter))
   490  
   491  	rw := NewRecorder()
   492  	req := newRequest("GET", "/test/hello/asdf")
   493  	router.ServeHTTP(rw, req)
   494  
   495  	actualMethods := rw.Header().Get("Access-Control-Allow-Methods")
   496  	expectedMethods := "GET,OPTIONS"
   497  	if actualMethods != expectedMethods {
   498  		t.Fatalf("expected methods %q but got: %q", expectedMethods, actualMethods)
   499  	}
   500  }
   501  
   502  func TestMiddlewareOnMultiSubrouter(t *testing.T) {
   503  	first := "first"
   504  	second := "second"
   505  	notFound := "404 not found"
   506  
   507  	router := NewRouter()
   508  	firstSubRouter := router.PathPrefix("/").Subrouter()
   509  	secondSubRouter := router.PathPrefix("/").Subrouter()
   510  
   511  	router.NotFoundHandler = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
   512  		rw.Write([]byte(notFound))
   513  	})
   514  
   515  	firstSubRouter.HandleFunc("/first", func(w http.ResponseWriter, r *http.Request) {
   516  
   517  	})
   518  
   519  	secondSubRouter.HandleFunc("/second", func(w http.ResponseWriter, r *http.Request) {
   520  
   521  	})
   522  
   523  	firstSubRouter.Use(func(h http.Handler) http.Handler {
   524  		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   525  			w.Write([]byte(first))
   526  			h.ServeHTTP(w, r)
   527  		})
   528  	})
   529  
   530  	secondSubRouter.Use(func(h http.Handler) http.Handler {
   531  		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   532  			w.Write([]byte(second))
   533  			h.ServeHTTP(w, r)
   534  		})
   535  	})
   536  
   537  	t.Run("/first uses first middleware", func(t *testing.T) {
   538  		rw := NewRecorder()
   539  		req := newRequest("GET", "/first")
   540  
   541  		router.ServeHTTP(rw, req)
   542  		if rw.Body.String() != first {
   543  			t.Fatalf("Middleware did not run: expected %s middleware to write a response (got %s)", first, rw.Body.String())
   544  		}
   545  	})
   546  
   547  	t.Run("/second uses second middleware", func(t *testing.T) {
   548  		rw := NewRecorder()
   549  		req := newRequest("GET", "/second")
   550  
   551  		router.ServeHTTP(rw, req)
   552  		if rw.Body.String() != second {
   553  			t.Fatalf("Middleware did not run: expected %s middleware to write a response (got %s)", second, rw.Body.String())
   554  		}
   555  	})
   556  
   557  	t.Run("uses not found handler", func(t *testing.T) {
   558  		rw := NewRecorder()
   559  		req := newRequest("GET", "/second/not-exist")
   560  
   561  		router.ServeHTTP(rw, req)
   562  		if rw.Body.String() != notFound {
   563  			t.Fatalf("Notfound handler did not run: expected %s for not-exist, (got %s)", notFound, rw.Body.String())
   564  		}
   565  	})
   566  }