- 在 valuex.Accessor 接口中添加 Raw() 方法 - normalizeInputValue 支持 valuex.Accessor 和 []valuex.Accessor 类型 - 提取 normalizeAccessorSlice 泛型函数消除重复代码 - 使用 switch 语句替代 if-else 链提高可读性 - 添加相关测试用例确保功能正确性
1478 lines
34 KiB
Go
1478 lines
34 KiB
Go
package reflux
|
||
|
||
import (
|
||
"encoding/json"
|
||
"reflect"
|
||
"testing"
|
||
|
||
"git.fsdpf.net/go/reflux/valuex"
|
||
)
|
||
|
||
// 测试用的结构体
|
||
type Person struct {
|
||
Name string
|
||
Age int
|
||
Email string
|
||
Address Address
|
||
Tags []string
|
||
Meta map[string]string
|
||
}
|
||
|
||
type Address struct {
|
||
City string
|
||
Street string
|
||
ZipCode int
|
||
}
|
||
|
||
// TestNew 测试 New 函数
|
||
func TestNew(t *testing.T) {
|
||
t.Run("New with pointer", func(t *testing.T) {
|
||
// 测试传入指针 - 修改应该影响原始数据
|
||
p := Person{Name: "Alice", Age: 30}
|
||
rfx := New(&p)
|
||
|
||
rfx.Set("Name", "Bob")
|
||
if p.Name != "Bob" {
|
||
t.Errorf("Expected original value to be modified, got '%s'", p.Name)
|
||
}
|
||
})
|
||
|
||
t.Run("New with value", func(t *testing.T) {
|
||
// 测试传入值 - 修改不应该影响原始数据
|
||
p := Person{Name: "Alice", Age: 30}
|
||
rfx := New(p)
|
||
|
||
rfx.Set("Name", "Bob")
|
||
// 原始值不应该被修改
|
||
if p.Name != "Alice" {
|
||
t.Errorf("Expected original value to remain 'Alice', got '%s'", p.Name)
|
||
}
|
||
|
||
// rfx 内部的值应该已经修改
|
||
if rfx.Get("Name").String() != "Bob" {
|
||
t.Errorf("Expected rfx value to be 'Bob', got '%s'", rfx.Get("Name").String())
|
||
}
|
||
})
|
||
|
||
t.Run("New with map pointer", func(t *testing.T) {
|
||
m := map[string]any{"name": "Alice"}
|
||
rfx := New(&m)
|
||
|
||
rfx.Set("name", "Bob")
|
||
if m["name"] != "Bob" {
|
||
t.Errorf("Expected map to be modified, got '%v'", m["name"])
|
||
}
|
||
})
|
||
|
||
t.Run("New with map value", func(t *testing.T) {
|
||
m := map[string]any{"name": "Alice"}
|
||
rfx := New(m)
|
||
|
||
rfx.Set("name", "Bob")
|
||
// 原始 map 不应该被修改
|
||
if m["name"] != "Alice" {
|
||
t.Errorf("Expected original map to remain 'Alice', got '%v'", m["name"])
|
||
}
|
||
})
|
||
|
||
t.Run("New with slice pointer", func(t *testing.T) {
|
||
s := []string{"a", "b", "c"}
|
||
rfx := New(&s)
|
||
|
||
rfx.Set("0", "x")
|
||
if s[0] != "x" {
|
||
t.Errorf("Expected slice to be modified, got '%s'", s[0])
|
||
}
|
||
})
|
||
|
||
t.Run("New with slice value", func(t *testing.T) {
|
||
s := []string{"a", "b", "c"}
|
||
rfx := New(s)
|
||
|
||
rfx.Set("0", "x")
|
||
// 原始切片不应该被修改
|
||
if s[0] != "a" {
|
||
t.Errorf("Expected original slice to remain 'a', got '%s'", s[0])
|
||
}
|
||
})
|
||
|
||
t.Run("New with array value", func(t *testing.T) {
|
||
a := [3]string{"a", "b", "c"}
|
||
rfx := New(a)
|
||
|
||
rfx.Set("0", "x")
|
||
// 原始数组不应该被修改
|
||
if a[0] != "a" {
|
||
t.Errorf("Expected original array to remain 'a', got '%s'", a[0])
|
||
}
|
||
})
|
||
|
||
t.Run("New with nil pointer", func(t *testing.T) {
|
||
defer func() {
|
||
if r := recover(); r == nil {
|
||
t.Error("Expected panic with nil pointer")
|
||
}
|
||
}()
|
||
var p *Person
|
||
New(p)
|
||
})
|
||
|
||
t.Run("New with unsupported type", func(t *testing.T) {
|
||
defer func() {
|
||
if r := recover(); r == nil {
|
||
t.Error("Expected panic with unsupported type")
|
||
}
|
||
}()
|
||
New(42) // int 不支持
|
||
})
|
||
|
||
t.Run("New with interface wrapping map", func(t *testing.T) {
|
||
// 测试 interface{} 包装的 map
|
||
var data any = map[string]string{"name": "Alice"}
|
||
rfx := New(data)
|
||
|
||
if rfx.Get("name").String() != "Alice" {
|
||
t.Errorf("Expected 'Alice', got '%s'", rfx.Get("name").String())
|
||
}
|
||
|
||
// 修改不应该影响原始数据(因为是值传递)
|
||
rfx.Set("name", "Bob")
|
||
originalMap := data.(map[string]string)
|
||
if originalMap["name"] != "Alice" {
|
||
t.Error("Original map should not be modified")
|
||
}
|
||
})
|
||
|
||
t.Run("New with interface wrapping struct", func(t *testing.T) {
|
||
// 测试 interface{} 包装的 struct
|
||
var data any = Person{Name: "Alice", Age: 30}
|
||
rfx := New(data)
|
||
|
||
if rfx.Get("Name").String() != "Alice" {
|
||
t.Errorf("Expected 'Alice', got '%s'", rfx.Get("Name").String())
|
||
}
|
||
})
|
||
|
||
t.Run("New with interface wrapping pointer", func(t *testing.T) {
|
||
// 测试 interface{} 包装的指针
|
||
person := Person{Name: "Alice", Age: 30}
|
||
var data any = &person
|
||
rfx := New(data)
|
||
|
||
rfx.Set("Name", "Bob")
|
||
// 原始数据应该被修改(因为是指针)
|
||
if person.Name != "Bob" {
|
||
t.Errorf("Expected original to be modified, got '%s'", person.Name)
|
||
}
|
||
})
|
||
|
||
t.Run("New with nil interface", func(t *testing.T) {
|
||
defer func() {
|
||
if r := recover(); r == nil {
|
||
t.Error("Expected panic with nil interface")
|
||
}
|
||
}()
|
||
var data any
|
||
New(data)
|
||
})
|
||
}
|
||
|
||
// TestNewWithPointer 测试传入指针的情况
|
||
func TestNewWithPointer(t *testing.T) {
|
||
p := Person{Name: "Alice", Age: 30}
|
||
|
||
// 测试传入指针值
|
||
rfx := New(&p)
|
||
if rfx == nil {
|
||
t.Fatal("New() with pointer returned nil")
|
||
}
|
||
}
|
||
|
||
// TestGet 测试 Get 方法
|
||
func TestGet(t *testing.T) {
|
||
p := Person{
|
||
Name: "Bob",
|
||
Age: 25,
|
||
Address: Address{
|
||
City: "Beijing",
|
||
Street: "Main St",
|
||
},
|
||
Tags: []string{"developer", "gopher"},
|
||
}
|
||
|
||
rfx := New(&p)
|
||
|
||
// 测试获取顶层字段
|
||
name := rfx.Get("Name").String()
|
||
if name != "Bob" {
|
||
t.Errorf("Expected 'Bob', got '%s'", name)
|
||
}
|
||
|
||
// 测试获取嵌套字段
|
||
city := rfx.Get("Address", "City").String()
|
||
if city != "Beijing" {
|
||
t.Errorf("Expected 'Beijing', got '%s'", city)
|
||
}
|
||
|
||
// 测试获取切片元素
|
||
tag := rfx.Get("Tags", "0").String()
|
||
if tag != "developer" {
|
||
t.Errorf("Expected 'developer', got '%s'", tag)
|
||
}
|
||
}
|
||
|
||
// TestScope 测试 Scope 方法
|
||
func TestScope(t *testing.T) {
|
||
p := Person{
|
||
Address: Address{
|
||
City: "Shanghai",
|
||
Street: "Park Ave",
|
||
ZipCode: 200000,
|
||
},
|
||
}
|
||
|
||
rfx := New(&p)
|
||
addressScope := rfx.Scope("Address")
|
||
|
||
// 测试通过 Scope 获取值
|
||
city := addressScope.Get("City").String()
|
||
if city != "Shanghai" {
|
||
t.Errorf("Expected 'Shanghai', got '%s'", city)
|
||
}
|
||
|
||
// 测试通过 Scope 设置值后,原对象不应该被修改(因为是深度克隆)
|
||
addressScope.Set("City", "Beijing")
|
||
|
||
// 验证原对象的值没有改变(深度克隆)
|
||
if p.Address.City != "Shanghai" {
|
||
t.Errorf("Expected original object's City to remain 'Shanghai', got '%s'", p.Address.City)
|
||
}
|
||
|
||
// 验证 Scope 内的值已改变
|
||
scopeCity := addressScope.Get("City").String()
|
||
if scopeCity != "Beijing" {
|
||
t.Errorf("Expected scope's City to be 'Beijing', got '%s'", scopeCity)
|
||
}
|
||
|
||
// 验证通过原 rfx 对象获取的值仍然是原值
|
||
cityFromOriginal := rfx.Get("Address", "City").String()
|
||
if cityFromOriginal != "Shanghai" {
|
||
t.Errorf("Expected 'Shanghai' from original rfx, got '%s'", cityFromOriginal)
|
||
}
|
||
|
||
// 测试在 Scope 上设置多个字段
|
||
addressScope.Set("Street", "New Street").Set("ZipCode", 100000)
|
||
|
||
// 验证原对象的值都没有改变
|
||
if p.Address.Street != "Park Ave" {
|
||
t.Errorf("Expected original Street to remain 'Park Ave', got '%s'", p.Address.Street)
|
||
}
|
||
if p.Address.ZipCode != 200000 {
|
||
t.Errorf("Expected original ZipCode to remain 200000, got %d", p.Address.ZipCode)
|
||
}
|
||
|
||
// 验证 Scope 内的值已经改变
|
||
scopeStreet := addressScope.Get("Street").String()
|
||
scopeZipCode := addressScope.Get("ZipCode").Int()
|
||
if scopeStreet != "New Street" {
|
||
t.Errorf("Expected scope's Street to be 'New Street', got '%s'", scopeStreet)
|
||
}
|
||
if scopeZipCode != 100000 {
|
||
t.Errorf("Expected scope's ZipCode to be 100000, got %d", scopeZipCode)
|
||
}
|
||
|
||
// 再次验证 Scope 的值与原对象不同(证明是深度克隆)
|
||
if scopeCity == p.Address.City {
|
||
t.Error("Scope should have independent copy, not reference to original")
|
||
}
|
||
}
|
||
|
||
// TestSet 测试 Set 方法
|
||
func TestSet(t *testing.T) {
|
||
p := Person{
|
||
Name: "Charlie",
|
||
Age: 30,
|
||
Address: Address{
|
||
City: "Guangzhou",
|
||
},
|
||
Meta: make(map[string]string),
|
||
}
|
||
|
||
rfx := New(&p)
|
||
|
||
// 测试设置顶层字段
|
||
rfx.Set("Name", "David")
|
||
if p.Name != "David" {
|
||
t.Errorf("Expected 'David', got '%s'", p.Name)
|
||
}
|
||
|
||
// 测试设置嵌套字段
|
||
rfx.Set("Address.City", "Shenzhen")
|
||
if p.Address.City != "Shenzhen" {
|
||
t.Errorf("Expected 'Shenzhen', got '%s'", p.Address.City)
|
||
}
|
||
|
||
// 测试设置 map 值
|
||
rfx.Set("Meta.key1", "value1")
|
||
if p.Meta["key1"] != "value1" {
|
||
t.Errorf("Expected 'value1', got '%s'", p.Meta["key1"])
|
||
}
|
||
|
||
// 测试类型转换
|
||
rfx.Set("Age", int32(35))
|
||
if p.Age != 35 {
|
||
t.Errorf("Expected 35, got %d", p.Age)
|
||
}
|
||
|
||
// 测试设置整个 Address 结构体
|
||
newAddress := Address{
|
||
City: "Shanghai",
|
||
Street: "Nanjing Road",
|
||
ZipCode: 200000,
|
||
}
|
||
rfx.Set("Address", newAddress)
|
||
if p.Address.City != "Shanghai" {
|
||
t.Errorf("Expected City 'Shanghai', got '%s'", p.Address.City)
|
||
}
|
||
if p.Address.Street != "Nanjing Road" {
|
||
t.Errorf("Expected Street 'Nanjing Road', got '%s'", p.Address.Street)
|
||
}
|
||
if p.Address.ZipCode != 200000 {
|
||
t.Errorf("Expected ZipCode 200000, got %d", p.Address.ZipCode)
|
||
}
|
||
|
||
// 测试设置 Address 结构体后再修改其字段
|
||
rfx.Set("Address.ZipCode", 200001)
|
||
if p.Address.ZipCode != 200001 {
|
||
t.Errorf("Expected ZipCode 200001, got %d", p.Address.ZipCode)
|
||
}
|
||
}
|
||
|
||
// TestDelete 测试 Delete 方法
|
||
func TestDelete(t *testing.T) {
|
||
meta := map[string]string{
|
||
"key1": "value1",
|
||
"key2": "value2",
|
||
}
|
||
p := Person{
|
||
Meta: meta,
|
||
Tags: []string{"tag1", "tag2", "tag3"},
|
||
}
|
||
|
||
rfx := New(&p)
|
||
|
||
// 测试删除 map 键
|
||
rfx.Delete("Meta", "key1")
|
||
if _, exists := p.Meta["key1"]; exists {
|
||
t.Error("key1 should have been deleted")
|
||
}
|
||
|
||
// 测试删除切片元素
|
||
rfx.Delete("Tags", "1")
|
||
if len(p.Tags) != 2 || p.Tags[1] != "tag3" {
|
||
t.Errorf("Expected Tags to be ['tag1', 'tag3'], got %v", p.Tags)
|
||
}
|
||
}
|
||
|
||
// TestExists 测试 Exists 方法
|
||
func TestExists(t *testing.T) {
|
||
p := Person{
|
||
Name: "Eve",
|
||
Address: Address{
|
||
City: "Hangzhou",
|
||
},
|
||
}
|
||
|
||
rfx := New(&p)
|
||
|
||
// 测试存在的字段
|
||
if !rfx.Exists("Name") {
|
||
t.Error("Name should exist")
|
||
}
|
||
|
||
// 测试嵌套字段
|
||
if !rfx.Exists("Address", "City") {
|
||
t.Error("Address.City should exist")
|
||
}
|
||
|
||
// 测试不存在的字段
|
||
if rfx.Exists("NonExistent") {
|
||
t.Error("NonExistent should not exist")
|
||
}
|
||
|
||
// 测试空字段
|
||
if rfx.Exists("Email") {
|
||
// Email 是空字符串,但字段存在
|
||
email := rfx.Get("Email").String()
|
||
if email != "" {
|
||
t.Error("Email should be empty string")
|
||
}
|
||
}
|
||
}
|
||
|
||
// TestArray 测试 Array 方法
|
||
func TestArray(t *testing.T) {
|
||
tags := []string{"go", "rust", "python"}
|
||
p := Person{
|
||
Tags: tags,
|
||
}
|
||
|
||
rfx := New(&p)
|
||
arr := rfx.Get("Tags").Array()
|
||
|
||
if len(arr) != 3 {
|
||
t.Errorf("Expected array length 3, got %d", len(arr))
|
||
}
|
||
|
||
if arr[0].String() != "go" {
|
||
t.Errorf("Expected 'go', got '%s'", arr[0].String())
|
||
}
|
||
}
|
||
|
||
// TestKeys 测试 keys 方法
|
||
func TestKeys(t *testing.T) {
|
||
meta := map[string]string{
|
||
"key1": "value1",
|
||
"key2": "value2",
|
||
"key3": "value3",
|
||
}
|
||
p := Person{
|
||
Meta: meta,
|
||
}
|
||
|
||
rfx := New(&p)
|
||
keys := rfx.Get("Meta").Keys()
|
||
|
||
if len(keys) != 3 {
|
||
t.Errorf("Expected 3 keys, got %d", len(keys))
|
||
}
|
||
|
||
// 检查键是否存在
|
||
keyMap := make(map[string]bool)
|
||
for _, k := range keys {
|
||
keyMap[k] = true
|
||
}
|
||
if !keyMap["key1"] || !keyMap["key2"] || !keyMap["key3"] {
|
||
t.Error("Missing expected keys")
|
||
}
|
||
}
|
||
|
||
// TestTypeConversions 测试类型转换方法
|
||
func TestTypeConversions(t *testing.T) {
|
||
// 测试整数转换
|
||
type Numbers struct {
|
||
IntVal int
|
||
Int64Val int64
|
||
UintVal uint
|
||
FloatVal float64
|
||
BoolVal bool
|
||
StringVal string
|
||
}
|
||
|
||
n := Numbers{
|
||
IntVal: 42,
|
||
Int64Val: 9223372036854775807,
|
||
UintVal: 100,
|
||
FloatVal: 3.14,
|
||
BoolVal: true,
|
||
StringVal: "hello",
|
||
}
|
||
|
||
rfx := New(&n)
|
||
|
||
// Int
|
||
if rfx.Get("IntVal").Int() != 42 {
|
||
t.Error("Int() conversion failed")
|
||
}
|
||
|
||
// Int64
|
||
if rfx.Get("Int64Val").Int64() != 9223372036854775807 {
|
||
t.Error("Int64() conversion failed")
|
||
}
|
||
|
||
// Uint
|
||
if rfx.Get("UintVal").Uint() != 100 {
|
||
t.Error("Uint() conversion failed")
|
||
}
|
||
|
||
// Float64
|
||
if rfx.Get("FloatVal").Float64() != 3.14 {
|
||
t.Error("Float64() conversion failed")
|
||
}
|
||
|
||
// Bool
|
||
if !rfx.Get("BoolVal").Bool() {
|
||
t.Error("Bool() conversion failed")
|
||
}
|
||
|
||
// String
|
||
if rfx.Get("StringVal").String() != "hello" {
|
||
t.Error("String() conversion failed")
|
||
}
|
||
|
||
// 跨类型转换
|
||
if rfx.Get("IntVal").Float64() != 42.0 {
|
||
t.Error("Int to Float64 conversion failed")
|
||
}
|
||
|
||
if rfx.Get("FloatVal").Int64() != 3 {
|
||
t.Error("Float64 to Int64 conversion failed")
|
||
}
|
||
}
|
||
|
||
// TestStringMapConversions 测试 map 类型转换
|
||
func TestStringMapConversions(t *testing.T) {
|
||
type Maps struct {
|
||
StringMap map[string]string
|
||
IntMap map[string]int
|
||
BoolMap map[string]bool
|
||
StringSliceMap map[string][]string
|
||
}
|
||
|
||
m := Maps{
|
||
StringMap: map[string]string{
|
||
"name": "Alice",
|
||
"city": "NYC",
|
||
},
|
||
IntMap: map[string]int{
|
||
"age": 30,
|
||
"score": 100,
|
||
},
|
||
BoolMap: map[string]bool{
|
||
"active": true,
|
||
"enabled": false,
|
||
},
|
||
StringSliceMap: map[string][]string{
|
||
"tags": {"go", "rust"},
|
||
},
|
||
}
|
||
|
||
rfx := New(&m)
|
||
|
||
// StringMapString
|
||
strMap := rfx.Get("StringMap").StringMapString()
|
||
if strMap["name"] != "Alice" {
|
||
t.Error("StringMapString conversion failed")
|
||
}
|
||
|
||
// StringMapInt
|
||
intMap := rfx.Get("IntMap").StringMapInt()
|
||
if intMap["age"] != 30 {
|
||
t.Error("StringMapInt conversion failed")
|
||
}
|
||
|
||
// StringMapBool
|
||
boolMap := rfx.Get("BoolMap").StringMapBool()
|
||
if !boolMap["active"] {
|
||
t.Error("StringMapBool conversion failed")
|
||
}
|
||
|
||
// StringMapStringSlice
|
||
sliceMap := rfx.Get("StringSliceMap").StringMapStringSlice()
|
||
if len(sliceMap["tags"]) != 2 || sliceMap["tags"][0] != "go" {
|
||
t.Error("StringMapStringSlice conversion failed")
|
||
}
|
||
}
|
||
|
||
// TestSliceConversions 测试切片类型转换
|
||
func TestSliceConversions(t *testing.T) {
|
||
type Slices struct {
|
||
IntSlice []int
|
||
StringSlice []string
|
||
BoolSlice []bool
|
||
}
|
||
|
||
s := Slices{
|
||
IntSlice: []int{1, 2, 3},
|
||
StringSlice: []string{"a", "b", "c"},
|
||
BoolSlice: []bool{true, false, true},
|
||
}
|
||
|
||
rfx := New(&s)
|
||
|
||
// IntSlice
|
||
intSlice := rfx.Get("IntSlice").IntSlice()
|
||
if len(intSlice) != 3 || intSlice[1] != 2 {
|
||
t.Error("IntSlice conversion failed")
|
||
}
|
||
|
||
// StringSlice
|
||
strSlice := rfx.Get("StringSlice").StringSlice()
|
||
if len(strSlice) != 3 || strSlice[1] != "b" {
|
||
t.Error("StringSlice conversion failed")
|
||
}
|
||
|
||
// BoolSlice
|
||
boolSlice := rfx.Get("BoolSlice").BoolSlice()
|
||
if len(boolSlice) != 3 || boolSlice[1] != false {
|
||
t.Error("BoolSlice conversion failed")
|
||
}
|
||
|
||
// Slice (any)
|
||
anySlice := rfx.Get("IntSlice").Slice()
|
||
if len(anySlice) != 3 {
|
||
t.Error("Slice conversion failed")
|
||
}
|
||
}
|
||
|
||
// TestPtr 测试 Ptr 方法
|
||
func TestPtr(t *testing.T) {
|
||
p := Person{Name: "Frank"}
|
||
rfx := New(&p)
|
||
|
||
ptr := rfx.Ptr()
|
||
if ptr == nil {
|
||
t.Error("Ptr() returned nil")
|
||
}
|
||
|
||
// 验证指针指向正确的对象
|
||
if pPtr, ok := ptr.(*Person); ok {
|
||
if pPtr.Name != "Frank" {
|
||
t.Error("Ptr() returned incorrect pointer")
|
||
}
|
||
} else {
|
||
t.Error("Ptr() returned wrong type")
|
||
}
|
||
}
|
||
|
||
// TestGetPtrWithJSONUnmarshal 测试 Get().Ptr() 与 json.Unmarshal() 的配合
|
||
func TestGetPtrWithJSONUnmarshal(t *testing.T) {
|
||
t.Run("Unmarshal to nested struct field", func(t *testing.T) {
|
||
p := Person{
|
||
Name: "Alice",
|
||
Age: 30,
|
||
Address: Address{
|
||
City: "OldCity",
|
||
},
|
||
}
|
||
|
||
rfx := New(&p)
|
||
|
||
// 准备 JSON 数据
|
||
jsonData := []byte(`{
|
||
"City": "Shanghai",
|
||
"Street": "Nanjing Road",
|
||
"ZipCode": 200000
|
||
}`)
|
||
|
||
// 通过 Get().Ptr() 获取 Address 字段的指针
|
||
addressPtr := rfx.Get("Address").Ptr()
|
||
if addressPtr == nil {
|
||
t.Fatal("Get('Address').Ptr() returned nil")
|
||
}
|
||
|
||
// 使用 json.Unmarshal 直接赋值
|
||
err := json.Unmarshal(jsonData, addressPtr)
|
||
if err != nil {
|
||
t.Fatalf("json.Unmarshal failed: %v", err)
|
||
}
|
||
|
||
// 验证赋值成功
|
||
if p.Address.City != "Shanghai" {
|
||
t.Errorf("Expected City 'Shanghai', got '%s'", p.Address.City)
|
||
}
|
||
if p.Address.Street != "Nanjing Road" {
|
||
t.Errorf("Expected Street 'Nanjing Road', got '%s'", p.Address.Street)
|
||
}
|
||
if p.Address.ZipCode != 200000 {
|
||
t.Errorf("Expected ZipCode 200000, got %d", p.Address.ZipCode)
|
||
}
|
||
})
|
||
|
||
t.Run("Unmarshal to top-level struct", func(t *testing.T) {
|
||
p := Person{
|
||
Name: "Bob",
|
||
Age: 25,
|
||
}
|
||
|
||
rfx := New(&p)
|
||
|
||
// 准备 JSON 数据
|
||
jsonData := []byte(`{
|
||
"Name": "Charlie",
|
||
"Age": 35,
|
||
"Email": "charlie@example.com"
|
||
}`)
|
||
|
||
// 通过 Ptr() 获取整个 Person 的指针
|
||
personPtr := rfx.Ptr()
|
||
if personPtr == nil {
|
||
t.Fatal("Ptr() returned nil")
|
||
}
|
||
|
||
// 使用 json.Unmarshal 直接赋值
|
||
err := json.Unmarshal(jsonData, personPtr)
|
||
if err != nil {
|
||
t.Fatalf("json.Unmarshal failed: %v", err)
|
||
}
|
||
|
||
// 验证赋值成功
|
||
if p.Name != "Charlie" {
|
||
t.Errorf("Expected Name 'Charlie', got '%s'", p.Name)
|
||
}
|
||
if p.Age != 35 {
|
||
t.Errorf("Expected Age 35, got %d", p.Age)
|
||
}
|
||
if p.Email != "charlie@example.com" {
|
||
t.Errorf("Expected Email 'charlie@example.com', got '%s'", p.Email)
|
||
}
|
||
})
|
||
|
||
t.Run("Unmarshal to map field", func(t *testing.T) {
|
||
p := Person{
|
||
Name: "Dave",
|
||
Meta: make(map[string]string),
|
||
}
|
||
|
||
rfx := New(&p)
|
||
|
||
// 准备 JSON 数据
|
||
jsonData := []byte(`{
|
||
"key1": "value1",
|
||
"key2": "value2",
|
||
"key3": "value3"
|
||
}`)
|
||
|
||
// 通过 Get().Ptr() 获取 Meta 字段的指针
|
||
metaPtr := rfx.Get("Meta").Ptr()
|
||
if metaPtr == nil {
|
||
t.Fatal("Get('Meta').Ptr() returned nil")
|
||
}
|
||
|
||
// 使用 json.Unmarshal 直接赋值
|
||
err := json.Unmarshal(jsonData, metaPtr)
|
||
if err != nil {
|
||
t.Fatalf("json.Unmarshal failed: %v", err)
|
||
}
|
||
|
||
// 验证赋值成功
|
||
if p.Meta["key1"] != "value1" {
|
||
t.Errorf("Expected Meta['key1'] 'value1', got '%s'", p.Meta["key1"])
|
||
}
|
||
if p.Meta["key2"] != "value2" {
|
||
t.Errorf("Expected Meta['key2'] 'value2', got '%s'", p.Meta["key2"])
|
||
}
|
||
if p.Meta["key3"] != "value3" {
|
||
t.Errorf("Expected Meta['key3'] 'value3', got '%s'", p.Meta["key3"])
|
||
}
|
||
})
|
||
|
||
t.Run("Unmarshal to slice field", func(t *testing.T) {
|
||
p := Person{
|
||
Name: "Eve",
|
||
Tags: []string{"old1", "old2"},
|
||
}
|
||
|
||
rfx := New(&p)
|
||
|
||
// 准备 JSON 数据
|
||
jsonData := []byte(`["tag1", "tag2", "tag3"]`)
|
||
|
||
// 通过 Get().Ptr() 获取 Tags 字段的指针
|
||
tagsPtr := rfx.Get("Tags").Ptr()
|
||
if tagsPtr == nil {
|
||
t.Fatal("Get('Tags').Ptr() returned nil")
|
||
}
|
||
|
||
// 使用 json.Unmarshal 直接赋值
|
||
err := json.Unmarshal(jsonData, tagsPtr)
|
||
if err != nil {
|
||
t.Fatalf("json.Unmarshal failed: %v", err)
|
||
}
|
||
|
||
// 验证赋值成功
|
||
if len(p.Tags) != 3 {
|
||
t.Errorf("Expected Tags length 3, got %d", len(p.Tags))
|
||
}
|
||
if p.Tags[0] != "tag1" || p.Tags[1] != "tag2" || p.Tags[2] != "tag3" {
|
||
t.Errorf("Expected Tags ['tag1', 'tag2', 'tag3'], got %v", p.Tags)
|
||
}
|
||
})
|
||
}
|
||
|
||
// TestAny 测试 Any 方法
|
||
func TestAny(t *testing.T) {
|
||
p := Person{Name: "Grace", Age: 28}
|
||
rfx := New(&p)
|
||
|
||
anyVal := rfx.Any()
|
||
if person, ok := anyVal.(Person); ok {
|
||
if person.Name != "Grace" || person.Age != 28 {
|
||
t.Error("Any() returned incorrect value")
|
||
}
|
||
} else {
|
||
t.Error("Any() returned wrong type")
|
||
}
|
||
}
|
||
|
||
// TestRaw 测试 Value 方法
|
||
func TestRaw(t *testing.T) {
|
||
p := Person{Name: "Henry"}
|
||
rfx := New(&p)
|
||
|
||
val := rfx.Raw()
|
||
if !val.IsValid() {
|
||
t.Error("Value() returned invalid value")
|
||
}
|
||
|
||
if val.Kind() != 22 { // reflect.Ptr
|
||
t.Errorf("Expected pointer kind, got %v", val.Kind())
|
||
}
|
||
}
|
||
|
||
// TestComplexNesting 测试复杂嵌套场景
|
||
func TestComplexNesting(t *testing.T) {
|
||
type Company struct {
|
||
Name string
|
||
Employees []Person
|
||
}
|
||
|
||
company := Company{
|
||
Name: "TechCorp",
|
||
Employees: []Person{
|
||
{Name: "Alice", Age: 30},
|
||
{Name: "Bob", Age: 25},
|
||
},
|
||
}
|
||
|
||
rfx := New(&company)
|
||
|
||
// 获取嵌套数组中的值
|
||
empName := rfx.Get("Employees", "0", "Name").String()
|
||
if empName != "Alice" {
|
||
t.Errorf("Expected 'Alice', got '%s'", empName)
|
||
}
|
||
|
||
// 设置嵌套数组中的值
|
||
rfx.Set("Employees.0.Age", 31)
|
||
if company.Employees[0].Age != 31 {
|
||
t.Errorf("Expected 31, got %d", company.Employees[0].Age)
|
||
}
|
||
}
|
||
|
||
// TestEdgeCases 测试边界情况
|
||
func TestEdgeCases(t *testing.T) {
|
||
p := Person{}
|
||
rfx := New(&p)
|
||
|
||
// 测试空值
|
||
emptyStr := rfx.Get("Name").String()
|
||
if emptyStr != "" {
|
||
t.Error("Empty string should be returned")
|
||
}
|
||
|
||
// 测试不存在的路径
|
||
nonExistent := rfx.Get("NonExistent", "Field").String()
|
||
if nonExistent != "" {
|
||
t.Error("Non-existent path should return empty string")
|
||
}
|
||
|
||
// 测试初始化的 nil map(结构体字段默认为 nil)
|
||
metaMap := rfx.Get("Meta").StringMapString()
|
||
// nil map 会返回 nil,这是正确的行为
|
||
_ = metaMap
|
||
|
||
// 测试 nil 切片(结构体字段默认为 nil)
|
||
tags := rfx.Get("Tags").StringSlice()
|
||
// nil slice 会返回 nil,这是正确的行为
|
||
_ = tags
|
||
|
||
// 测试设置到不存在的路径应该 panic
|
||
defer func() {
|
||
if r := recover(); r == nil {
|
||
t.Error("Setting non-existent field should panic")
|
||
}
|
||
}()
|
||
rfx.Set("NonExistent", "value") // 应该 panic
|
||
}
|
||
|
||
// TestMapOperations 测试 map 操作
|
||
func TestMapOperations(t *testing.T) {
|
||
m := map[string]any{
|
||
"name": "Alice",
|
||
"age": 30,
|
||
"tags": []string{"go", "rust"},
|
||
}
|
||
|
||
rfx := New(&m)
|
||
|
||
// Get from map
|
||
name := rfx.Get("name").String()
|
||
if name != "Alice" {
|
||
t.Errorf("Expected 'Alice', got '%s'", name)
|
||
}
|
||
|
||
// Set to map
|
||
rfx.Set("name", "Bob")
|
||
if m["name"] != "Bob" {
|
||
t.Errorf("Expected 'Bob', got '%v'", m["name"])
|
||
}
|
||
|
||
// Add new key
|
||
rfx.Set("age", 35)
|
||
|
||
// Delete from map
|
||
rfx.Delete("age")
|
||
if _, exists := m["age"]; exists {
|
||
t.Error("Key should have been deleted")
|
||
}
|
||
}
|
||
|
||
// BenchmarkGet 性能测试 Get 方法
|
||
func BenchmarkGet(b *testing.B) {
|
||
p := Person{
|
||
Name: "Benchmark",
|
||
Address: Address{
|
||
City: "TestCity",
|
||
},
|
||
}
|
||
rfx := New(&p)
|
||
|
||
b.ResetTimer()
|
||
for i := 0; i < b.N; i++ {
|
||
_ = rfx.Get("Address", "City").String()
|
||
}
|
||
}
|
||
|
||
// BenchmarkSet 性能测试 Set 方法
|
||
func BenchmarkSet(b *testing.B) {
|
||
p := Person{}
|
||
rfx := New(&p)
|
||
|
||
b.ResetTimer()
|
||
for i := 0; i < b.N; i++ {
|
||
rfx.Set("Name", "TestName")
|
||
}
|
||
}
|
||
|
||
// TestDotNotationPath 测试点号分割路径
|
||
func TestDotNotationPath(t *testing.T) {
|
||
p := Person{
|
||
Name: "Alice",
|
||
Age: 30,
|
||
Address: Address{
|
||
City: "Beijing",
|
||
Street: "Main St",
|
||
ZipCode: 100000,
|
||
},
|
||
Tags: []string{"go", "rust", "python"},
|
||
Meta: map[string]string{
|
||
"level": "senior",
|
||
},
|
||
}
|
||
|
||
rfx := New(&p)
|
||
|
||
// 测试点号分割路径获取
|
||
t.Run("Get with dot notation", func(t *testing.T) {
|
||
// 使用点号访问嵌套字段
|
||
city := rfx.Get("Address.City").String()
|
||
if city != "Beijing" {
|
||
t.Errorf("Expected 'Beijing', got '%s'", city)
|
||
}
|
||
|
||
zipCode := rfx.Get("Address.ZipCode").Int()
|
||
if zipCode != 100000 {
|
||
t.Errorf("Expected 100000, got %d", zipCode)
|
||
}
|
||
})
|
||
|
||
// 测试混合使用点号和分隔参数
|
||
t.Run("Get with mixed notation", func(t *testing.T) {
|
||
city := rfx.Get("Address.City").String()
|
||
if city != "Beijing" {
|
||
t.Errorf("Expected 'Beijing', got '%s'", city)
|
||
}
|
||
|
||
// 混合使用
|
||
metaLevel := rfx.Get("Meta", "level").String()
|
||
if metaLevel != "senior" {
|
||
t.Errorf("Expected 'senior', got '%s'", metaLevel)
|
||
}
|
||
|
||
// 也可以用点号访问 Map
|
||
metaLevel2 := rfx.Get("Meta.level").String()
|
||
if metaLevel2 != "senior" {
|
||
t.Errorf("Expected 'senior', got '%s'", metaLevel2)
|
||
}
|
||
})
|
||
|
||
// 测试访问切片元素
|
||
t.Run("Get array with dot notation", func(t *testing.T) {
|
||
tag := rfx.Get("Tags.0").String()
|
||
if tag != "go" {
|
||
t.Errorf("Expected 'go', got '%s'", tag)
|
||
}
|
||
|
||
tag2 := rfx.Get("Tags.1").String()
|
||
if tag2 != "rust" {
|
||
t.Errorf("Expected 'rust', got '%s'", tag2)
|
||
}
|
||
})
|
||
|
||
// 测试 Set 使用点号路径
|
||
t.Run("Set with dot notation", func(t *testing.T) {
|
||
rfx.Set("Address.City", "Shanghai")
|
||
if p.Address.City != "Shanghai" {
|
||
t.Errorf("Expected 'Shanghai', got '%s'", p.Address.City)
|
||
}
|
||
|
||
rfx.Set("Address.ZipCode", 200000)
|
||
if p.Address.ZipCode != 200000 {
|
||
t.Errorf("Expected 200000, got %d", p.Address.ZipCode)
|
||
}
|
||
})
|
||
|
||
// 测试 Exists 使用点号路径
|
||
t.Run("Exists with dot notation", func(t *testing.T) {
|
||
if !rfx.Exists("Address.City") {
|
||
t.Error("Address.City should exist")
|
||
}
|
||
|
||
if rfx.Exists("Address.NonExistent") {
|
||
t.Error("Address.NonExistent should not exist")
|
||
}
|
||
})
|
||
|
||
// 测试 Delete 使用点号路径
|
||
t.Run("Delete with dot notation", func(t *testing.T) {
|
||
rfx.Delete("Meta.level")
|
||
if _, exists := p.Meta["level"]; exists {
|
||
t.Error("Meta.level should have been deleted")
|
||
}
|
||
})
|
||
|
||
// 测试 Scope 使用点号路径
|
||
t.Run("Scope with dot notation", func(t *testing.T) {
|
||
addressScope := rfx.Scope("Address")
|
||
city := addressScope.Get("City").String()
|
||
if city != "Shanghai" {
|
||
t.Errorf("Expected 'Shanghai', got '%s'", city)
|
||
}
|
||
})
|
||
}
|
||
|
||
// TestExpandPath 测试路径展开功能
|
||
func TestExpandPath(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
input []string
|
||
expected []string
|
||
}{
|
||
{
|
||
name: "Simple path",
|
||
input: []string{"a", "b", "c"},
|
||
expected: []string{"a", "b", "c"},
|
||
},
|
||
{
|
||
name: "Dot notation",
|
||
input: []string{"a.b.c"},
|
||
expected: []string{"a", "b", "c"},
|
||
},
|
||
{
|
||
name: "Mixed notation",
|
||
input: []string{"a.b", "c"},
|
||
expected: []string{"a", "b", "c"},
|
||
},
|
||
{
|
||
name: "Multiple dots",
|
||
input: []string{"a.b", "c.d", "e"},
|
||
expected: []string{"a", "b", "c", "d", "e"},
|
||
},
|
||
{
|
||
name: "Empty segments",
|
||
input: []string{"a..b", "c"},
|
||
expected: []string{"a", "b", "c"},
|
||
},
|
||
{
|
||
name: "Leading/trailing dots",
|
||
input: []string{".a.b.", "c"},
|
||
expected: []string{"a", "b", "c"},
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
result := expandPath(tt.input...)
|
||
if len(result) != len(tt.expected) {
|
||
t.Errorf("Expected length %d, got %d", len(tt.expected), len(result))
|
||
return
|
||
}
|
||
for i := range result {
|
||
if result[i] != tt.expected[i] {
|
||
t.Errorf("At index %d: expected '%s', got '%s'", i, tt.expected[i], result[i])
|
||
}
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// TestComplexDotNotation 测试复杂的点号路径场景
|
||
func TestComplexDotNotation(t *testing.T) {
|
||
type Company struct {
|
||
Name string
|
||
Address Address
|
||
CEO Person
|
||
}
|
||
|
||
company := Company{
|
||
Name: "TechCorp",
|
||
Address: Address{
|
||
City: "Beijing",
|
||
Street: "Tech Street",
|
||
},
|
||
CEO: Person{
|
||
Name: "John Doe",
|
||
Age: 45,
|
||
Address: Address{
|
||
City: "Shanghai",
|
||
},
|
||
},
|
||
}
|
||
|
||
rfx := New(&company)
|
||
|
||
// 深层嵌套访问
|
||
ceoCity := rfx.Get("CEO.Address.City").String()
|
||
if ceoCity != "Shanghai" {
|
||
t.Errorf("Expected 'Shanghai', got '%s'", ceoCity)
|
||
}
|
||
|
||
// 设置深层嵌套值
|
||
rfx.Set("CEO.Address.City", "Guangzhou")
|
||
if company.CEO.Address.City != "Guangzhou" {
|
||
t.Errorf("Expected 'Guangzhou', got '%s'", company.CEO.Address.City)
|
||
}
|
||
|
||
// 检查深层路径存在性
|
||
if !rfx.Exists("CEO.Address.City") {
|
||
t.Error("CEO.Address.City should exist")
|
||
}
|
||
}
|
||
|
||
// TestScopeSetIssue 测试 Scope 深度克隆的特性
|
||
func TestScopeSetIssue(t *testing.T) {
|
||
p := Person{
|
||
Name: "Alice",
|
||
Address: Address{
|
||
City: "Beijing",
|
||
},
|
||
}
|
||
|
||
rfx := New(&p)
|
||
|
||
// 测试直接通过路径设置值 - 这应该可以工作
|
||
t.Run("Direct path set", func(t *testing.T) {
|
||
rfx.Set("Address.City", "Shanghai")
|
||
if p.Address.City != "Shanghai" {
|
||
t.Errorf("Expected 'Shanghai', got '%s'", p.Address.City)
|
||
}
|
||
})
|
||
|
||
// 测试通过 Scope 设置值 - 由于是深度克隆,不会修改原始值
|
||
t.Run("Scope set - deep clone behavior", func(t *testing.T) {
|
||
addressScope := rfx.Scope("Address")
|
||
originalCity := p.Address.City // "Shanghai"
|
||
|
||
// 在 Scope 上设置值
|
||
addressScope.Set("City", "Guangzhou")
|
||
|
||
// 原始值不应该被修改(因为 Scope 返回深度克隆)
|
||
if p.Address.City != originalCity {
|
||
t.Errorf("Original value should remain '%s', got '%s'", originalCity, p.Address.City)
|
||
}
|
||
|
||
// Scope 内的值应该已经改变
|
||
scopeCity := addressScope.Get("City").String()
|
||
if scopeCity != "Guangzhou" {
|
||
t.Errorf("Scope value should be 'Guangzhou', got '%s'", scopeCity)
|
||
}
|
||
})
|
||
|
||
// 测试获取 Scope 的值是独立副本
|
||
t.Run("Scope returns independent copy", func(t *testing.T) {
|
||
// 获取 Address 的 Scope
|
||
addressScope := rfx.Scope("Address")
|
||
|
||
// 获取 Scope 的 Any 值
|
||
addr := addressScope.Any()
|
||
if address, ok := addr.(Address); ok {
|
||
originalCity := p.Address.City
|
||
|
||
// 修改 Scope 返回的值
|
||
addressScope.Set("City", "TestCity")
|
||
|
||
// 原始值不应该被修改
|
||
if p.Address.City != originalCity {
|
||
t.Errorf("Modifying scope should not affect original, expected '%s', got '%s'",
|
||
originalCity, p.Address.City)
|
||
}
|
||
|
||
// 确保 address 变量本身也是副本
|
||
_ = address
|
||
}
|
||
})
|
||
|
||
// 测试嵌套 Scope 的深度克隆
|
||
t.Run("Nested scope deep clone", func(t *testing.T) {
|
||
p2 := Person{
|
||
Name: "Bob",
|
||
Age: 25,
|
||
Address: Address{
|
||
City: "Beijing",
|
||
Street: "Main St",
|
||
ZipCode: 100000,
|
||
},
|
||
}
|
||
|
||
sm2 := New(&p2)
|
||
|
||
// 创建 Scope
|
||
scope := sm2.Scope("Address")
|
||
|
||
// 在 Scope 上修改多个字段
|
||
scope.Set("City", "Shenzhen")
|
||
scope.Set("Street", "New Street")
|
||
scope.Set("ZipCode", 518000)
|
||
|
||
// 验证原始对象没有被修改
|
||
if p2.Address.City != "Beijing" {
|
||
t.Errorf("Original City should be 'Beijing', got '%s'", p2.Address.City)
|
||
}
|
||
if p2.Address.Street != "Main St" {
|
||
t.Errorf("Original Street should be 'Main St', got '%s'", p2.Address.Street)
|
||
}
|
||
if p2.Address.ZipCode != 100000 {
|
||
t.Errorf("Original ZipCode should be 100000, got %d", p2.Address.ZipCode)
|
||
}
|
||
|
||
// 验证 Scope 内的值已改变
|
||
if scope.Get("City").String() != "Shenzhen" {
|
||
t.Errorf("Scope City should be 'Shenzhen', got '%s'", scope.Get("City").String())
|
||
}
|
||
})
|
||
}
|
||
|
||
// TestScopeDeepClone 测试 Scope 深度克隆复杂嵌套结构
|
||
func TestScopeDeepClone(t *testing.T) {
|
||
type Company struct {
|
||
Name string
|
||
Employees []Person
|
||
Locations map[string]Address
|
||
}
|
||
|
||
company := Company{
|
||
Name: "TechCorp",
|
||
Employees: []Person{
|
||
{
|
||
Name: "Alice",
|
||
Age: 30,
|
||
Address: Address{
|
||
City: "Beijing",
|
||
Street: "Main St",
|
||
},
|
||
Tags: []string{"go", "rust"},
|
||
},
|
||
{
|
||
Name: "Bob",
|
||
Age: 25,
|
||
Address: Address{
|
||
City: "Shanghai",
|
||
},
|
||
},
|
||
},
|
||
Locations: map[string]Address{
|
||
"hq": {
|
||
City: "Beijing",
|
||
Street: "Tech Park",
|
||
ZipCode: 100000,
|
||
},
|
||
},
|
||
}
|
||
|
||
rfx := New(&company)
|
||
|
||
t.Run("Clone and modify nested slice", func(t *testing.T) {
|
||
// 创建 Employees 的 Scope
|
||
empScope := rfx.Scope("Employees")
|
||
|
||
// 修改 Scope 中的值
|
||
empScope.Set("0.Name", "Charlie")
|
||
empScope.Set("0.Age", 35)
|
||
|
||
// 验证原始数据没有被修改
|
||
if company.Employees[0].Name != "Alice" {
|
||
t.Errorf("Original employee name should be 'Alice', got '%s'", company.Employees[0].Name)
|
||
}
|
||
if company.Employees[0].Age != 30 {
|
||
t.Errorf("Original employee age should be 30, got %d", company.Employees[0].Age)
|
||
}
|
||
|
||
// 验证 Scope 中的值已修改
|
||
if empScope.Get("0", "Name").String() != "Charlie" {
|
||
t.Error("Scope employee name should be 'Charlie'")
|
||
}
|
||
})
|
||
|
||
t.Run("Clone and modify nested map", func(t *testing.T) {
|
||
// 创建 Locations 的 Scope
|
||
locScope := rfx.Scope("Locations")
|
||
|
||
// 修改 Scope 中的值
|
||
locScope.Set("hq.City", "Shanghai").Set("hq.ZipCode", 200000)
|
||
|
||
// 验证原始数据没有被修改
|
||
if company.Locations["hq"].City != "Beijing" {
|
||
t.Errorf("Original location city should be 'Beijing', got '%s'", company.Locations["hq"].City)
|
||
}
|
||
if company.Locations["hq"].ZipCode != 100000 {
|
||
t.Errorf("Original location zipcode should be 100000, got %d", company.Locations["hq"].ZipCode)
|
||
}
|
||
|
||
// 验证 Scope 中的值已修改
|
||
if locScope.Get("hq", "City").String() != "Shanghai" {
|
||
t.Error("Scope location city should be 'Shanghai'")
|
||
}
|
||
if locScope.Get("hq", "ZipCode").Int() != 200000 {
|
||
t.Error("Scope location zipcode should be 200000")
|
||
}
|
||
})
|
||
|
||
t.Run("Clone nested struct with slices", func(t *testing.T) {
|
||
// 创建第一个员工的 Scope
|
||
emp0Scope := rfx.Scope("Employees", "0")
|
||
|
||
// 修改嵌套的切片
|
||
emp0Scope.Set("Tags.0", "python")
|
||
|
||
// 验证原始数据没有被修改
|
||
if company.Employees[0].Tags[0] != "go" {
|
||
t.Errorf("Original tag should be 'go', got '%s'", company.Employees[0].Tags[0])
|
||
}
|
||
|
||
// 验证 Scope 中的值已修改
|
||
if emp0Scope.Get("Tags", "0").String() != "python" {
|
||
t.Error("Scope tag should be 'python'")
|
||
}
|
||
})
|
||
}
|
||
|
||
// TestSetValueWithCastInStruct 测试在实际结构体中使用 cast 转换
|
||
func TestSetValueWithCastInStruct(t *testing.T) {
|
||
type Config struct {
|
||
Port int
|
||
Host string
|
||
Enabled bool
|
||
Timeout float64
|
||
}
|
||
|
||
config := Config{}
|
||
rfx := New(&config)
|
||
|
||
// 字符串转 int
|
||
rfx.Set("Port", "8080")
|
||
if config.Port != 8080 {
|
||
t.Errorf("Expected Port to be 8080, got %d", config.Port)
|
||
}
|
||
|
||
// int 转 string
|
||
rfx.Set("Host", 12345)
|
||
if config.Host != "12345" {
|
||
t.Errorf("Expected Host to be '12345', got '%s'", config.Host)
|
||
}
|
||
|
||
// 字符串转 bool
|
||
rfx.Set("Enabled", "true")
|
||
if !config.Enabled {
|
||
t.Errorf("Expected Enabled to be true, got %v", config.Enabled)
|
||
}
|
||
|
||
// 字符串转 float
|
||
rfx.Set("Timeout", "30.5")
|
||
if config.Timeout != 30.5 {
|
||
t.Errorf("Expected Timeout to be 30.5, got %f", config.Timeout)
|
||
}
|
||
}
|
||
|
||
// TestSetValueChainWithCast 测试链式调用中的 cast 转换
|
||
func TestSetValueChainWithCast(t *testing.T) {
|
||
type User struct {
|
||
Name string
|
||
Age int
|
||
Active bool
|
||
Score float64
|
||
}
|
||
|
||
user := User{}
|
||
rfx := New(&user)
|
||
|
||
// 链式设置,使用不同类型的值
|
||
rfx.Set("Name", 12345). // int -> string
|
||
Set("Age", "30"). // string -> int
|
||
Set("Active", "1"). // string -> bool
|
||
Set("Score", "95.5") // string -> float64
|
||
|
||
if user.Name != "12345" {
|
||
t.Errorf("Expected Name to be '12345', got '%s'", user.Name)
|
||
}
|
||
if user.Age != 30 {
|
||
t.Errorf("Expected Age to be 30, got %d", user.Age)
|
||
}
|
||
if !user.Active {
|
||
t.Errorf("Expected Active to be true, got %v", user.Active)
|
||
}
|
||
if user.Score != 95.5 {
|
||
t.Errorf("Expected Score to be 95.5, got %f", user.Score)
|
||
}
|
||
}
|
||
|
||
// TestNormalizeValuexAccessor 测试 normalizeInputValue 对 valuex.Accessor 的支持
|
||
func TestNormalizeValuexAccessor(t *testing.T) {
|
||
// 创建一个 R 实例
|
||
data := map[string]any{
|
||
"name": "Alice",
|
||
"age": 30,
|
||
}
|
||
r := New(data)
|
||
|
||
// 测试 valuex.Accessor 类型
|
||
rv, isPtr, err := normalizeInputValue(r)
|
||
if err != nil {
|
||
t.Fatalf("normalizeInputValue failed: %v", err)
|
||
}
|
||
if !isPtr {
|
||
t.Error("Expected isPtr to be true for valuex.Accessor")
|
||
}
|
||
if !rv.IsValid() {
|
||
t.Error("Expected valid reflect.Value")
|
||
}
|
||
}
|
||
|
||
// TestNormalizeValuexAccessorSlice 测试 normalizeInputValue 对 []valuex.Accessor 的支持
|
||
func TestNormalizeValuexAccessorSlice(t *testing.T) {
|
||
// 创建多个 R 实例
|
||
r1 := New(map[string]any{"id": 1, "name": "Alice"})
|
||
r2 := New(map[string]any{"id": 2, "name": "Bob"})
|
||
|
||
// 创建 []valuex.Accessor
|
||
slice := []valuex.Accessor{r1, r2}
|
||
|
||
// 测试 []valuex.Accessor 类型
|
||
rv, isPtr, err := normalizeInputValue(slice)
|
||
if err != nil {
|
||
t.Fatalf("normalizeInputValue failed: %v", err)
|
||
}
|
||
if !isPtr {
|
||
t.Error("Expected isPtr to be true for []valuex.Accessor")
|
||
}
|
||
if !rv.IsValid() {
|
||
t.Error("Expected valid reflect.Value")
|
||
}
|
||
if rv.Kind() != reflect.Slice {
|
||
t.Errorf("Expected Slice kind, got %v", rv.Kind())
|
||
}
|
||
if rv.Len() != 2 {
|
||
t.Errorf("Expected slice length 2, got %d", rv.Len())
|
||
}
|
||
}
|