package middleware_test import ( "bytes" "encoding/json" "errors" "net/http/httptest" "testing" apierrors "git.yuntuo.hk/k2safe/go-omni/errors" "git.yuntuo.hk/k2safe/go-omni/httpx" "git.yuntuo.hk/k2safe/go-omni/logx" "git.yuntuo.hk/k2safe/go-omni/middleware" ) func TestAccessLogReportsCommittedStatus(t *testing.T) { for _, tc := range []struct { name string handler httpx.Handler status int }{ {"unwritten error", func(c *httpx.Context) error { return apierrors.NotFound("MISSING", "missing") }, 404}, {"encoding error", func(c *httpx.Context) error { return c.OK(make(chan int)) }, 500}, {"committed error", func(c *httpx.Context) error { _ = c.Created("saved"); return errors.New("later error") }, 201}, {"committed panic", func(c *httpx.Context) error { _ = c.Accepted("saved"); panic("later panic") }, 202}, } { t.Run(tc.name, func(t *testing.T) { var output bytes.Buffer logger, err := logx.New(logx.Config{Format: "json", Writer: &output}) if err != nil { t.Fatal(err) } logx.SetDefault(logger) defer logx.SetDefault(nil) req := httptest.NewRequest("GET", "/users/42", nil) req.Header.Set("X-Request-ID", "logRequest") req = req.WithContext(httpx.WithRoutePattern(req.Context(), "/users/:id")) w := httptest.NewRecorder() ctx := httpx.NewContext(req, w, nil) chain := middleware.RequestID("X-Request-ID")(middleware.AccessLog()(middleware.Recovery()(tc.handler))) if err := chain(ctx); err != nil { ctx.WriteError(err) } var entry struct { Status int Route string RequestID string `json:"requestId"` } if err := json.Unmarshal(output.Bytes(), &entry); err != nil { t.Fatal(err) } if entry.Status != tc.status || w.Code != tc.status || entry.Route != "/users/:id" || entry.RequestID != "logRequest" { t.Fatalf("log=%s response=%d; expected status %d and request metadata", &output, w.Code, tc.status) } }) } } func TestAccessLogUsesVerifiedClientIP(t *testing.T) { loggerOutput := bytes.Buffer{} logger, err := logx.New(logx.Config{Format: "json", Writer: &loggerOutput}) if err != nil { t.Fatal(err) } logx.SetDefault(logger) defer logx.SetDefault(nil) resolver, err := httpx.NewClientIPResolver("127.0.0.1") if err != nil { t.Fatal(err) } request := httptest.NewRequest("GET", "/health", nil) request.RemoteAddr = "127.0.0.1:443" request.Header.Set("X-Forwarded-For", "198.51.100.7") request = request.WithContext(httpx.WithClientIPResolver(request.Context(), resolver)) ctx := httpx.NewContext(request, httptest.NewRecorder(), nil) if err := middleware.AccessLog()(func(c *httpx.Context) error { return c.OK("ok") })(ctx); err != nil { t.Fatal(err) } var entry struct { ClientIP string `json:"clientIp"` } if err := json.Unmarshal(loggerOutput.Bytes(), &entry); err != nil { t.Fatal(err) } if entry.ClientIP != "198.51.100.7" { t.Fatalf("expected verified client IP, got %q", entry.ClientIP) } }