fix: inherit App error catalogs on generated routes without local errors
This commit is contained in:
126
app/errorInheritance_test.go
Normal file
126
app/errorInheritance_test.go
Normal file
@@ -0,0 +1,126 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user