convert/convert_test.go

149 lines
3.9 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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