package app_test import ( "encoding/json" "net/http" "net/http/httptest" "testing" omni "git.yuntuo.hk/k2safe/go-omni" apierrors "git.yuntuo.hk/k2safe/go-omni/errors" "git.yuntuo.hk/k2safe/go-omni/httpx" "git.yuntuo.hk/k2safe/go-omni/response" ) func TestRouteInheritsAppErrorCatalog(t *testing.T) { catalog := apierrors.Catalog{DefaultStatus: 400, Definitions: map[string]apierrors.Definition{ "USERNAME_INVALID": {Code: 400002, Message: "用户名格式不正确"}, "TEXT_TOO_LONG": {Status: 200, Code: 400003, Message: "{label}不能超过{max}个字符", Params: []apierrors.Parameter{{Name: "label", Type: "string"}, {Name: "max", Type: "int"}}}, "INTERNAL_ERROR": {Status: 500, Code: 9999, Message: "系统错误"}, }} format := response.Config{CodeType: "int32", SuccessCode: 0, ErrorCode: 9999, MessageField: "msg", DataMode: "always"} app := omni.New(omni.WithErrors(catalog), omni.WithResponse(format)) defer app.Close() getError := func(c *httpx.Context) error { key := c.Header("X-Error") if key == "TEXT_TOO_LONG" { return apierrors.ByCode(key).WithParams(map[string]any{"label": "企业名称", "max": 100}) } return apierrors.ByCode(key) } app.Use(func(next httpx.Handler) httpx.Handler { return func(c *httpx.Context) error { if c.Header("X-Early") == "true" { return getError(c) } return next(c) } }) bridge := httpx.WrapMiddleware(func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { next.ServeHTTP(w, r) }) }) app.GET("/plain", getError) // This is exactly the wrapper emitted by v0.11.0, not just the new generator. legacy := response.MustWithErrors(apierrors.Catalog{}, response.MustNew(format)) app.HandleWithResponse("GET", "/generated", legacy, getError, bridge) app.HandleWithResponse("GET", "/raw", response.RawJSON(legacy), getError, bridge) for _, path := range []string{"/plain", "/generated", "/raw"} { for _, early := range []string{"false", "true"} { for _, test := range []struct { key string status, code int message string }{ {"USERNAME_INVALID", 400, 400002, "用户名格式不正确"}, {"TEXT_TOO_LONG", 200, 400003, "企业名称不能超过100个字符"}, {"MISSING", 500, 9999, "系统错误"}, } { t.Run(path+"/"+early+"/"+test.key, func(t *testing.T) { r := httptest.NewRequest("GET", path, nil) r.Header.Set("X-Early", early) r.Header.Set("X-Error", test.key) w := httptest.NewRecorder() app.ServeHTTP(w, r) var value map[string]any if err := json.Unmarshal(w.Body.Bytes(), &value); err != nil { t.Fatal(err, w.Body.String()) } if w.Code != test.status || value["code"] != float64(test.code) || value["msg"] != test.message { t.Fatal(w.Code, value) } if data, ok := value["data"]; !ok || data != nil { t.Fatal("data:null lost", value) } }) } } } } func TestExplicitRouteErrorCatalogRemainsIndependent(t *testing.T) { format := response.Config{CodeType: "int32", SuccessCode: 0, ErrorCode: 9999, MessageField: "msg", DataMode: "always"} global := apierrors.Catalog{DefaultStatus: 400, Definitions: map[string]apierrors.Definition{ "INVALID": {Code: 400002, Message: "全局提示"}, "APP_ONLY": {Code: 400003, Message: "全局独有"}, "INTERNAL_ERROR": {Status: 500, Code: 9999, Message: "全局系统错误"}, }} local := apierrors.Catalog{DefaultStatus: 409, Definitions: map[string]apierrors.Definition{ "INVALID": {Code: 409001, Message: "路由提示"}, "INTERNAL_ERROR": {Status: 500, Code: 9998, Message: "路由系统错误"}, }} app := omni.New(omni.WithErrors(global), omni.WithResponse(format)) defer app.Close() getError := func(c *httpx.Context) error { return apierrors.ByCode(c.Header("X-Error")) } app.Use(func(next httpx.Handler) httpx.Handler { return func(c *httpx.Context) error { if c.Header("X-Early") == "true" { return getError(c) } return next(c) } }) routeFormat := format routeFormat.MessageField = "detail" localEncoder := response.MustWithErrors(local, response.MustNew(routeFormat)) app.HandleWithResponse("GET", "/", response.RawJSON(localEncoder), getError) for _, early := range []string{"false", "true"} { for _, test := range []struct { key string status, code int message string }{ {"INVALID", 409, 409001, "路由提示"}, {"APP_ONLY", 500, 9998, "路由系统错误"}, } { r := httptest.NewRequest("GET", "/", nil) r.Header.Set("X-Early", early) r.Header.Set("X-Error", test.key) w := httptest.NewRecorder() app.ServeHTTP(w, r) var value map[string]any if err := json.Unmarshal(w.Body.Bytes(), &value); err != nil { t.Fatal(err) } if w.Code != test.status || value["code"] != float64(test.code) || value["detail"] != test.message || value["msg"] != nil { t.Fatal(w.Code, value) } } } }