// Copyright 2014 beego Author. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package web

import (
	"bytes"
	"fmt"
	"net/http"
	"net/http/httptest"
	"reflect"
	"sort"
	"strings"
	"testing"

	"github.com/beego/beego/v2/core/logs"
	"github.com/beego/beego/v2/server/web/context"
)

type PrefixTestController struct {
	Controller
}

func (ptc *PrefixTestController) PrefixList() {
	ptc.Ctx.Output.Body([]byte("i am list in prefix test"))
}

type TestControllerWithInterface struct{}

func (m TestControllerWithInterface) Ping() {
	fmt.Println("pong")
}

func (m *TestControllerWithInterface) PingPointer() {
	fmt.Println("pong pointer")
}

type TestController struct {
	Controller
}

func (tc *TestController) Get() {
	tc.Data["Username"] = "astaxie"
	tc.Ctx.Output.Body([]byte("ok"))
}

func (tc *TestController) Post() {
	tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Query(":name")))
}

func (tc *TestController) Param() {
	tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Query(":name")))
}

func (tc *TestController) List() {
	tc.Ctx.Output.Body([]byte("i am list"))
}

func (tc *TestController) Params() {
	tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Param("0") + tc.Ctx.Input.Param("1") + tc.Ctx.Input.Param("2")))
}

func (tc *TestController) Myext() {
	tc.Ctx.Output.Body([]byte(tc.Ctx.Input.Param(":ext")))
}

func (tc *TestController) GetURL() {
	tc.Ctx.Output.Body([]byte(tc.URLFor(".Myext")))
}

func (tc *TestController) GetParams() {
	tc.Ctx.WriteString(tc.Ctx.Input.Query(":last") + "+" +
		tc.Ctx.Input.Query(":first") + "+" + tc.Ctx.Input.Query("learn"))
}

func (tc *TestController) GetManyRouter() {
	tc.Ctx.WriteString(tc.Ctx.Input.Query(":id") + tc.Ctx.Input.Query(":page"))
}

func (tc *TestController) GetEmptyBody() {
	var res []byte
	tc.Ctx.Output.Body(res)
}

type JSONController struct {
	Controller
}

func (jc *JSONController) Prepare() {
	jc.Data["json"] = "prepare"
	jc.ServeJSON(true)
}

func (jc *JSONController) Get() {
	jc.Data["Username"] = "astaxie"
	jc.Ctx.Output.Body([]byte("ok"))
}

func TestPrefixUrlFor(t *testing.T) {
	handler := NewControllerRegister()
	handler.Add("/my/prefix/list", &PrefixTestController{}, WithRouterMethods(&PrefixTestController{}, "get:PrefixList"))

	if a := handler.URLFor(`PrefixTestController.PrefixList`); a != `/my/prefix/list` {
		logs.Info(a)
		t.Errorf("PrefixTestController.PrefixList must equal to /my/prefix/list")
	}
	if a := handler.URLFor(`TestController.PrefixList`); a != `` {
		logs.Info(a)
		t.Errorf("TestController.PrefixList must equal to empty string")
	}
}

func TestUrlFor(t *testing.T) {
	handler := NewControllerRegister()
	handler.Add("/api/list", &TestController{}, WithRouterMethods(&TestController{}, "*:List"))
	handler.Add("/person/:last/:first", &TestController{}, WithRouterMethods(&TestController{}, "*:Param"))
	if a := handler.URLFor("TestController.List"); a != "/api/list" {
		logs.Info(a)
		t.Errorf("TestController.List must equal to /api/list")
	}
	if a := handler.URLFor("TestController.Param", ":last", "xie", ":first", "asta"); a != "/person/xie/asta" {
		t.Errorf("TestController.Param must equal to /person/xie/asta, but get %s", a)
	}
}

func TestUrlFor3(t *testing.T) {
	handler := NewControllerRegister()
	handler.AddAuto(&TestController{})
	a := handler.URLFor("TestController.Myext")
	if a != "/test/myext" && a != "/Test/Myext" {
		t.Errorf("TestController.Myext must equal to /test/myext, but get %s", a)
	}
	a = handler.URLFor("TestController.GetURL")
	if a != "/test/geturl" && a != "/Test/GetURL" {
		t.Errorf("TestController.GetURL must equal to /test/geturl, but get %s", a)
	}
}

