package cast_test import ( "reflect" "testing" "apigo.cc/go/cast" ) // Matrix Test Entry func TestConvertMatrix(t *testing.T) { type testCase struct { name string from any to any // 传入指针的指针,用于接收结果 expected any } // 1. 基础类型全互转矩阵 cases := []testCase{ {name: "int to string", from: 123, to: new(string), expected: "123"}, {name: "string to int", from: "456", to: new(int), expected: 456}, {name: "float to int", from: 123.45, to: new(int), expected: 123}, {name: "bool to string", from: true, to: new(string), expected: "true"}, {name: "string to bool", from: "1", to: new(bool), expected: true}, {name: "string to bool (text)", from: "true", to: new(bool), expected: true}, // 2. 容器与单值的自动包装/解包 (去摩擦) {name: "single to slice", from: 100, to: new([]int), expected: []int{100}}, {name: "slice to single (len 1)", from: []int{200}, to: new(int), expected: 200}, {name: "slice to single (len >1, take first)", from: []int{300, 400}, to: new(int), expected: 300}, // 3. 字符串与切片的智能转换 {name: "csv string to slice", from: "1, 2, 3", to: new([]int), expected: []int{1, 2, 3}}, {name: "json string to slice", from: `[4, 5, 6]`, to: new([]int), expected: []int{4, 5, 6}}, // 4. 指针转换 (深度穿透) {name: "deep pointer to val", from: ptr(ptr(789)), to: new(int), expected: 789}, {name: "val to pointer", from: 999, to: new(*int), expected: ptr(999)}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { cast.Convert(tc.to, tc.from) // 获取 to 指针指向的实际值 actual := reflect.ValueOf(tc.to).Elem().Interface() if !reflect.DeepEqual(actual, tc.expected) { t.Errorf("[%s] Failed: from(%v) expected(%v) but got(%v)", tc.name, tc.from, tc.expected, actual) } }) } } func TestConvert_SelfAssignment(t *testing.T) { type User struct { Name string } u := &User{Name: "Tom"} // 应命中自我转换保护,不报错且不改变状态 cast.Convert(u, u) if u.Name != "Tom" { t.Error("Self-assignment guard failed") } } // 5. 复杂映射与去摩擦 Key 匹配测试 func TestComplexFrictionlessMapping(t *testing.T) { // 场景:Map 键名非常混乱,Struct 嵌套,目标存在旧数据 type Sub struct { Age int } type User struct { UserID int UserName string SubInfo Sub } from := map[string]any{ "user_id": 1001, // 下划线 "USER-NAME": "Andy", // 中划线+大写 "subinfo": map[string]any{ // 嵌套+全小写 "age": "18", // 类型不一致 (string -> int) }, } var to User to.UserID = 999 // 预设旧数据,验证是否被正确覆盖 cast.Convert(&to, from) if to.UserID != 1001 { t.Errorf("UserID match failed: %d", to.UserID) } if to.UserName != "Andy" { t.Errorf("UserName match failed: %s", to.UserName) } if to.SubInfo.Age != 18 { t.Errorf("SubInfo.Age match failed: %d", to.SubInfo.Age) } } // 6. 函数转换测试 (Func to Func) func TestFuncConversion(t *testing.T) { // 源函数:接收 int, string,返回 int, string f1 := func(a int, b string) (int, string) { return a + 1, b + "!" } // 目标函数:意图是接收 string, any,返回 string, any var f2 func(string, any) (string, any) cast.Convert(&f2, f1) if f2 == nil { t.Fatal("Converted function is nil") } r1, r2 := f2("10", "hello") if r1 != "11" || r2 != "hello!" { t.Errorf("Func conversion failed: r1=%v, r2=%v", r1, r2) } } // 8. 命名风格模糊匹配测试 func TestFuzzyNamingMapping(t *testing.T) { type TestStruct struct { JSONTag string UserID int UserName string IsActiveFlag bool } // 测试用例:各种不规范输入到标准结构体的映射 from := map[string]any{ "jsontag": "val1", // 小写 "JSONTag": "val2", // 原名 "JSON_TAG": "val3", // 下划线大写 "user_id": 123, // 下划线小写 "USERID": 456, // 全大写 "userName": "Andy", // 首字母小写 "user-name": "Bob", // 中划线 "is_active_flag": "true", // 模糊匹配 bool } var to TestStruct cast.Convert(&to, from) if to.UserID == 0 { t.Errorf("UserID should be matched and set") } if to.UserName == "" { t.Errorf("UserName should be matched and set") } if !to.IsActiveFlag { t.Errorf("IsActiveFlag should be set to true") } t.Logf("Conversion result: %+v", to) } // 9. 异常防御测试 (Panic Prevention) func TestPanicPrevention(t *testing.T) { t.Run("nil destination", func(t *testing.T) { defer func() { if r := recover(); r != nil { t.Errorf("Panic occurred with nil destination: %v", r) } }() cast.Convert(nil, "data") }) t.Run("nil pointer destination", func(t *testing.T) { // nil 指针目标应该是可以接受的,库应当忽略或优雅处理,不应 panic var p *int = nil cast.Convert(&p, 123) // 这里传入的是指针的指针 if p == nil || *p != 123 { t.Errorf("Expected p to be initialized and set to 123, got %v", p) } }) t.Run("read-only destination", func(t *testing.T) { const i = 1 cast.Convert(i, 123) }) } // 辅助函数 func ptr[T any](v T) *T { return &v } // --- 性能测试 --- func BenchmarkCastMatrixConvert(b *testing.B) { from := map[string]any{"id": "123", "name": "test"} type Target struct { ID int Name string } var to Target for i := 0; i < b.N; i++ { cast.Convert(&to, from) } }