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