334 lines
9.2 KiB
Go
334 lines
9.2 KiB
Go
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<string, any>"},
|
|
{"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<string, any>")
|
|
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<SimpleStruct>;", 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)
|
|
})
|
|
}
|