func TestUrlFor2(t *testing.T) {
	handler := NewControllerRegister()
	handler.Add("/v1/:v/cms_:id(.+)_:page(.+).html", &TestController{}, WithRouterMethods(&TestController{}, "*:List"))
	handler.Add("/v1/:username/edit", &TestController{}, WithRouterMethods(&TestController{}, "get:GetURL"))
	handler.Add("/v1/:v(.+)_cms/ttt_:id(.+)_:page(.+).html", &TestController{}, WithRouterMethods(&TestController{}, "*:Param"))
	handler.Add("/:year:int/:month:int/:title/:entid", &TestController{})
	if handler.URLFor("TestController.GetURL", ":username", "astaxie") != "/v1/astaxie/edit" {
		logs.Info(handler.URLFor("TestController.GetURL"))
		t.Errorf("TestController.List must equal to /v1/astaxie/edit")
	}

	if handler.URLFor("TestController.List", ":v", "za", ":id", "12", ":page", "123") !=
		"/v1/za/cms_12_123.html" {
		logs.Info(handler.URLFor("TestController.List"))
		t.Errorf("TestController.List must equal to /v1/za/cms_12_123.html")
	}
	if handler.URLFor("TestController.Param", ":v", "za", ":id", "12", ":page", "123") !=
		"/v1/za_cms/ttt_12_123.html" {
		logs.Info(handler.URLFor("TestController.Param"))
		t.Errorf("TestController.List must equal to /v1/za_cms/ttt_12_123.html")
	}
	if handler.URLFor("TestController.Get", ":year", "1111", ":month", "11",
		":title", "aaaa", ":entid", "aaaa") !=
		"/1111/11/aaaa/aaaa" {
		logs.Info(handler.URLFor("TestController.Get"))
		t.Errorf("TestController.Get must equal to /1111/11/aaaa/aaaa")
	}
}

