From f622a17ab6d82f3bec689caa8ee27560bd99c339 Mon Sep 17 00:00:00 2001 From: Redish101 Date: Fri, 20 Sep 2024 20:22:53 +0800 Subject: [PATCH] test: add test cases of monitor --- monitor/assertions.go | 68 ++++++++++++++ monitor/config_test.go | 162 +++++++++++++++++++++++++++++++++ monitor/monitor_test.go | 197 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 427 insertions(+) create mode 100644 monitor/assertions.go create mode 100644 monitor/config_test.go create mode 100644 monitor/monitor_test.go diff --git a/monitor/assertions.go b/monitor/assertions.go new file mode 100644 index 00000000..352da662 --- /dev/null +++ b/monitor/assertions.go @@ -0,0 +1,68 @@ +// ⚡️ Fiber is an Express inspired web framework written in Go with ☕️ +// 🤖 Github Repository: https://github.com/gofiber/fiber +// 📌 API Documentation: https://docs.gofiber.io + +package monitor + +import ( + "bytes" + "fmt" + "log" + "path/filepath" + "reflect" + "runtime" + "testing" + "text/tabwriter" +) + +// AssertEqual checks if values are equal +func AssertEqual(tb testing.TB, expected, actual interface{}, description ...string) { //nolint:thelper // TODO: Verify if tb can be nil + if tb != nil { + tb.Helper() + } + + if reflect.DeepEqual(expected, actual) { + return + } + + aType := "" + bType := "" + + if expected != nil { + aType = reflect.TypeOf(expected).String() + } + if actual != nil { + bType = reflect.TypeOf(actual).String() + } + + testName := "AssertEqual" + if tb != nil { + testName = tb.Name() + } + + _, file, line, _ := runtime.Caller(1) + + var buf bytes.Buffer + const pad = 5 + w := tabwriter.NewWriter(&buf, 0, 0, pad, ' ', 0) + _, _ = fmt.Fprintf(w, "\nTest:\t%s", testName) + _, _ = fmt.Fprintf(w, "\nTrace:\t%s:%d", filepath.Base(file), line) + if len(description) > 0 { + _, _ = fmt.Fprintf(w, "\nDescription:\t%s", description[0]) + } + _, _ = fmt.Fprintf(w, "\nExpect:\t%v\t(%s)", expected, aType) + _, _ = fmt.Fprintf(w, "\nResult:\t%v\t(%s)", actual, bType) + + var result string + if err := w.Flush(); err != nil { + result = err.Error() + } else { + result = buf.String() + } + + if tb != nil { + tb.Fatal(result) + } else { + log.Fatal(result) //nolint:revive // tb might be nil, so we need a fallback + } +} \ No newline at end of file diff --git a/monitor/config_test.go b/monitor/config_test.go new file mode 100644 index 00000000..cdf1275a --- /dev/null +++ b/monitor/config_test.go @@ -0,0 +1,162 @@ +package monitor + +import ( + "testing" + "time" + + "github.com/gofiber/fiber/v3" +) + +func Test_Config_Default(t *testing.T) { + t.Parallel() + + t.Run("use default", func(t *testing.T) { + t.Parallel() + cfg := configDefault() + + AssertEqual(t, defaultTitle, cfg.Title) + AssertEqual(t, defaultRefresh, cfg.Refresh) + AssertEqual(t, defaultFontURL, cfg.FontURL) + AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) + AssertEqual(t, defaultCustomHead, cfg.CustomHead) + AssertEqual(t, false, cfg.APIOnly) + AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) + AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJSURL, defaultCustomHead}), cfg.index) + }) + + t.Run("set title", func(t *testing.T) { + t.Parallel() + title := "title" + cfg := configDefault(Config{ + Title: title, + }) + + AssertEqual(t, title, cfg.Title) + AssertEqual(t, defaultRefresh, cfg.Refresh) + AssertEqual(t, defaultFontURL, cfg.FontURL) + AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) + AssertEqual(t, defaultCustomHead, cfg.CustomHead) + AssertEqual(t, false, cfg.APIOnly) + AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) + AssertEqual(t, newIndex(viewBag{title, defaultRefresh, defaultFontURL, defaultChartJSURL, defaultCustomHead}), cfg.index) + }) + + t.Run("set refresh less than default", func(t *testing.T) { + t.Parallel() + cfg := configDefault(Config{ + Refresh: 100 * time.Millisecond, + }) + + AssertEqual(t, defaultTitle, cfg.Title) + AssertEqual(t, minRefresh, cfg.Refresh) + AssertEqual(t, defaultFontURL, cfg.FontURL) + AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) + AssertEqual(t, defaultCustomHead, cfg.CustomHead) + AssertEqual(t, false, cfg.APIOnly) + AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) + AssertEqual(t, newIndex(viewBag{defaultTitle, minRefresh, defaultFontURL, defaultChartJSURL, defaultCustomHead}), cfg.index) + }) + + t.Run("set refresh", func(t *testing.T) { + t.Parallel() + refresh := time.Second + cfg := configDefault(Config{ + Refresh: refresh, + }) + + AssertEqual(t, defaultTitle, cfg.Title) + AssertEqual(t, refresh, cfg.Refresh) + AssertEqual(t, defaultFontURL, cfg.FontURL) + AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) + AssertEqual(t, defaultCustomHead, cfg.CustomHead) + AssertEqual(t, false, cfg.APIOnly) + AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) + AssertEqual(t, newIndex(viewBag{defaultTitle, refresh, defaultFontURL, defaultChartJSURL, defaultCustomHead}), cfg.index) + }) + + t.Run("set font url", func(t *testing.T) { + t.Parallel() + fontURL := "https://example.com" + cfg := configDefault(Config{ + FontURL: fontURL, + }) + + AssertEqual(t, defaultTitle, cfg.Title) + AssertEqual(t, defaultRefresh, cfg.Refresh) + AssertEqual(t, fontURL, cfg.FontURL) + AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) + AssertEqual(t, defaultCustomHead, cfg.CustomHead) + AssertEqual(t, false, cfg.APIOnly) + AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) + AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, fontURL, defaultChartJSURL, defaultCustomHead}), cfg.index) + }) + + t.Run("set chart js url", func(t *testing.T) { + t.Parallel() + chartURL := "http://example.com" + cfg := configDefault(Config{ + ChartJSURL: chartURL, + }) + + AssertEqual(t, defaultTitle, cfg.Title) + AssertEqual(t, defaultRefresh, cfg.Refresh) + AssertEqual(t, defaultFontURL, cfg.FontURL) + AssertEqual(t, chartURL, cfg.ChartJSURL) + AssertEqual(t, defaultCustomHead, cfg.CustomHead) + AssertEqual(t, false, cfg.APIOnly) + AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) + AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, chartURL, defaultCustomHead}), cfg.index) + }) + + t.Run("set custom head", func(t *testing.T) { + t.Parallel() + head := "head" + cfg := configDefault(Config{ + CustomHead: head, + }) + + AssertEqual(t, defaultTitle, cfg.Title) + AssertEqual(t, defaultRefresh, cfg.Refresh) + AssertEqual(t, defaultFontURL, cfg.FontURL) + AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) + AssertEqual(t, head, cfg.CustomHead) + AssertEqual(t, false, cfg.APIOnly) + AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) + AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJSURL, head}), cfg.index) + }) + + t.Run("set api only", func(t *testing.T) { + t.Parallel() + cfg := configDefault(Config{ + APIOnly: true, + }) + + AssertEqual(t, defaultTitle, cfg.Title) + AssertEqual(t, defaultRefresh, cfg.Refresh) + AssertEqual(t, defaultFontURL, cfg.FontURL) + AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) + AssertEqual(t, defaultCustomHead, cfg.CustomHead) + AssertEqual(t, true, cfg.APIOnly) + AssertEqual(t, (func(*fiber.Ctx) bool)(nil), cfg.Next) + AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJSURL, defaultCustomHead}), cfg.index) + }) + + t.Run("set next", func(t *testing.T) { + t.Parallel() + f := func(c *fiber.Ctx) bool { + return true + } + cfg := configDefault(Config{ + Next: f, + }) + + AssertEqual(t, defaultTitle, cfg.Title) + AssertEqual(t, defaultRefresh, cfg.Refresh) + AssertEqual(t, defaultFontURL, cfg.FontURL) + AssertEqual(t, defaultChartJSURL, cfg.ChartJSURL) + AssertEqual(t, defaultCustomHead, cfg.CustomHead) + AssertEqual(t, false, cfg.APIOnly) + AssertEqual(t, f(nil), cfg.Next(nil)) + AssertEqual(t, newIndex(viewBag{defaultTitle, defaultRefresh, defaultFontURL, defaultChartJSURL, defaultCustomHead}), cfg.index) + }) +} \ No newline at end of file diff --git a/monitor/monitor_test.go b/monitor/monitor_test.go new file mode 100644 index 00000000..c3414e6a --- /dev/null +++ b/monitor/monitor_test.go @@ -0,0 +1,197 @@ +package monitor + +import ( + "bytes" + "fmt" + "io" + "net/http/httptest" + "testing" + "time" + + "github.com/gofiber/fiber/v3" + + "github.com/valyala/fasthttp" +) + +func Test_Monitor_405(t *testing.T) { + t.Parallel() + + app := fiber.New() + + app.Use("/", New()) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodPost, "/", nil)) + AssertEqual(t, nil, err) + AssertEqual(t, 405, resp.StatusCode) +} + +func Test_Monitor_Html(t *testing.T) { + t.Parallel() + + app := fiber.New() + + // defaults + app.Get("/", New()) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) + + AssertEqual(t, nil, err) + AssertEqual(t, 200, resp.StatusCode) + AssertEqual(t, fiber.MIMETextHTMLCharsetUTF8, + resp.Header.Get(fiber.HeaderContentType)) + buf, err := io.ReadAll(resp.Body) + AssertEqual(t, nil, err) + AssertEqual(t, true, bytes.Contains(buf, []byte(""+defaultTitle+""))) + timeoutLine := fmt.Sprintf("setTimeout(fetchJSON, %d)", + defaultRefresh.Milliseconds()-timeoutDiff) + AssertEqual(t, true, bytes.Contains(buf, []byte(timeoutLine))) + + // custom config + conf := Config{Title: "New " + defaultTitle, Refresh: defaultRefresh + time.Second} + app.Get("/custom", New(conf)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/custom", nil)) + + AssertEqual(t, nil, err) + AssertEqual(t, 200, resp.StatusCode) + AssertEqual(t, fiber.MIMETextHTMLCharsetUTF8, + resp.Header.Get(fiber.HeaderContentType)) + buf, err = io.ReadAll(resp.Body) + AssertEqual(t, nil, err) + AssertEqual(t, true, bytes.Contains(buf, []byte(""+conf.Title+""))) + timeoutLine = fmt.Sprintf("setTimeout(fetchJSON, %d)", + conf.Refresh.Milliseconds()-timeoutDiff) + AssertEqual(t, true, bytes.Contains(buf, []byte(timeoutLine))) +} + +func Test_Monitor_Html_CustomCodes(t *testing.T) { + t.Parallel() + + app := fiber.New() + + // defaults + app.Get("/", New()) + resp, err := app.Test(httptest.NewRequest(fiber.MethodGet, "/", nil)) + + AssertEqual(t, nil, err) + AssertEqual(t, 200, resp.StatusCode) + AssertEqual(t, fiber.MIMETextHTMLCharsetUTF8, + resp.Header.Get(fiber.HeaderContentType)) + buf, err := io.ReadAll(resp.Body) + AssertEqual(t, nil, err) + AssertEqual(t, true, bytes.Contains(buf, []byte(""+defaultTitle+""))) + timeoutLine := fmt.Sprintf("setTimeout(fetchJSON, %d)", + defaultRefresh.Milliseconds()-timeoutDiff) + AssertEqual(t, true, bytes.Contains(buf, []byte(timeoutLine))) + + // custom config + conf := Config{ + Title: "New " + defaultTitle, + Refresh: defaultRefresh + time.Second, + ChartJSURL: "https://cdnjs.com/libraries/Chart.js", + FontURL: "/public/my-font.css", + CustomHead: ``, + } + app.Get("/custom", New(conf)) + resp, err = app.Test(httptest.NewRequest(fiber.MethodGet, "/custom", nil)) + + AssertEqual(t, nil, err) + AssertEqual(t, 200, resp.StatusCode) + AssertEqual(t, fiber.MIMETextHTMLCharsetUTF8, + resp.Header.Get(fiber.HeaderContentType)) + buf, err = io.ReadAll(resp.Body) + AssertEqual(t, nil, err) + AssertEqual(t, true, bytes.Contains(buf, []byte(""+conf.Title+""))) + AssertEqual(t, true, bytes.Contains(buf, []byte("https://cdnjs.com/libraries/Chart.js"))) + AssertEqual(t, true, bytes.Contains(buf, []byte("/public/my-font.css"))) + AssertEqual(t, true, bytes.Contains(buf, []byte(conf.CustomHead))) + + timeoutLine = fmt.Sprintf("setTimeout(fetchJSON, %d)", + conf.Refresh.Milliseconds()-timeoutDiff) + AssertEqual(t, true, bytes.Contains(buf, []byte(timeoutLine))) +} + +// go test -run Test_Monitor_JSON -race +func Test_Monitor_JSON(t *testing.T) { + t.Parallel() + + app := fiber.New() + + app.Get("/", New()) + + req := httptest.NewRequest(fiber.MethodGet, "/", nil) + req.Header.Set(fiber.HeaderAccept, fiber.MIMEApplicationJSON) + resp, err := app.Test(req) + AssertEqual(t, nil, err) + AssertEqual(t, 200, resp.StatusCode) + AssertEqual(t, fiber.MIMEApplicationJSON, resp.Header.Get(fiber.HeaderContentType)) + + b, err := io.ReadAll(resp.Body) + AssertEqual(t, nil, err) + AssertEqual(t, true, bytes.Contains(b, []byte("pid"))) + AssertEqual(t, true, bytes.Contains(b, []byte("os"))) +} + +// go test -v -run=^$ -bench=Benchmark_Monitor -benchmem -count=4 +func Benchmark_Monitor(b *testing.B) { + app := fiber.New() + + app.Get("/", New()) + + h := app.Handler() + + fctx := &fasthttp.RequestCtx{} + fctx.Request.Header.SetMethod(fiber.MethodGet) + fctx.Request.SetRequestURI("/") + fctx.Request.Header.Set(fiber.HeaderAccept, fiber.MIMEApplicationJSON) + + b.ReportAllocs() + b.ResetTimer() + + b.RunParallel(func(pb *testing.PB) { + for pb.Next() { + h(fctx) + } + }) + + AssertEqual(b, 200, fctx.Response.Header.StatusCode()) + AssertEqual(b, + fiber.MIMEApplicationJSON, + string(fctx.Response.Header.Peek(fiber.HeaderContentType))) +} + +// go test -run Test_Monitor_Next +func Test_Monitor_Next(t *testing.T) { + t.Parallel() + + app := fiber.New() + + app.Use("/", New(Config{ + Next: func(_ *fiber.Ctx) bool { + return true + }, + })) + + resp, err := app.Test(httptest.NewRequest(fiber.MethodPost, "/", nil)) + AssertEqual(t, nil, err) + AssertEqual(t, 404, resp.StatusCode) +} + +// go test -run Test_Monitor_APIOnly -race +func Test_Monitor_APIOnly(t *testing.T) { + app := fiber.New() + + app.Get("/", New(Config{ + APIOnly: true, + })) + + req := httptest.NewRequest(fiber.MethodGet, "/", nil) + req.Header.Set(fiber.HeaderAccept, fiber.MIMEApplicationJSON) + resp, err := app.Test(req) + AssertEqual(t, nil, err) + AssertEqual(t, 200, resp.StatusCode) + AssertEqual(t, fiber.MIMEApplicationJSON, resp.Header.Get(fiber.HeaderContentType)) + + b, err := io.ReadAll(resp.Body) + AssertEqual(t, nil, err) + AssertEqual(t, true, bytes.Contains(b, []byte("pid"))) + AssertEqual(t, true, bytes.Contains(b, []byte("os"))) +} \ No newline at end of file