1+ package qjs_test
2+
3+ import (
4+ "reflect"
5+ "testing"
6+
7+ "github.com/fastschema/qjs"
8+ "github.com/stretchr/testify/assert"
9+ )
10+
11+ func TestIsConvertibleToJs (t * testing.T ) {
12+ t .Run ("BasicTypes" , func (t * testing.T ) {
13+ basicTypes := []struct {
14+ name string
15+ value any
16+ expected bool
17+ }{
18+ {"int" , 42 , true },
19+ {"string" , "hello" , true },
20+ {"bool" , true , true },
21+ {"float64" , 3.14 , true },
22+ {"[]int" , []int {1 , 2 , 3 }, true },
23+ {"map[string]int" , map [string ]int {"key" : 1 }, true },
24+ }
25+
26+ for _ , tt := range basicTypes {
27+ t .Run (tt .name , func (t * testing.T ) {
28+ err := qjs .IsConvertibleToJs (reflect .TypeOf (tt .value ), make (map [reflect.Type ]bool ), "test" )
29+ if tt .expected {
30+ assert .NoError (t , err , "Expected %s to be convertible" , tt .name )
31+ } else {
32+ assert .Error (t , err , "Expected %s to not be convertible" , tt .name )
33+ }
34+ })
35+ }
36+ })
37+
38+ t .Run ("UnsupportedTypes" , func (t * testing.T ) {
39+ t .Run ("channel" , func (t * testing.T ) {
40+ err := qjs .IsConvertibleToJs (reflect .TypeOf (make (chan int )), make (map [reflect.Type ]bool ), "test" )
41+ assert .Error (t , err , "Channel should not be convertible" )
42+ assert .Contains (t , err .Error (), "channel" , "Error should mention channel" )
43+ })
44+
45+ // Note: Testing unsafe.Pointer directly is tricky in unit tests since (*int)(nil)
46+ // is treated as *int, not unsafe.Pointer. The unsafe.Pointer case is handled
47+ // in the switch statement but requires actual unsafe.Pointer type to trigger.
48+ })
49+
50+ t .Run ("StructWithFields" , func (t * testing.T ) {
51+ t .Run ("ExportedFields" , func (t * testing.T ) {
52+ type TestStruct struct {
53+ PublicField string
54+ AnotherField int
55+ }
56+
57+ err := qjs .IsConvertibleToJs (reflect .TypeOf (TestStruct {}), make (map [reflect.Type ]bool ), "test" )
58+ assert .NoError (t , err , "Struct with exported fields should be convertible" )
59+ })
60+
61+ t .Run ("UnexportedFieldsIgnored" , func (t * testing.T ) {
62+ type TestStruct struct {
63+ PublicField string
64+ privateField string // Should be ignored
65+ }
66+
67+ err := qjs .IsConvertibleToJs (reflect .TypeOf (TestStruct {}), make (map [reflect.Type ]bool ), "test" )
68+ assert .NoError (t , err , "Struct with unexported fields should be convertible (unexported fields ignored)" )
69+ })
70+
71+ t .Run ("MixedFieldsWithUnsupportedPrivate" , func (t * testing.T ) {
72+ type TestStruct struct {
73+ PublicField string
74+ privateField chan int // Unsupported type but private, should be ignored
75+ }
76+
77+ err := qjs .IsConvertibleToJs (reflect .TypeOf (TestStruct {}), make (map [reflect.Type ]bool ), "test" )
78+ assert .NoError (t , err , "Struct should be convertible when unsupported types are in unexported fields" )
79+ })
80+ })
81+
82+ t .Run ("StructWithJSONTags" , func (t * testing.T ) {
83+ t .Run ("JSONOmitTag" , func (t * testing.T ) {
84+ type TestStruct struct {
85+ PublicField string
86+ OmittedField chan int `json:"-"` // Should be ignored due to json:"-"
87+ AnotherField int
88+ }
89+
90+ err := qjs .IsConvertibleToJs (reflect .TypeOf (TestStruct {}), make (map [reflect.Type ]bool ), "test" )
91+ assert .NoError (t , err , "Struct should be convertible when unsupported types have json:\" -\" tag" )
92+ })
93+
94+ t .Run ("JSONRenameTag" , func (t * testing.T ) {
95+ type TestStruct struct {
96+ Field string `json:"renamed_field"`
97+ UnsupportedField chan int `json:"-"`
98+ }
99+
100+ err := qjs .IsConvertibleToJs (reflect .TypeOf (TestStruct {}), make (map [reflect.Type ]bool ), "test" )
101+ assert .NoError (t , err , "Struct should be convertible with JSON rename tags" )
102+ })
103+
104+ t .Run ("UnsupportedFieldWithoutOmitTag" , func (t * testing.T ) {
105+ type TestStruct struct {
106+ PublicField string
107+ UnsupportedField chan int // Should cause error since it's exported and not omitted
108+ }
109+
110+ err := qjs .IsConvertibleToJs (reflect .TypeOf (TestStruct {}), make (map [reflect.Type ]bool ), "test" )
111+ assert .Error (t , err , "Struct should not be convertible with exported unsupported fields" )
112+ assert .Contains (t , err .Error (), "TestStruct.UnsupportedField" , "Error should mention the problematic field" )
113+ })
114+
115+ t .Run ("ComplexJSONTags" , func (t * testing.T ) {
116+ type TestStruct struct {
117+ Field1 string `json:"field1,omitempty"`
118+ Field2 int `json:"field2"`
119+ OmittedField chan int `json:"-"`
120+ AnotherOmitted chan bool `json:"-,"`
121+ }
122+
123+ err := qjs .IsConvertibleToJs (reflect .TypeOf (TestStruct {}), make (map [reflect.Type ]bool ), "test" )
124+ assert .NoError (t , err , "Struct should handle complex JSON tags correctly" )
125+ })
126+ })
127+
128+ t .Run ("RecursiveTypes" , func (t * testing.T ) {
129+ type RecursiveStruct struct {
130+ Name string
131+ Self * RecursiveStruct
132+ }
133+
134+ err := qjs .IsConvertibleToJs (reflect .TypeOf (RecursiveStruct {}), make (map [reflect.Type ]bool ), "test" )
135+ assert .NoError (t , err , "Recursive struct should be convertible" )
136+ })
137+
138+ t .Run ("NestedStructs" , func (t * testing.T ) {
139+ t .Run ("ValidNested" , func (t * testing.T ) {
140+ type Inner struct {
141+ Value int
142+ }
143+ type Outer struct {
144+ Inner Inner
145+ Name string
146+ }
147+
148+ err := qjs .IsConvertibleToJs (reflect .TypeOf (Outer {}), make (map [reflect.Type ]bool ), "test" )
149+ assert .NoError (t , err , "Nested convertible structs should be convertible" )
150+ })
151+
152+ t .Run ("InvalidNested" , func (t * testing.T ) {
153+ type Inner struct {
154+ BadField chan int // Unsupported
155+ }
156+ type Outer struct {
157+ Inner Inner
158+ Name string
159+ }
160+
161+ err := qjs .IsConvertibleToJs (reflect .TypeOf (Outer {}), make (map [reflect.Type ]bool ), "test" )
162+ assert .Error (t , err , "Nested struct with unsupported fields should not be convertible" )
163+ })
164+
165+ t .Run ("NestedWithOmittedField" , func (t * testing.T ) {
166+ type Inner struct {
167+ GoodField string
168+ BadField chan int `json:"-"`
169+ }
170+ type Outer struct {
171+ Inner Inner
172+ Name string
173+ }
174+
175+ err := qjs .IsConvertibleToJs (reflect .TypeOf (Outer {}), make (map [reflect.Type ]bool ), "test" )
176+ assert .NoError (t , err , "Nested struct with omitted unsupported fields should be convertible" )
177+ })
178+ })
179+
180+ t .Run ("Pointers" , func (t * testing.T ) {
181+ type TestStruct struct {
182+ Field string
183+ OmittedField chan int `json:"-"`
184+ }
185+
186+ err := qjs .IsConvertibleToJs (reflect .TypeOf (& TestStruct {}), make (map [reflect.Type ]bool ), "test" )
187+ assert .NoError (t , err , "Pointer to convertible struct should be convertible" )
188+ })
189+ }
0 commit comments