func TestUserFunc(t *testing.T) {
	r, _ := http.NewRequest("GET", "/api/list", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.Add("/api/list", &TestController{}, WithRouterMethods(&TestController{}, "*:List"))
	handler.ServeHTTP(w, r)
	if w.Body.String() != "i am list" {
		t.Errorf("user define func can't run")
	}
}

func TestPostFunc(t *testing.T) {
	r, _ := http.NewRequest("POST", "/astaxie", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.Add("/:name", &TestController{})
	handler.ServeHTTP(w, r)
	if w.Body.String() != "astaxie" {
		t.Errorf("post func should astaxie")
	}
}

func TestAutoFunc(t *testing.T) {
	r, _ := http.NewRequest("GET", "/test/list", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.AddAuto(&TestController{})
	handler.ServeHTTP(w, r)
	if w.Body.String() != "i am list" {
		t.Errorf("user define func can't run")
	}
}

func TestAutoFunc2(t *testing.T) {
	r, _ := http.NewRequest("GET", "/Test/List", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.AddAuto(&TestController{})
	handler.ServeHTTP(w, r)
	if w.Body.String() != "i am list" {
		t.Errorf("user define func can't run")
	}
}

func TestAutoFuncParams(t *testing.T) {
	r, _ := http.NewRequest("GET", "/test/params/2009/11/12", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.AddAuto(&TestController{})
	handler.ServeHTTP(w, r)
	if w.Body.String() != "20091112" {
		t.Errorf("user define func can't run")
	}
}

func TestAutoExtFunc(t *testing.T) {
	r, _ := http.NewRequest("GET", "/test/myext.json", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.AddAuto(&TestController{})
	handler.ServeHTTP(w, r)
	if w.Body.String() != "json" {
		t.Errorf("user define func can't run")
	}
}

func TestEscape(t *testing.T) {
	r, _ := http.NewRequest("GET", "/search/%E4%BD%A0%E5%A5%BD", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.Get("/search/:keyword(.+)", func(ctx *context.Context) {
		value := ctx.Input.Param(":keyword")
		ctx.Output.Body([]byte(value))
	})
	handler.ServeHTTP(w, r)
	str := w.Body.String()
	if str != "你好" {
		t.Errorf("incorrect, %s", str)
	}
}

func TestRouteOk(t *testing.T) {
	r, _ := http.NewRequest("GET", "/person/anderson/thomas?learn=kungfu", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.Add("/person/:last/:first", &TestController{}, WithRouterMethods(&TestController{}, "get:GetParams"))
	handler.ServeHTTP(w, r)
	body := w.Body.String()
	if body != "anderson+thomas+kungfu" {
		t.Errorf("url param set to [%s];", body)
	}
}

func TestManyRoute(t *testing.T) {
	r, _ := http.NewRequest("GET", "/beego32-12.html", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.Add("/beego:id([0-9]+)-:page([0-9]+).html", &TestController{}, WithRouterMethods(&TestController{}, "get:GetManyRouter"))
	handler.ServeHTTP(w, r)

	body := w.Body.String()

	if body != "3212" {
		t.Errorf("url param set to [%s];", body)
	}
}

// Test for issue #1669
func TestEmptyResponse(t *testing.T) {
	r, _ := http.NewRequest("GET", "/beego-empty.html", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.Add("/beego-empty.html", &TestController{}, WithRouterMethods(&TestController{}, "get:GetEmptyBody"))
	handler.ServeHTTP(w, r)

	if body := w.Body.String(); body != "" {
		t.Error("want empty body")
	}
}

func TestNotFound(t *testing.T) {
	r, _ := http.NewRequest("GET", "/", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.ServeHTTP(w, r)

	if w.Code != http.StatusNotFound {
		t.Errorf("Code set to [%v]; want [%v]", w.Code, http.StatusNotFound)
	}
}

// TestStatic tests the ability to serve static
// content from the filesystem
func TestStatic(t *testing.T) {
	r, _ := http.NewRequest("GET", "/static/js/jquery.js", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.ServeHTTP(w, r)

	if w.Code != 404 {
		t.Errorf("handler.Static failed to serve file")
	}
}

func TestPrepare(t *testing.T) {
	r, _ := http.NewRequest("GET", "/json/list", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.Add("/json/list", &JSONController{})
	handler.ServeHTTP(w, r)
	if w.Body.String() != `"prepare"` {
		t.Errorf("%s user define func can't run", w.Body.String())
	}
}

func TestAutoPrefix(t *testing.T) {
	r, _ := http.NewRequest("GET", "/admin/test/list", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.AddAutoPrefix("/admin", &TestController{})
	handler.ServeHTTP(w, r)
	if w.Body.String() != "i am list" {
		t.Errorf("TestAutoPrefix can't run")
	}
}

func TestCtrlGet(t *testing.T) {
	r, _ := http.NewRequest("GET", "/user", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.Get("/user", func(ctx *context.Context) {
		ctx.Output.Body([]byte("Get userlist"))
	})
	handler.ServeHTTP(w, r)
	if w.Body.String() != "Get userlist" {
		t.Errorf("TestCtrlGet can't run")
	}
}

func TestCtrlPost(t *testing.T) {
	r, _ := http.NewRequest("POST", "/user/123", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.Post("/user/:id", func(ctx *context.Context) {
		ctx.Output.Body([]byte(ctx.Input.Param(":id")))
	})
	handler.ServeHTTP(w, r)
	if w.Body.String() != "123" {
		t.Errorf("TestCtrlPost can't run")
	}
}

func sayhello(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("sayhello"))
}

func TestRouterHandler(t *testing.T) {
	r, _ := http.NewRequest("POST", "/sayhi", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.Handler("/sayhi", http.HandlerFunc(sayhello))
	handler.ServeHTTP(w, r)
	if w.Body.String() != "sayhello" {
		t.Errorf("TestRouterHandler can't run")
	}
}

func TestRouterHandlerAll(t *testing.T) {
	r, _ := http.NewRequest("POST", "/sayhi/a/b/c", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.Handler("/sayhi", http.HandlerFunc(sayhello), true)
	handler.ServeHTTP(w, r)
	if w.Body.String() != "sayhello" {
		t.Errorf("TestRouterHandler can't run")
	}
}

//
// Benchmarks NewHttpSever:
//

func beegoFilterFunc(ctx *context.Context) {
	ctx.WriteString("hello")
}

type AdminController struct {
	Controller
}

func (a *AdminController) Get() {
	a.Ctx.WriteString("hello")
}

func TestRouterFunc(t *testing.T) {
	mux := NewControllerRegister()
	mux.Get("/action", beegoFilterFunc)
	mux.Post("/action", beegoFilterFunc)
	rw, r := testRequest("GET", "/action")
	mux.ServeHTTP(rw, r)
	if rw.Body.String() != "hello" {
		t.Errorf("TestRouterFunc can't run")
	}
}

func BenchmarkFunc(b *testing.B) {
	mux := NewControllerRegister()
	mux.Get("/action", beegoFilterFunc)
	rw, r := testRequest("GET", "/action")
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		mux.ServeHTTP(rw, r)
	}
}

func BenchmarkController(b *testing.B) {
	mux := NewControllerRegister()
	mux.Add("/action", &AdminController{})
	rw, r := testRequest("GET", "/action")
	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		mux.ServeHTTP(rw, r)
	}
}

func testRequest(method, path string) (*httptest.ResponseRecorder, *http.Request) {
	request, _ := http.NewRequest(method, path, nil)
	recorder := httptest.NewRecorder()

	return recorder, request
}

// Expectation: A Filter with the correct configuration should be created given
// specific parameters.
func TestInsertFilter(t *testing.T) {
	testName := "TestInsertFilter"

	mux := NewControllerRegister()
	mux.InsertFilter("*", BeforeRouter, func(*context.Context) {}, WithReturnOnOutput(true))
	if !mux.filters[BeforeRouter][0].returnOnOutput {
		t.Errorf(
			"%s: passing no variadic params should set returnOnOutput to true",
			testName)
	}
	if mux.filters[BeforeRouter][0].resetParams {
		t.Errorf(
			"%s: passing no variadic params should set resetParams to false",
			testName)
	}

	mux = NewControllerRegister()
	mux.InsertFilter("*", BeforeRouter, func(*context.Context) {}, WithReturnOnOutput(false))
	if mux.filters[BeforeRouter][0].returnOnOutput {
		t.Errorf(
			"%s: passing false as 1st variadic param should set returnOnOutput to false",
			testName)
	}

	mux = NewControllerRegister()
	mux.InsertFilter("*", BeforeRouter, func(*context.Context) {}, WithReturnOnOutput(true), WithResetParams(true))
	if !mux.filters[BeforeRouter][0].resetParams {
		t.Errorf(
			"%s: passing true as 2nd variadic param should set resetParams to true",
			testName)
	}
}

// Expectation: the second variadic arg should cause the execution of the filter
// to preserve the parameters from before its execution.
func TestParamResetFilter(t *testing.T) {
	testName := "TestParamResetFilter"
	route := "/beego/*" // splat
	path := "/beego/routes/routes"

	mux := NewControllerRegister()

	mux.InsertFilter("*", BeforeExec, beegoResetParams, WithReturnOnOutput(true), WithResetParams(true))

	mux.Get(route, beegoHandleResetParams)

	rw, r := testRequest("GET", path)
	mux.ServeHTTP(rw, r)

	// The two functions, `beegoResetParams` and `beegoHandleResetParams` add
	// a response header of `Splat`.  The expectation here is that Header
	// value should match what the _request's_ router set, not the filter's.

	headers := rw.Result().Header
	if len(headers["Splat"]) != 1 {
		t.Errorf(
			"%s: There was an error in the test. Splat param not set in Header",
			testName)
	}
	if headers["Splat"][0] != "routes/routes" {
		t.Errorf(
			"%s: expected `:splat` param to be [routes/routes] but it was [%s]",
			testName, headers["Splat"][0])
	}
}

// Execution point: BeforeRouter
// expectation: only BeforeRouter function is executed, notmatch output as router doesn't handle
func TestFilterBeforeRouter(t *testing.T) {
	testName := "TestFilterBeforeRouter"
	url := "/beforeRouter"

	mux := NewControllerRegister()
	mux.InsertFilter(url, BeforeRouter, beegoBeforeRouter1)

	mux.Get(url, beegoFilterFunc)

	rw, r := testRequest("GET", url)
	mux.ServeHTTP(rw, r)

	if !strings.Contains(rw.Body.String(), "BeforeRouter1") {
		t.Errorf("%s BeforeRouter did not run", testName)
	}
	if strings.Contains(rw.Body.String(), "hello") {
		t.Errorf("%s BeforeRouter did not return properly", testName)
	}
}

// Execution point: BeforeExec
// expectation: only BeforeExec function is executed, match as router determines route only
func TestFilterBeforeExec(t *testing.T) {
	testName := "TestFilterBeforeExec"
	url := "/beforeExec"

	mux := NewControllerRegister()
	mux.InsertFilter(url, BeforeRouter, beegoFilterNoOutput, WithReturnOnOutput(true))
	mux.InsertFilter(url, BeforeExec, beegoBeforeExec1, WithReturnOnOutput(true))

	mux.Get(url, beegoFilterFunc)

	rw, r := testRequest("GET", url)
	mux.ServeHTTP(rw, r)

	if !strings.Contains(rw.Body.String(), "BeforeExec1") {
		t.Errorf("%s BeforeExec did not run", testName)
	}
	if strings.Contains(rw.Body.String(), "hello") {
		t.Errorf("%s BeforeExec did not return properly", testName)
	}
	if strings.Contains(rw.Body.String(), "BeforeRouter") {
		t.Errorf("%s BeforeRouter ran in error", testName)
	}
}

// Execution point: AfterExec
// expectation: only AfterExec function is executed, match as router handles
func TestFilterAfterExec(t *testing.T) {
	testName := "TestFilterAfterExec"
	url := "/afterExec"

	mux := NewControllerRegister()
	mux.InsertFilter(url, BeforeRouter, beegoFilterNoOutput)
	mux.InsertFilter(url, BeforeExec, beegoFilterNoOutput)
	mux.InsertFilter(url, AfterExec, beegoAfterExec1, WithReturnOnOutput(false))

	mux.Get(url, beegoFilterFunc)

	rw, r := testRequest("GET", url)
	mux.ServeHTTP(rw, r)

	if !strings.Contains(rw.Body.String(), "AfterExec1") {
		t.Errorf("%s AfterExec did not run", testName)
	}
	if !strings.Contains(rw.Body.String(), "hello") {
		t.Errorf("%s handler did not run properly", testName)
	}
	if strings.Contains(rw.Body.String(), "BeforeRouter") {
		t.Errorf("%s BeforeRouter ran in error", testName)
	}
	if strings.Contains(rw.Body.String(), "BeforeExec") {
		t.Errorf("%s BeforeExec ran in error", testName)
	}
}

// Execution point: FinishRouter
// expectation: only FinishRouter function is executed, match as router handles
func TestFilterFinishRouter(t *testing.T) {
	testName := "TestFilterFinishRouter"
	url := "/finishRouter"

	mux := NewControllerRegister()
	mux.InsertFilter(url, BeforeRouter, beegoFilterNoOutput, WithReturnOnOutput(true))
	mux.InsertFilter(url, BeforeExec, beegoFilterNoOutput, WithReturnOnOutput(true))
	mux.InsertFilter(url, AfterExec, beegoFilterNoOutput, WithReturnOnOutput(true))
	mux.InsertFilter(url, FinishRouter, beegoFinishRouter1, WithReturnOnOutput(true))

	mux.Get(url, beegoFilterFunc)

	rw, r := testRequest("GET", url)
	mux.ServeHTTP(rw, r)

	if strings.Contains(rw.Body.String(), "FinishRouter1") {
		t.Errorf("%s FinishRouter did not run", testName)
	}
	if !strings.Contains(rw.Body.String(), "hello") {
		t.Errorf("%s handler did not run properly", testName)
	}
	if strings.Contains(rw.Body.String(), "AfterExec1") {
		t.Errorf("%s AfterExec ran in error", testName)
	}
	if strings.Contains(rw.Body.String(), "BeforeRouter") {
		t.Errorf("%s BeforeRouter ran in error", testName)
	}
	if strings.Contains(rw.Body.String(), "BeforeExec") {
		t.Errorf("%s BeforeExec ran in error", testName)
	}
}

// Execution point: FinishRouter
// expectation: only first FinishRouter function is executed, match as router handles
func TestFilterFinishRouterMultiFirstOnly(t *testing.T) {
	testName := "TestFilterFinishRouterMultiFirstOnly"
	url := "/finishRouterMultiFirstOnly"

	mux := NewControllerRegister()
	mux.InsertFilter(url, FinishRouter, beegoFinishRouter1, WithReturnOnOutput(false))
	mux.InsertFilter(url, FinishRouter, beegoFinishRouter2)

	mux.Get(url, beegoFilterFunc)

	rw, r := testRequest("GET", url)
	mux.ServeHTTP(rw, r)

	if !strings.Contains(rw.Body.String(), "FinishRouter1") {
		t.Errorf("%s FinishRouter1 did not run", testName)
	}
	if !strings.Contains(rw.Body.String(), "hello") {
		t.Errorf("%s handler did not run properly", testName)
	}
	// not expected in body
	if strings.Contains(rw.Body.String(), "FinishRouter2") {
		t.Errorf("%s FinishRouter2 did run", testName)
	}
}

// Execution point: FinishRouter
// expectation: both FinishRouter functions execute, match as router handles
func TestFilterFinishRouterMulti(t *testing.T) {
	testName := "TestFilterFinishRouterMulti"
	url := "/finishRouterMulti"

	mux := NewControllerRegister()
	mux.InsertFilter(url, FinishRouter, beegoFinishRouter1, WithReturnOnOutput(false))
	mux.InsertFilter(url, FinishRouter, beegoFinishRouter2, WithReturnOnOutput(false))

	mux.Get(url, beegoFilterFunc)

	rw, r := testRequest("GET", url)
	mux.ServeHTTP(rw, r)

	if !strings.Contains(rw.Body.String(), "FinishRouter1") {
		t.Errorf("%s FinishRouter1 did not run", testName)
	}
	if !strings.Contains(rw.Body.String(), "hello") {
		t.Errorf("%s handler did not run properly", testName)
	}
	if !strings.Contains(rw.Body.String(), "FinishRouter2") {
		t.Errorf("%s FinishRouter2 did not run properly", testName)
	}
}

func beegoFilterNoOutput(ctx *context.Context) {
}

func beegoBeforeRouter1(ctx *context.Context) {
	ctx.WriteString("|BeforeRouter1")
}

func beegoBeforeExec1(ctx *context.Context) {
	ctx.WriteString("|BeforeExec1")
}

func beegoAfterExec1(ctx *context.Context) {
	ctx.WriteString("|AfterExec1")
}

func beegoFinishRouter1(ctx *context.Context) {
	ctx.WriteString("|FinishRouter1")
}

func beegoFinishRouter2(ctx *context.Context) {
	ctx.WriteString("|FinishRouter2")
}

func beegoResetParams(ctx *context.Context) {
	ctx.ResponseWriter.Header().Set("splat", ctx.Input.Param(":splat"))
}

func beegoHandleResetParams(ctx *context.Context) {
	ctx.ResponseWriter.Header().Set("splat", ctx.Input.Param(":splat"))
}

// YAML
type YAMLController struct {
	Controller
}

func (jc *YAMLController) Prepare() {
	jc.Data["yaml"] = "prepare"
	jc.ServeYAML()
}

func (jc *YAMLController) Get() {
	jc.Data["Username"] = "astaxie"
	jc.Ctx.Output.Body([]byte("ok"))
}

func TestYAMLPrepare(t *testing.T) {
	r, _ := http.NewRequest("GET", "/yaml/list", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.Add("/yaml/list", &YAMLController{})
	handler.ServeHTTP(w, r)
	if strings.TrimSpace(w.Body.String()) != "prepare" {
		t.Errorf("%s", w.Body.String())
	}
}

func TestRouterEntityTooLargeCopyBody(t *testing.T) {
	_MaxMemory := BConfig.MaxMemory
	_CopyRequestBody := BConfig.CopyRequestBody
	BConfig.CopyRequestBody = true
	BConfig.MaxMemory = 20

	BeeApp.Cfg.CopyRequestBody = true
	BeeApp.Cfg.MaxMemory = 20
	b := bytes.NewBuffer([]byte("barbarbarbarbarbarbarbarbarbar"))
	r, _ := http.NewRequest("POST", "/user/123", b)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.Post("/user/:id", func(ctx *context.Context) {
		ctx.Output.Body([]byte(ctx.Input.Param(":id")))
	})
	handler.ServeHTTP(w, r)

	BConfig.CopyRequestBody = _CopyRequestBody
	BConfig.MaxMemory = _MaxMemory

	if w.Code != http.StatusRequestEntityTooLarge {
		t.Errorf("TestRouterRequestEntityTooLarge can't run")
	}
}

func TestRouterSessionSet(t *testing.T) {
	oldGlobalSessionOn := BConfig.WebConfig.Session.SessionOn
	defer func() {
		BConfig.WebConfig.Session.SessionOn = oldGlobalSessionOn
	}()

	// global sessionOn = false, router sessionOn = false
	r, _ := http.NewRequest("GET", "/user", nil)
	w := httptest.NewRecorder()
	handler := NewControllerRegister()
	handler.Add("/user", &TestController{}, WithRouterMethods(&TestController{}, "get:Get"),
		WithRouterSessionOn(false))
	handler.ServeHTTP(w, r)
	if w.Header().Get("Set-Cookie") != "" {
		t.Errorf("TestRotuerSessionSet failed")
	}

	// global sessionOn = false, router sessionOn = true
	r, _ = http.NewRequest("GET", "/user", nil)
	w = httptest.NewRecorder()
	handler = NewControllerRegister()
	handler.Add("/user", &TestController{}, WithRouterMethods(&TestController{}, "get:Get"),
		WithRouterSessionOn(true))
	handler.ServeHTTP(w, r)
	if w.Header().Get("Set-Cookie") != "" {
		t.Errorf("TestRotuerSessionSet failed")
	}

	BConfig.WebConfig.Session.SessionOn = true
	if err := registerSession(); err != nil {
		t.Errorf("register session failed, error: %s", err.Error())
	}
	// global sessionOn = true, router sessionOn = false
	r, _ = http.NewRequest("GET", "/user", nil)
	w = httptest.NewRecorder()
	handler = NewControllerRegister()
	handler.Add("/user", &TestController{}, WithRouterMethods(&TestController{}, "get:Get"),
		WithRouterSessionOn(false))
	handler.ServeHTTP(w, r)
	if w.Header().Get("Set-Cookie") != "" {
		t.Errorf("TestRotuerSessionSet failed")
	}

	// global sessionOn = true, router sessionOn = true
	r, _ = http.NewRequest("GET", "/user", nil)
	w = httptest.NewRecorder()
	handler = NewControllerRegister()
	handler.Add("/user", &TestController{}, WithRouterMethods(&TestController{}, "get:Get"),
		WithRouterSessionOn(true))
	handler.ServeHTTP(w, r)
	if w.Header().Get("Set-Cookie") == "" {
		t.Errorf("TestRotuerSessionSet failed")
	}
}

func TestRouterCtrlGet(t *testing.T) {
	r, _ := http.NewRequest(http.MethodGet, "/user", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.CtrlGet("/user", ExampleController.Ping)
	handler.ServeHTTP(w, r)
	if w.Body.String() != exampleBody {
		t.Errorf("TestRouterCtrlGet can't run")
	}
}

func TestRouterCtrlPost(t *testing.T) {
	r, _ := http.NewRequest(http.MethodPost, "/user", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.CtrlPost("/user", ExampleController.Ping)
	handler.ServeHTTP(w, r)
	if w.Body.String() != exampleBody {
		t.Errorf("TestRouterCtrlPost can't run")
	}
}

func TestRouterCtrlHead(t *testing.T) {
	r, _ := http.NewRequest(http.MethodHead, "/user", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.CtrlHead("/user", ExampleController.Ping)
	handler.ServeHTTP(w, r)
	if w.Body.String() != exampleBody {
		t.Errorf("TestRouterCtrlHead can't run")
	}
}

func TestRouterCtrlPut(t *testing.T) {
	r, _ := http.NewRequest(http.MethodPut, "/user", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.CtrlPut("/user", ExampleController.Ping)
	handler.ServeHTTP(w, r)
	if w.Body.String() != exampleBody {
		t.Errorf("TestRouterCtrlPut can't run")
	}
}

func TestRouterCtrlPatch(t *testing.T) {
	r, _ := http.NewRequest(http.MethodPatch, "/user", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.CtrlPatch("/user", ExampleController.Ping)
	handler.ServeHTTP(w, r)
	if w.Body.String() != exampleBody {
		t.Errorf("TestRouterCtrlPatch can't run")
	}
}

func TestRouterCtrlDelete(t *testing.T) {
	r, _ := http.NewRequest(http.MethodDelete, "/user", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.CtrlDelete("/user", ExampleController.Ping)
	handler.ServeHTTP(w, r)
	if w.Body.String() != exampleBody {
		t.Errorf("TestRouterCtrlDelete can't run")
	}
}

func TestRouterCtrlAny(t *testing.T) {
	handler := NewControllerRegister()
	handler.CtrlAny("/user", ExampleController.Ping)

	for method := range HTTPMETHOD {
		w := httptest.NewRecorder()
		r, _ := http.NewRequest(method, "/user", nil)
		handler.ServeHTTP(w, r)
		if w.Body.String() != exampleBody {
			t.Errorf("TestRouterCtrlAny can't run, get the response is %s", w.Body.String())
		}
	}
}

func TestRouterCtrlGetPointerMethod(t *testing.T) {
	r, _ := http.NewRequest(http.MethodGet, "/user", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.CtrlGet("/user", (*ExampleController).PingPointer)
	handler.ServeHTTP(w, r)
	if w.Body.String() != examplePointerBody {
		t.Errorf("TestRouterCtrlGetPointerMethod can't run")
	}
}

func TestRouterCtrlPostPointerMethod(t *testing.T) {
	r, _ := http.NewRequest(http.MethodPost, "/user", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.CtrlPost("/user", (*ExampleController).PingPointer)
	handler.ServeHTTP(w, r)
	if w.Body.String() != examplePointerBody {
		t.Errorf("TestRouterCtrlPostPointerMethod can't run")
	}
}

func TestRouterCtrlHeadPointerMethod(t *testing.T) {
	r, _ := http.NewRequest(http.MethodHead, "/user", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.CtrlHead("/user", (*ExampleController).PingPointer)
	handler.ServeHTTP(w, r)
	if w.Body.String() != examplePointerBody {
		t.Errorf("TestRouterCtrlHeadPointerMethod can't run")
	}
}

func TestRouterCtrlPutPointerMethod(t *testing.T) {
	r, _ := http.NewRequest(http.MethodPut, "/user", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.CtrlPut("/user", (*ExampleController).PingPointer)
	handler.ServeHTTP(w, r)
	if w.Body.String() != examplePointerBody {
		t.Errorf("TestRouterCtrlPutPointerMethod can't run")
	}
}

func TestRouterCtrlPatchPointerMethod(t *testing.T) {
	r, _ := http.NewRequest(http.MethodPatch, "/user", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.CtrlPatch("/user", (*ExampleController).PingPointer)
	handler.ServeHTTP(w, r)
	if w.Body.String() != examplePointerBody {
		t.Errorf("TestRouterCtrlPatchPointerMethod can't run")
	}
}

func TestRouterCtrlDeletePointerMethod(t *testing.T) {
	r, _ := http.NewRequest(http.MethodDelete, "/user", nil)
	w := httptest.NewRecorder()

	handler := NewControllerRegister()
	handler.CtrlDelete("/user", (*ExampleController).PingPointer)
	handler.ServeHTTP(w, r)
	if w.Body.String() != examplePointerBody {
		t.Errorf("TestRouterCtrlDeletePointerMethod can't run")
	}
}

func TestRouterCtrlAnyPointerMethod(t *testing.T) {
	handler := NewControllerRegister()
	handler.CtrlAny("/user", (*ExampleController).PingPointer)

	for method := range HTTPMETHOD {
		w := httptest.NewRecorder()
		r, _ := http.NewRequest(method, "/user", nil)
		handler.ServeHTTP(w, r)
		if w.Body.String() != examplePointerBody {
			t.Errorf("TestRouterCtrlAnyPointerMethod can't run, get the response is %s", w.Body.String())
		}
	}
}

func TestRouterAddRouterMethodPanicInvalidMethod(t *testing.T) {
	method := "some random method"
	message := "not support http method: " + strings.ToUpper(method)
	defer func() {
		err := recover()
		if err != nil { // 产生了panic异常
			errStr, ok := err.(string)
			if ok && errStr == message {
				return
			}
		}
		t.Errorf("TestRouterAddRouterMethodPanicInvalidMethod failed: %v", err)
	}()

	handler := NewControllerRegister()
	handler.AddRouterMethod(method, "/user", ExampleController.Ping)
}

func TestRouterAddRouterMethodPanicNotAMethod(t *testing.T) {
	method := http.MethodGet
	message := "not a method"
	defer func() {
		err := recover()
		if err != nil { // 产生了panic异常
			errStr, ok := err.(string)
			if ok && errStr == message {
				return
			}
		}
		t.Errorf("TestRouterAddRouterMethodPanicNotAMethod failed: %v", err)
	}()

	handler := NewControllerRegister()
	handler.AddRouterMethod(method, "/user", ExampleController{})
}

func TestRouterAddRouterMethodPanicNotPublicMethod(t *testing.T) {
	method := http.MethodGet
	message := "ping is not a public method"
	defer func() {
		err := recover()
		if err != nil { // 产生了panic异常
			errStr, ok := err.(string)
			if ok && errStr == message {
				return
			}
		}
		t.Errorf("TestRouterAddRouterMethodPanicNotPublicMethod failed: %v", err)
	}()

	handler := NewControllerRegister()
	handler.AddRouterMethod(method, "/user", ExampleController.ping)
}

func TestRouterAddRouterMethodPanicNotImplementInterface(t *testing.T) {
	method := http.MethodGet
	message := "web.TestControllerWithInterface is not implemented ControllerInterface"
	defer func() {
		err := recover()
		if err != nil { // 产生了panic异常
			errStr, ok := err.(string)
			if ok && errStr == message {
				return
			}
		}
		t.Errorf("TestRouterAddRouterMethodPanicNotImplementInterface failed: %v", err)
	}()

	handler := NewControllerRegister()
	handler.AddRouterMethod(method, "/user", TestControllerWithInterface.Ping)
}

func TestRouterAddRouterPointerMethodPanicNotImplementInterface(t *testing.T) {
	method := http.MethodGet
	message := "web.TestControllerWithInterface is not implemented ControllerInterface"
	defer func() {
		err := recover()
		if err != nil { // 产生了panic异常
			errStr, ok := err.(string)
			if ok && errStr == message {
				return
			}
		}
		t.Errorf("TestRouterAddRouterPointerMethodPanicNotImplementInterface failed: %v", err)
	}()

	handler := NewControllerRegister()
	handler.AddRouterMethod(method, "/user", (*TestControllerWithInterface).PingPointer)
}

func TestGetAllControllerInfo(t *testing.T) {
	handler := NewControllerRegister()
	handler.Add("/level1", &TestController{}, WithRouterMethods(&TestController{}, "get:Get"))
	handler.Add("/level1/level2", &TestController{}, WithRouterMethods(&TestController{}, "get:Get"))
	handler.Add("/:name1", &TestController{}, WithRouterMethods(&TestController{}, "post:Post"))

	var actualPatterns []string
	var actualMethods []string
	for _, controllerInfo := range handler.GetAllControllerInfo() {
		actualPatterns = append(actualPatterns, controllerInfo.GetPattern())
		for _, httpMethod := range controllerInfo.GetMethod() {
			actualMethods = append(actualMethods, httpMethod)
		}
	}
	sort.Strings(actualPatterns)
	expectedPatterns := []string{"/level1", "/level1/level2", "/:name1"}
	sort.Strings(expectedPatterns)
	if !reflect.DeepEqual(actualPatterns, expectedPatterns) {
		t.Errorf("ControllerInfo.GetMethod expected %#v, but %#v got", expectedPatterns, actualPatterns)
	}

	sort.Strings(actualMethods)
	expectedMethods := []string{"Get", "Get", "Post"}
	sort.Strings(expectedMethods)
	if !reflect.DeepEqual(actualMethods, expectedMethods) {
		t.Errorf("ControllerInfo.GetMethod expected %#v, but %#v got", expectedMethods, actualMethods)
	}
}
