reflux/rfx_test.go
what baad0cadfc feat: 完善 normalizeInputValue 支持 valuex.Accessor 并优化代码
- 在 valuex.Accessor 接口中添加 Raw() 方法
- normalizeInputValue 支持 valuex.Accessor 和 []valuex.Accessor 类型
- 提取 normalizeAccessorSlice 泛型函数消除重复代码
- 使用 switch 语句替代 if-else 链提高可读性
- 添加相关测试用例确保功能正确性
2025-12-16 16:14:46 +08:00

1478 lines
34 KiB
Go
Raw 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 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())
}
}