package main_test import ( "context" "encoding/json" "io" "net/http/httptest" "strings" "testing" "example.com/errorCatalog/internal/handler" "example.com/errorCatalog/internal/svc" 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/logx" "git.yuntuo.hk/k2safe/go-omni/response" ) func TestGoOwnedCatalogOnGeneratedRoutes(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: "系统错误"}, "BODY_TOO_LARGE": {Status: 413, Code: 413001, Message: "请求体超限"}, }} lc := logx.Default() lc.Writer = io.Discard logger, err := logx.New(lc) if err != nil { t.Fatal(err) } defer logger.Close() deps, err := svc.New(context.Background(), logger) if err != nil { t.Fatal(err) } defer deps.Close() format := response.Config{CodeType: "int32", SuccessCode: 0, ErrorCode: 9999, MessageField: "msg", DataMode: "always", RequestIDField: "-"} app := omni.New(omni.WithErrors(catalog), omni.WithResponse(format)) defer app.Close() resolve := func(c *httpx.Context) error { switch c.Request().URL.Query().Get("kind") { case "long": return apierrors.ByCode("TEXT_TOO_LONG").WithParams(map[string]any{"label": "企业名称", "max": 100}) case "unknown": return apierrors.ByCode("NOT_REGISTERED") default: return apierrors.ByCode("USERNAME_INVALID") } } app.Use(func(next httpx.Handler) httpx.Handler { return func(c *httpx.Context) error { if c.Header("X-Early") == "true" { return resolve(c) } return next(c) } }) app.GET("/ordinary", resolve) if err := handler.RegisterRoutes(app, deps); err != nil { t.Fatal(err) } // Explicit route policy stays independent of App errors and response fields. local := apierrors.Catalog{Definitions: map[string]apierrors.Definition{"USERNAME_INVALID": {Status: 409, Code: 409001, Message: "路由提示"}}} localFormat := format localFormat.MessageField = "detail" app.HandleWithResponse("GET", "/independent", response.MustWithErrors(local, response.MustNew(localFormat)), resolve) for _, early := range []string{"false", "true"} { for _, test := range []struct { kind string status, code int message string }{ {"invalid", 400, 400002, "用户名格式不正确"}, {"long", 200, 400003, "企业名称不能超过100个字符"}, {"unknown", 500, 9999, "系统错误"}, } { var ordinary string for _, path := range []string{"/ordinary", "/plain", "/raw", "/cleared"} { r := httptest.NewRequest("GET", path+"?kind="+test.kind, nil) r.Header.Set("X-Early", early) w := httptest.NewRecorder() app.ServeHTTP(w, r) var body map[string]any if err := json.Unmarshal(w.Body.Bytes(), &body); err != nil { t.Fatal(err, w.Body.String()) } if w.Code != test.status || body["code"] != float64(test.code) || body["msg"] != test.message { t.Fatal(path, early, w.Code, body) } if value, ok := body["data"]; !ok || value != nil { t.Fatal("data:null missing", body) } if path == "/ordinary" { ordinary = w.Body.String() } else if w.Body.String() != ordinary { t.Fatal("generated response differs", ordinary, w.Body.String()) } } } r := httptest.NewRequest("GET", "/independent", nil) r.Header.Set("X-Early", early) w := httptest.NewRecorder() app.ServeHTTP(w, r) if w.Code != 409 || !strings.Contains(w.Body.String(), `"detail":"路由提示"`) || !strings.Contains(w.Body.String(), `"code":409001`) { t.Fatal(w.Code, w.Body.String()) } } for _, path := range []string{"/plain", "/raw"} { w := httptest.NewRecorder() app.ServeHTTP(w, httptest.NewRequest("GET", path+"?kind=success", nil)) if w.Code != 200 || !strings.Contains(w.Body.String(), `"ok":true`) { t.Fatal(w.Code, w.Body.String()) } if path == "/raw" && strings.Contains(w.Body.String(), "code") { t.Fatal("raw success wrapped") } } for _, size := range []int{8, 65} { w := httptest.NewRecorder() app.ServeHTTP(w, httptest.NewRequest("POST", "/bounded", strings.NewReader(strings.Repeat("x", size)))) if size == 8 { if w.Code != 200 || w.Body.String() != "ACK" { t.Fatal(w.Code, w.Body.String()) } } else if w.Code != 413 || !strings.Contains(w.Body.String(), "请求体超限") { t.Fatal(w.Code, w.Body.String()) } } }