- parseModelError 按模型配置的 ErrorMessageMapping(schema 树)解析错误响应 - 成功判定: code 提取为空 / code 节点配置 defaultValue 且提取值等于它 → 成功 - 未配置 mapping 不识别错误(纯配置驱动), 删硬编码 ModelErrorResp/ModelError1Resp - DTO 加 errorMessageMapping 字段, update.sql 建 error_message_mapping JSONB 列 - 单测: parse_error_test.go 覆盖扁平/嵌套/数组/纯字符串路径/未配置/坏 JSON
147 lines
5.6 KiB
Go
147 lines
5.6 KiB
Go
package service
|
|
|
|
import "testing"
|
|
|
|
// flatMapping 扁平形态:code 配置 defaultValue=20000000 为成功码
|
|
func flatMapping() map[string]any {
|
|
return map[string]any{
|
|
"code": map[string]any{
|
|
"type": "number", "value": 0, "label": "", "fieldType": "number", "isForm": false, "required": false,
|
|
"defaultValue": float64(20000000),
|
|
},
|
|
"message": map[string]any{
|
|
"type": "string", "value": "", "label": "", "fieldType": "string", "isForm": false, "required": false,
|
|
},
|
|
}
|
|
}
|
|
|
|
func TestParseModelErrorFlatDefaultValue(t *testing.T) {
|
|
mapping := flatMapping()
|
|
// 成功:code == defaultValue
|
|
code, msg, err := parseModelError([]byte(`{"code":20000000,"message":"ok"}`), mapping)
|
|
if err != nil || code != "" || msg != "" {
|
|
t.Fatalf("success(default): code=%q msg=%q err=%v", code, msg, err)
|
|
}
|
|
// 成功:code 数字零值(视为空)
|
|
code, msg, err = parseModelError([]byte(`{"code":0}`), mapping)
|
|
if err != nil || code != "" || msg != "" {
|
|
t.Fatalf("success(zero): code=%q msg=%q err=%v", code, msg, err)
|
|
}
|
|
// 错误:code != defaultValue
|
|
code, msg, err = parseModelError([]byte(`{"code":1234,"message":"boom"}`), mapping)
|
|
if err != nil || code != "1234" || msg != "boom" {
|
|
t.Fatalf("error: code=%q msg=%q err=%v", code, msg, err)
|
|
}
|
|
}
|
|
|
|
func TestParseModelErrorNested(t *testing.T) {
|
|
mapping := map[string]any{
|
|
"error": map[string]any{
|
|
"type": "object",
|
|
"attrs": map[string]any{
|
|
"code": map[string]any{"type": "string", "label": "", "value": "", "fieldType": "string", "isForm": false, "required": false},
|
|
"message": map[string]any{"type": "string", "label": "", "value": "", "fieldType": "string", "isForm": false, "required": false},
|
|
},
|
|
"label": "", "isForm": false, "required": false, "fieldType": "string",
|
|
},
|
|
}
|
|
// 错误响应
|
|
code, msg, err := parseModelError([]byte(`{"error":{"code":"rate_limit_exceeded","message":"Too fast"}}`), mapping)
|
|
if err != nil || code != "rate_limit_exceeded" || msg != "Too fast" {
|
|
t.Fatalf("error: code=%q msg=%q err=%v", code, msg, err)
|
|
}
|
|
// 成功响应(无 error 字段 → code 提取为空)
|
|
code, msg, err = parseModelError([]byte(`{"id":"x","choices":[]}`), mapping)
|
|
if err != nil || code != "" || msg != "" {
|
|
t.Fatalf("success: code=%q msg=%q err=%v", code, msg, err)
|
|
}
|
|
}
|
|
|
|
func TestParseModelErrorArray(t *testing.T) {
|
|
mapping := map[string]any{
|
|
"errors": map[string]any{
|
|
"type": "array",
|
|
"attrs": []any{map[string]any{
|
|
"code": map[string]any{"type": "number"},
|
|
"message": map[string]any{"type": "string"},
|
|
}},
|
|
},
|
|
}
|
|
code, msg, err := parseModelError([]byte(`{"errors":[{"code":1001,"message":"err-a"}]}`), mapping)
|
|
if err != nil || code != "1001" || msg != "err-a" {
|
|
t.Fatalf("array error: code=%q msg=%q err=%v", code, msg, err)
|
|
}
|
|
code, msg, err = parseModelError([]byte(`{"errors":[]}`), mapping)
|
|
if err != nil || code != "" || msg != "" {
|
|
t.Fatalf("array success: code=%q msg=%q err=%v", code, msg, err)
|
|
}
|
|
}
|
|
|
|
func TestParseModelErrorPlainStringMapping(t *testing.T) {
|
|
// 纯字符串路径形态:字段值直接是字段路径
|
|
mapping := map[string]any{"code": "error.code", "message": "error.message"}
|
|
code, msg, err := parseModelError([]byte(`{"error":{"code":100,"message":"nope"}}`), mapping)
|
|
if err != nil || code != "100" || msg != "nope" {
|
|
t.Fatalf("plain mapping: code=%q msg=%q err=%v", code, msg, err)
|
|
}
|
|
code, msg, err = parseModelError([]byte(`{"data":"ok"}`), mapping)
|
|
if err != nil || code != "" || msg != "" {
|
|
t.Fatalf("plain success: code=%q msg=%q err=%v", code, msg, err)
|
|
}
|
|
}
|
|
|
|
func TestParseModelErrorNoMapping(t *testing.T) {
|
|
// 未配置 mapping:不识别错误,一律成功(纯配置驱动)
|
|
code, msg, err := parseModelError([]byte(`{"error":{"code":"x"}}`), nil)
|
|
if err != nil || code != "" || msg != "" {
|
|
t.Fatalf("no mapping: code=%q msg=%q err=%v", code, msg, err)
|
|
}
|
|
}
|
|
|
|
func TestParseModelErrorBadJSON(t *testing.T) {
|
|
mapping := map[string]any{"code": map[string]any{"type": "string"}}
|
|
if _, _, err := parseModelError([]byte(`{invalid`), mapping); err == nil {
|
|
t.Fatalf("bad json should err")
|
|
}
|
|
}
|
|
|
|
func TestParseModelErrorOnlyMessage(t *testing.T) {
|
|
// 只配置 message:以 message 是否为空判定错误
|
|
mapping := map[string]any{"message": map[string]any{"type": "string"}}
|
|
code, msg, err := parseModelError([]byte(`{"message":"something went wrong"}`), mapping)
|
|
if err != nil || code != "" || msg != "something went wrong" {
|
|
t.Fatalf("only-message error: code=%q msg=%q err=%v", code, msg, err)
|
|
}
|
|
code, msg, err = parseModelError([]byte(`{"data":"ok"}`), mapping)
|
|
if err != nil || code != "" || msg != "" {
|
|
t.Fatalf("only-message success: code=%q msg=%q err=%v", code, msg, err)
|
|
}
|
|
}
|
|
|
|
func TestCollectErrorMapping(t *testing.T) {
|
|
// 扁平 + defaultValue
|
|
codePath, msgPath, hasDef, def := collectErrorMapping(flatMapping())
|
|
if codePath != "code" || msgPath != "message" || !hasDef || def != float64(20000000) {
|
|
t.Fatalf("flat: code=%q msg=%q hasDef=%v def=%v", codePath, msgPath, hasDef, def)
|
|
}
|
|
// 嵌套
|
|
codePath, msgPath, hasDef, def = collectErrorMapping(map[string]any{
|
|
"error": map[string]any{"type": "object", "attrs": map[string]any{
|
|
"code": map[string]any{"type": "string"},
|
|
"message": map[string]any{"type": "string"},
|
|
}},
|
|
})
|
|
if codePath != "error.code" || msgPath != "error.message" || hasDef {
|
|
t.Fatalf("nested: code=%q msg=%q hasDef=%v", codePath, msgPath, hasDef)
|
|
}
|
|
// 数组 → [*]
|
|
codePath, _, _, _ = collectErrorMapping(map[string]any{
|
|
"errors": map[string]any{"type": "array", "attrs": []any{
|
|
map[string]any{"code": map[string]any{"type": "number"}},
|
|
}},
|
|
})
|
|
if codePath != "errors[*].code" {
|
|
t.Fatalf("array: code=%q", codePath)
|
|
}
|
|
}
|