cast/cast_test.go

264 lines
6.2 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 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.To[string](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 TestSpecialJSON(t *testing.T) {
// 关键测试:特殊 HTML 字符序列化不应被转义
type Content struct {
Text string `json:"text"`
}
c := Content{Text: "<a> & <b>"}
// 标准 json.Marshal 会变成 "<a> \u0026 <b>"
// 我们期望输出原始字符 "<a> & <b>"
js := cast.To[string](c)
expected := `{"text":"<a> & <b>"}`
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.To[string](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.To[string](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.To[string](d)
if !strings.Contains(res, "\"name\"") {
t.Errorf("Value struct ToJSON failed to lowercase key: %s", res)
}
}
func TestToJSON_Nil(t *testing.T) {
// Nil slice should be []
var s []int
if js := cast.To[string](s); js != "[]" {
t.Errorf("Nil slice expected [], got %s", js)
}
// Nil map should be {}
var m map[string]int
if js := cast.To[string](m); js != "{}" {
t.Errorf("Nil map expected {}, got %s", js)
}
// Nil pointer should be null
var p *int
if js := cast.To[string](p); js != "null" {
t.Errorf("Nil pointer expected null, got %s", js)
}
}
func TestToJSONDesensitizeBytes(t *testing.T) {
type User struct {
Name string
Password string
Age int
}
u := User{Name: "Tom", Password: "secret123", Age: 18}
// 测试脱敏功能
b := cast.As(cast.ToJSONDesensitizeBytes(u, []string{"password"}))
js := string(b)
if !strings.Contains(js, `"password":"***"`) {
t.Errorf("Password should be desensitized, got: %s", js)
}
if !strings.Contains(js, `"name":"Tom"`) {
t.Errorf("Name should not be desensitized, got: %s", js)
}
}
func TestFastEncoder_MapAny(t *testing.T) {
data := map[any]any{
"userName": "admin",
123: "val",
}
js := cast.To[string](data)
if !strings.Contains(js, `"123":"val"`) || !strings.Contains(js, `"userName":"admin"`) {
t.Errorf("MapAny encoding failed: %s", js)
}
}
func TestFastEncoder_GojaArray(t *testing.T) {
data := map[any]any{
0: "a",
1: "b",
}
js := cast.To[string](data)
if js != `["a","b"]` {
t.Errorf("Goja array fallback failed: %s", js)
}
}