convert/convert_test.go

149 lines
3.9 KiB
Go
Raw Permalink Normal View History

package convert_test
import (
"reflect"
"testing"
"apigo.cc/go/convert"
)
// 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) {
convert.To(tc.from, tc.to)
// 获取 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)
}
})
}
}
// 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 // 预设旧数据,验证是否被正确覆盖
convert.To(from, &to)
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)
convert.To(f1, &f2)
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)
}
}
// 7. Parse 钩子测试
type HookStruct struct {
Status int
}
func (h *HookStruct) ParseStatus(v any) int {
s := reflect.ValueOf(v).String()
if s == "active" {
return 1
}
return 0
}
func TestParseHook(t *testing.T) {
from := map[string]any{"status": "active"}
var to HookStruct
convert.To(from, &to)
if to.Status != 1 {
t.Errorf("Parse hook failed, got %d", to.Status)
}
}
// 辅助函数
func ptr[T any](v T) *T { return &v }
// --- 性能测试 ---
func BenchmarkMatrixConvert(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++ {
convert.To(from, &to)
}
}