Files
model-gateway/service/parse_error_test.go
T
19904408334 923644db2b refactor: 模型错误解析改为 ErrorMessageMapping 配置驱动
- parseModelError 按模型配置的 ErrorMessageMapping(schema 树)解析错误响应
- 成功判定: code 提取为空 / code 节点配置 defaultValue 且提取值等于它 → 成功
- 未配置 mapping 不识别错误(纯配置驱动), 删硬编码 ModelErrorResp/ModelError1Resp
- DTO 加 errorMessageMapping 字段, update.sql 建 error_message_mapping JSONB 列
- 单测: parse_error_test.go 覆盖扁平/嵌套/数组/纯字符串路径/未配置/坏 JSON
2026-08-29 17:52:23 +08:00

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)
}
}