package tsconvert import ( "reflect" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // Test types for conversion type SimpleStruct struct { Name string `json:"name"` Count int `json:"count"` } type NestedStruct struct { ID int `json:"id"` Simple SimpleStruct `json:"simple"` Pointer *SimpleStruct `json:"pointer,omitempty"` } type OptionalFields struct { Required string `json:"required"` Optional *string `json:"optional,omitempty"` Number int `json:"number,omitempty"` } type ComplexTypes struct { Strings []string `json:"strings"` Numbers []int `json:"numbers"` Mapping map[string]any `json:"mapping"` Nested []SimpleStruct `json:"nested"` } func TestConvertType(t *testing.T) { tests := []struct { name string input any expected string }{ {"string", "", "string"}, {"int", int(0), "number"}, {"int8", int8(0), "number"}, {"int16", int16(0), "number"}, {"int32", int32(0), "number"}, {"int64", int64(0), "number"}, {"uint", uint(0), "number"}, {"float32", float32(0), "number"}, {"float64", float64(0), "number"}, {"bool", true, "boolean"}, {"interface", (*any)(nil), "any | null"}, {"slice of strings", []string{}, "string[]"}, {"slice of ints", []int{}, "number[]"}, {"map", map[string]any{}, "Record"}, {"struct", SimpleStruct{}, "SimpleStruct"}, {"pointer to string", (*string)(nil), "string | null"}, {"pointer to struct", (*SimpleStruct)(nil), "SimpleStruct | null"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { // Get Input Type var inputType reflect.Type if tt.input == nil { inputType = reflect.TypeOf((*any)(nil)).Elem() } else { inputType = reflect.TypeOf(tt.input) } // Convert Type result := ConvertType(inputType) // Verify Result assert.Equal(t, tt.expected, result) }) } } func TestCollectTypes(t *testing.T) { t.Run("simple struct", func(t *testing.T) { // Collect Types argsType := reflect.TypeOf(SimpleStruct{}) fnType := reflect.TypeOf(func() (SimpleStruct, error) { return SimpleStruct{}, nil }) ts := CollectTypes(argsType, fnType) // Verify TypeSet require.NotNil(t, ts) assert.Len(t, ts.All(), 1) // Verify SimpleStruct Definition def, ok := ts.Get("SimpleStruct") assert.True(t, ok) assert.Contains(t, def, "interface SimpleStruct") assert.Contains(t, def, "name: string") assert.Contains(t, def, "count: number") }) t.Run("nested struct", func(t *testing.T) { // Collect Types argsType := reflect.TypeOf(NestedStruct{}) fnType := reflect.TypeOf(func() (NestedStruct, error) { return NestedStruct{}, nil }) ts := CollectTypes(argsType, fnType) // Verify TypeSet require.NotNil(t, ts) all := ts.All() assert.Len(t, all, 2) // NestedStruct and SimpleStruct // Verify NestedStruct Definition def, ok := ts.Get("NestedStruct") assert.True(t, ok) assert.Contains(t, def, "interface NestedStruct") assert.Contains(t, def, "id: number") assert.Contains(t, def, "simple: SimpleStruct") assert.Contains(t, def, "pointer?: SimpleStruct | null") // Verify SimpleStruct is Also Included _, ok = ts.Get("SimpleStruct") assert.True(t, ok) }) t.Run("optional fields", func(t *testing.T) { // Collect Types argsType := reflect.TypeOf(OptionalFields{}) fnType := reflect.TypeOf(func() (OptionalFields, error) { return OptionalFields{}, nil }) ts := CollectTypes(argsType, fnType) // Verify TypeSet require.NotNil(t, ts) // Verify OptionalFields Definition def, ok := ts.Get("OptionalFields") assert.True(t, ok) assert.Contains(t, def, "required: string") assert.Contains(t, def, "optional?: string | null") assert.Contains(t, def, "number?: number") }) t.Run("complex types", func(t *testing.T) { // Collect Types argsType := reflect.TypeOf(ComplexTypes{}) fnType := reflect.TypeOf(func() (ComplexTypes, error) { return ComplexTypes{}, nil }) ts := CollectTypes(argsType, fnType) // Verify TypeSet require.NotNil(t, ts) // Verify ComplexTypes Definition def, ok := ts.Get("ComplexTypes") assert.True(t, ok) assert.Contains(t, def, "strings: string[]") assert.Contains(t, def, "numbers: number[]") assert.Contains(t, def, "mapping: Record") assert.Contains(t, def, "nested: SimpleStruct[]") }) t.Run("no return type", func(t *testing.T) { // Collect Types argsType := reflect.TypeOf(SimpleStruct{}) fnType := reflect.TypeOf(func() error { return nil }) ts := CollectTypes(argsType, fnType) // Verify TypeSet - Only SimpleStruct from args require.NotNil(t, ts) assert.Len(t, ts.All(), 1) }) } func TestTypeSet(t *testing.T) { t.Run("add and get", func(t *testing.T) { // Create TypeSet ts := NewTypeSet() // Add Type err := ts.Add("User", "interface User { name: string }") require.NoError(t, err) // Verify Type def, ok := ts.Get("User") assert.True(t, ok) assert.Equal(t, "interface User { name: string }", def) }) t.Run("duplicate same definition", func(t *testing.T) { // Create TypeSet ts := NewTypeSet() // Add Type err := ts.Add("User", "interface User { name: string }") require.NoError(t, err) // Add Same Type with Same Definition - Should Not Error err = ts.Add("User", "interface User { name: string }") require.NoError(t, err) // Verify Count assert.Len(t, ts.All(), 1) }) t.Run("conflicting definitions", func(t *testing.T) { // Create TypeSet ts := NewTypeSet() // Add Type err := ts.Add("User", "interface User { name: string }") require.NoError(t, err) // Add Same Type with Different Definition - Should Error err = ts.Add("User", "interface User { id: number }") require.Error(t, err) assert.Contains(t, err.Error(), "type conflict") assert.Contains(t, err.Error(), "User") }) t.Run("merge type sets", func(t *testing.T) { // Create First TypeSet ts1 := NewTypeSet() err := ts1.Add("User", "interface User { name: string }") require.NoError(t, err) // Create Second TypeSet ts2 := NewTypeSet() err = ts2.Add("Post", "interface Post { title: string }") require.NoError(t, err) // Merge TypeSets err = ts1.Merge(ts2) require.NoError(t, err) // Verify Merged Types assert.Len(t, ts1.All(), 2) _, ok := ts1.Get("User") assert.True(t, ok) _, ok = ts1.Get("Post") assert.True(t, ok) }) t.Run("merge with conflict", func(t *testing.T) { // Create First TypeSet ts1 := NewTypeSet() err := ts1.Add("User", "interface User { name: string }") require.NoError(t, err) // Create Second TypeSet with Conflicting Type ts2 := NewTypeSet() err = ts2.Add("User", "interface User { id: number }") require.NoError(t, err) // Merge Should Fail Due to Conflict err = ts1.Merge(ts2) require.Error(t, err) assert.Contains(t, err.Error(), "type conflict") }) } func TestExtractName(t *testing.T) { tests := []struct { definition string expected string }{ {"interface User { name: string }", "User"}, {"interface MyType { }", "MyType"}, {"type MyAlias = string", "MyAlias"}, {"type ComplexType = { a: number }", "ComplexType"}, {"invalid syntax here", ""}, {"", ""}, } for _, tt := range tests { t.Run(tt.definition, func(t *testing.T) { // Extract Name result := ExtractName(tt.definition) // Verify Result assert.Equal(t, tt.expected, result) }) } } func TestGenerateFunctionDecl(t *testing.T) { t.Run("simple function", func(t *testing.T) { // Generate Declaration argsType := reflect.TypeOf(SimpleStruct{}) fnType := reflect.TypeOf(func() (SimpleStruct, error) { return SimpleStruct{}, nil }) decl := GenerateFunctionDecl("myFunc", argsType, fnType, false) // Verify Declaration assert.Equal(t, "declare function myFunc(name: string, count: number): SimpleStruct;", decl) }) t.Run("async function", func(t *testing.T) { // Generate Declaration argsType := reflect.TypeOf(SimpleStruct{}) fnType := reflect.TypeOf(func() (SimpleStruct, error) { return SimpleStruct{}, nil }) decl := GenerateFunctionDecl("myAsyncFunc", argsType, fnType, true) // Verify Declaration assert.Equal(t, "declare function myAsyncFunc(name: string, count: number): Promise;", decl) }) t.Run("function with optional fields", func(t *testing.T) { // Generate Declaration argsType := reflect.TypeOf(OptionalFields{}) fnType := reflect.TypeOf(func() (OptionalFields, error) { return OptionalFields{}, nil }) decl := GenerateFunctionDecl("optionalFunc", argsType, fnType, false) // Verify Declaration assert.Contains(t, decl, "required: string") assert.Contains(t, decl, "optional?: string | null") assert.Contains(t, decl, "number?: number") }) t.Run("function with no return", func(t *testing.T) { // Generate Declaration argsType := reflect.TypeOf(SimpleStruct{}) fnType := reflect.TypeOf(func() error { return nil }) decl := GenerateFunctionDecl("noReturn", argsType, fnType, false) // Verify Declaration assert.Equal(t, "declare function noReturn(name: string, count: number): any;", decl) }) t.Run("non-struct args returns empty", func(t *testing.T) { // Generate Declaration argsType := reflect.TypeOf("") fnType := reflect.TypeOf(func() error { return nil }) decl := GenerateFunctionDecl("invalid", argsType, fnType, false) // Verify Declaration assert.Equal(t, "", decl) }) }