150 lines
4.9 KiB
Go
150 lines
4.9 KiB
Go
package main_test
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"sync"
|
|
"testing"
|
|
|
|
"example.com/errorCatalog/internal/apierrors"
|
|
"example.com/errorCatalog/internal/handler"
|
|
"example.com/errorCatalog/internal/svc"
|
|
omni "git.yuntuo.hk/k2safe/go-omni"
|
|
omnierrors "git.yuntuo.hk/k2safe/go-omni/errors"
|
|
"git.yuntuo.hk/k2safe/go-omni/httpx"
|
|
"git.yuntuo.hk/k2safe/go-omni/logx"
|
|
)
|
|
|
|
func TestGeneratedTemplateErrors(t *testing.T) {
|
|
var document map[string]any
|
|
if err := json.Unmarshal([]byte(handler.OpenAPIDocument), &document); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
operation := document["paths"].(map[string]any)["/token"].(map[string]any)["post"].(map[string]any)
|
|
media := operation["responses"].(map[string]any)["200"].(map[string]any)["content"].(map[string]any)["application/json"].(map[string]any)
|
|
example := media["examples"].(map[string]any)["TextTooLong"].(map[string]any)["value"].(map[string]any)
|
|
if example["msg"] != "企业名称不能超过100个字符" || example["code"] != float64(400002) {
|
|
t.Fatal(example)
|
|
}
|
|
if len(media["schema"].(map[string]any)["anyOf"].([]any)) != 2 {
|
|
t.Fatal("missing success/failure schema")
|
|
}
|
|
found := false
|
|
for _, raw := range document["x-omni-errors"].([]any) {
|
|
entry := raw.(map[string]any)
|
|
if entry["name"] == "TextTooLong" {
|
|
found = true
|
|
params := entry["params"].([]any)
|
|
if len(params) != 2 || params[0].(map[string]any)["type"] != "string" || params[1].(map[string]any)["type"] != "int" {
|
|
t.Fatal(params)
|
|
}
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatal("missing template metadata")
|
|
}
|
|
// Assigning to a typed function also verifies declaration order and Go types.
|
|
var typed func(string, int) *omnierrors.APIError = apierrors.TextTooLong
|
|
private := errors.New("private cause")
|
|
first := typed("企业名称", 100).WithCause(private)
|
|
second := typed("备注", 500)
|
|
if first.Message != "企业名称不能超过100个字符" || second.Message != "备注不能超过500个字符" || second.Cause != nil || !errors.Is(first, private) {
|
|
t.Fatal(first, second)
|
|
}
|
|
if apierrors.UsernameInvalid().Message != "用户名格式不正确" {
|
|
t.Fatal("fixed compatibility")
|
|
}
|
|
cat := apierrors.Catalog()
|
|
cat.Definitions["TEXT_TOO_LONG"].Params[0].Type = "bool"
|
|
if typed("企业名称", 100).Message != first.Message {
|
|
t.Fatal("mutable catalog")
|
|
}
|
|
var wg sync.WaitGroup
|
|
for n := 0; n < 40; n++ {
|
|
wg.Add(1)
|
|
go func(n int) {
|
|
defer wg.Done()
|
|
if n%2 == 0 {
|
|
if typed("企业名称", 100).Message != first.Message {
|
|
t.Error("isolation")
|
|
}
|
|
} else {
|
|
if typed("备注", 500).Message != second.Message {
|
|
t.Error("isolation")
|
|
}
|
|
}
|
|
}(n)
|
|
}
|
|
wg.Wait()
|
|
cfg := logx.Default()
|
|
cfg.Writer = io.Discard
|
|
logger, err := logx.New(cfg)
|
|
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()
|
|
app := omni.New()
|
|
defer app.Close()
|
|
app.Use(func(next httpx.Handler) httpx.Handler {
|
|
return func(c *httpx.Context) error {
|
|
switch c.Header("X-Fixture") {
|
|
case "company":
|
|
return typed("企业名称", 100).WithCause(private)
|
|
case "remark":
|
|
return omnierrors.ByCode("TEXT_TOO_LONG").WithParams(map[string]any{"label": "备注", "max": 500})
|
|
case "missing":
|
|
return omnierrors.ByCode("TEXT_TOO_LONG").WithParams(map[string]any{"label": "企业名称"})
|
|
case "type":
|
|
return omnierrors.ByCode("TEXT_TOO_LONG").WithParams(map[string]any{"label": "企业名称", "max": "100"})
|
|
case "fixed":
|
|
return apierrors.UsernameInvalid()
|
|
case "denied":
|
|
return apierrors.ResourceDenied("资源")
|
|
default:
|
|
return c.OK(map[string]string{"token": "ok"})
|
|
}
|
|
}
|
|
})
|
|
if err := handler.RegisterRoutes(app, deps); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, path := range []string{"/token", "/raw"} {
|
|
for _, test := range []struct {
|
|
kind string
|
|
status int
|
|
want string
|
|
}{
|
|
{"company", 200, first.Message}, {"remark", 200, second.Message}, {"missing", 500, "internal server error"}, {"type", 500, "internal server error"}, {"fixed", 200, "用户名格式不正确"}, {"denied", 403, "资源不可用"}, {"", 200, `"token":"ok"`},
|
|
} {
|
|
method := "POST"
|
|
if path == "/raw" {
|
|
method = "GET"
|
|
}
|
|
w := httptest.NewRecorder()
|
|
r := httptest.NewRequest(method, path, nil)
|
|
r.Header.Set("X-Fixture", test.kind)
|
|
app.ServeHTTP(w, r)
|
|
if w.Code != test.status || !strings.Contains(w.Body.String(), test.want) || strings.Contains(w.Body.String(), "private") || strings.Contains(w.Body.String(), "{label}") {
|
|
t.Fatal(test.kind, w.Code, w.Body.String())
|
|
}
|
|
if test.kind == "company" || test.kind == "remark" {
|
|
if !strings.Contains(w.Body.String(), `"code":400002`) || !strings.Contains(w.Body.String(), `"data":null`) {
|
|
t.Fatal(w.Body.String())
|
|
}
|
|
}
|
|
if path == "/raw" && test.kind == "" && strings.Contains(w.Body.String(), "code") {
|
|
t.Fatal("raw response wrapped")
|
|
}
|
|
}
|
|
}
|
|
}
|