package cast_test import ( "strings" "testing" "time" "apigo.cc/go/cast" ) func TestGenerics(t *testing.T) { // Test If if cast.If(true, 1, 2) != 1 { t.Error("If int failed") } if cast.If(false, "A", "B") != "B" { t.Error("If string failed") } // Test In if !cast.In([]int{1, 2, 3}, 2) { t.Error("In int failed") } if cast.In([]string{"A", "B"}, "C") { t.Error("In string negative failed") } } func TestStructToJSON(t *testing.T) { type User struct { Name string Age int } u := User{Name: "Tom", Age: 18} js := cast.MustToJSON(u) // 验证首字母小写逻辑 if !strings.Contains(js, `"name":"Tom"`) || !strings.Contains(js, `"age":18`) { t.Errorf("Struct to JSON auto-lowercase failed: %s", js) } } func TestJSONToStruct(t *testing.T) { type User struct { Name string Age int } data := `{"Name":"Tom","age":18}` var u User cast.UnmarshalJSON(data, &u) if u.Name != "Tom" || u.Age != 18 { t.Error("UnmarshalJSON to struct failed") } } func TestYAMLConversion(t *testing.T) { type Config struct { Port int } c := Config{Port: 8080} yamlStr := cast.MustToYAML(c) if !strings.Contains(yamlStr, "port: 8080") { t.Errorf("YAML conversion failed: %s", yamlStr) } } func TestSpecialJSON(t *testing.T) { // 关键测试:特殊 HTML 字符序列化不应被转义 type Content struct { Text string `json:"text"` } c := Content{Text: " & "} // 标准 json.Marshal 会变成 " \u0026 " // 我们期望输出原始字符 " & " js := cast.MustToJSON(c) expected := `{"text":" & "}` if js != expected { t.Errorf("Special JSON failed.\nExpected: %s\nActual: %s", expected, js) } // 漂亮打印测试 jsP := cast.PrettyToJSON(c) if !strings.Contains(jsP, "&") || !strings.Contains(jsP, "\n") { t.Error("JSONP special content failed") } } func TestComplexJSONType(t *testing.T) { // 测试 map[interface{}]interface{} 这种标准库无法处理的类型 data := map[interface{}]interface{}{ "name": "Tom", 123: "numeric key", "sub": map[interface{}]interface{}{ "ok": true, }, } js := cast.MustToJSON(data) // 期望 123 被转为 "123" 且内容正确 if !strings.Contains(js, `"123":"numeric key"`) || !strings.Contains(js, `"ok":true`) { t.Errorf("Complex JSON type failed: %s", js) } } func TestPointerHelpers(t *testing.T) { s := "hello" if *cast.Ptr(s) != s { t.Error("String Ptr failed") } i := 100 if *cast.Ptr(i) != i { t.Error("Int Ptr failed") } if *cast.Ptr(true) != true || *cast.Ptr(false) != false { t.Error("Bool Ptr failed") } } func TestNameConversions(t *testing.T) { if cast.GetLowerName("UserName") != "userName" { t.Error("GetLowerName failed") } if cast.GetUpperName("userName") != "UserName" { t.Error("GetUpperName failed") } } func TestEmptyValues(t *testing.T) { if cast.Int(nil) != 0 { t.Error("Int(nil) failed") } if cast.String(nil) != "" { t.Error("String(nil) failed") } if cast.Bool(nil) != false { t.Error("Bool(nil) failed") } } // 新增:专门测试 Goja 引擎等导致数组变成 Map[any]any 的补丁逻辑 func TestGojaMapToArrayFallback(t *testing.T) { // 模拟由于 JS 引擎导致的 map[interface{}]interface{} 伪装数组 // key 为 float64 或 int mockGojaArray := map[interface{}]interface{}{ float64(0): "apple", int(1): "banana", "2": "cherry", // 字符串形式的数字也应该被正确转换 } js := cast.MustToJSON(mockGojaArray) // 我们期望它被正确识别并转化为标准的 JSON 数组,且顺序不会乱 expected := `["apple","banana","cherry"]` if js != expected { t.Errorf("Goja Map-to-Array fallback failed.\nExpected: %s\nActual: %s", expected, js) } } // 新增:测试基础类型的转换与默认值 func TestTypeCasting(t *testing.T) { // 测试多种输入向 Int 转换 if cast.Int(12.34) != 12 { t.Error("Float to Int failed") } if cast.Int("100") != 100 { t.Error("String to Int failed") } if cast.Int([]byte("200")) != 200 { t.Error("Bytes to Int failed") } if cast.Int(true) != 1 { t.Error("Bool to Int failed") } // 测试多种输入向 Bool 转换 if !cast.Bool("T") || !cast.Bool("true") || !cast.Bool(1) || !cast.Bool(-1) { t.Error("Truthy to Bool failed") } if cast.Bool("false") || cast.Bool(0) || cast.Bool(nil) { t.Error("Falsy to Bool failed") } // 测试 Duration if cast.Duration("1s") != time.Second { t.Error("String to Duration failed") } if cast.Duration(2000) != 2000 { t.Error("Int to Duration failed") } } // 新增:测试结构体不可寻址时的安全性 (防止 CanSet panic) func TestUnaddressableStruct(t *testing.T) { type Dummy struct { Name string } d := Dummy{Name: "Test"} // 传入值类型而不是指针。如果 makeJSONType 中遗漏了 CanSet,这里会直接 panic defer func() { if r := recover(); r != nil { t.Errorf("ToJSON with value struct panicked! Missing CanSet() check: %v", r) } }() res := cast.MustToJSON(d) if !strings.Contains(res, "\"name\"") { t.Errorf("Value struct ToJSON failed to lowercase key: %s", res) } }