diff --git a/pkg/module_manager/go_hook/filter_result.go b/pkg/module_manager/go_hook/filter_result.go index ec6b97c10..a0ea7e588 100644 --- a/pkg/module_manager/go_hook/filter_result.go +++ b/pkg/module_manager/go_hook/filter_result.go @@ -36,6 +36,81 @@ func (f *Wrapped) UnmarshalTo(v any) error { return fmt.Errorf("reflect.TypeOf(v): %s", reflect.TypeOf(v)) } + rw := reflect.ValueOf(f.Wrapped) + if rw.Type().AssignableTo(rv.Elem().Type()) { + rv.Elem().Set(rw) + return nil + } + + if rw.Kind() == reflect.Pointer { + if rw.IsNil() { + rv.Elem().Set(reflect.Zero(rv.Elem().Type())) + return nil + } + rw = rw.Elem() + } + + if !rw.Type().AssignableTo(rv.Elem().Type()) { + return ErrUnmarshalToTypesNotMatch + } + + rv.Elem().Set(rw) + return nil +} + +func (f *Wrapped) UnmarshalToWithoutAssignable(v any) error { + if f.Wrapped == nil { + return ErrEmptyWrapped + } + + rv := reflect.ValueOf(v) + if rv.Kind() != reflect.Pointer || rv.IsNil() { + // error replace with "not pointer" + return fmt.Errorf("reflect.TypeOf(v): %s", reflect.TypeOf(v)) + } + + rw := reflect.ValueOf(f.Wrapped) + targetType := rv.Elem().Type() + wrappedType := rw.Type() + + // Handle nil pointer case + if rw.Kind() == reflect.Pointer && rw.IsNil() { + rv.Elem().Set(reflect.Zero(targetType)) + return nil + } + + // Check for exact type match + if wrappedType == targetType { + rv.Elem().Set(rw) + return nil + } + + // Handle pointer to value conversion + if rw.Kind() == reflect.Pointer { + rw = rw.Elem() + wrappedType = rw.Type() + } + + // Check for exact type match after dereferencing + if wrappedType == targetType { + rv.Elem().Set(rw) + return nil + } + + return ErrUnmarshalToTypesNotMatch +} + +func (f *Wrapped) UnmarshalToOld(v any) error { + if f.Wrapped == nil { + return ErrEmptyWrapped + } + + rv := reflect.ValueOf(v) + if rv.Kind() != reflect.Pointer || rv.IsNil() { + // error replace with "not pointer" + return fmt.Errorf("reflect.TypeOf(v): %s", reflect.TypeOf(v)) + } + rw := reflect.ValueOf(f.Wrapped) if rw.Kind() != reflect.Pointer || rw.IsNil() { rv.Elem().Set(rw) diff --git a/pkg/module_manager/go_hook/filter_result_bench_test.go b/pkg/module_manager/go_hook/filter_result_bench_test.go new file mode 100644 index 000000000..6f555fa3f --- /dev/null +++ b/pkg/module_manager/go_hook/filter_result_bench_test.go @@ -0,0 +1,739 @@ +package go_hook_test + +import ( + "testing" + + "github.com/flant/addon-operator/pkg/module_manager/go_hook" +) + +type BenchmarkData struct { + String string + Int int + Float float64 + Bool bool + Slice []int + Map map[string]int + NestedData *BenchmarkData +} + +func createTestData() *BenchmarkData { + return &BenchmarkData{ + String: "benchmark test string with some length to simulate real data", + Int: 42, + Float: 3.14159, + Bool: true, + Slice: []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + Map: map[string]int{ + "key1": 1, + "key2": 2, + "key3": 3, + }, + NestedData: &BenchmarkData{ + String: "nested string", + Int: 123, + Float: 2.71828, + Bool: false, + }, + } +} + +func BenchmarkUnmarshalTo_Int(b *testing.B) { + w := &go_hook.Wrapped{Wrapped: 42} + var target int + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalTo(&target) + } +} + +func BenchmarkUnmarshalToWithoutAssignable_Int(b *testing.B) { + w := &go_hook.Wrapped{Wrapped: 42} + var target int + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToWithoutAssignable(&target) + } +} + +func BenchmarkUnmarshalToOld_Int(b *testing.B) { + w := &go_hook.Wrapped{Wrapped: 42} + var target int + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToOld(&target) + } +} + +func BenchmarkUnmarshalTo_String(b *testing.B) { + w := &go_hook.Wrapped{Wrapped: "benchmark test string with some length"} + var target string + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalTo(&target) + } +} + +func BenchmarkUnmarshalToWithoutAssignable_String(b *testing.B) { + w := &go_hook.Wrapped{Wrapped: "benchmark test string with some length"} + var target string + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToWithoutAssignable(&target) + } +} + +func BenchmarkUnmarshalToOld_String(b *testing.B) { + w := &go_hook.Wrapped{Wrapped: "benchmark test string with some length"} + var target string + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToOld(&target) + } +} + +func BenchmarkUnmarshalTo_Slice(b *testing.B) { + slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + w := &go_hook.Wrapped{Wrapped: slice} + var target []int + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalTo(&target) + } +} + +func BenchmarkUnmarshalToWithoutAssignable_Slice(b *testing.B) { + slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + w := &go_hook.Wrapped{Wrapped: slice} + var target []int + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToWithoutAssignable(&target) + } +} + +func BenchmarkUnmarshalToOld_Slice(b *testing.B) { + slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} + w := &go_hook.Wrapped{Wrapped: slice} + var target []int + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToOld(&target) + } +} + +func BenchmarkUnmarshalTo_Map(b *testing.B) { + m := map[string]int{"key1": 1, "key2": 2, "key3": 3} + w := &go_hook.Wrapped{Wrapped: m} + var target map[string]int + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalTo(&target) + } +} + +func BenchmarkUnmarshalToWithoutAssignable_Map(b *testing.B) { + m := map[string]int{"key1": 1, "key2": 2, "key3": 3} + w := &go_hook.Wrapped{Wrapped: m} + var target map[string]int + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToWithoutAssignable(&target) + } +} + +func BenchmarkUnmarshalToOld_Map(b *testing.B) { + m := map[string]int{"key1": 1, "key2": 2, "key3": 3} + w := &go_hook.Wrapped{Wrapped: m} + var target map[string]int + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToOld(&target) + } +} + +func BenchmarkUnmarshalTo_Struct(b *testing.B) { + data := createTestData() + w := &go_hook.Wrapped{Wrapped: *data} + var target BenchmarkData + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalTo(&target) + } +} + +func BenchmarkUnmarshalToWithoutAssignable_Struct(b *testing.B) { + data := createTestData() + w := &go_hook.Wrapped{Wrapped: *data} + var target BenchmarkData + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToWithoutAssignable(&target) + } +} + +func BenchmarkUnmarshalToOld_Struct(b *testing.B) { + data := createTestData() + w := &go_hook.Wrapped{Wrapped: *data} + var target BenchmarkData + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToOld(&target) + } +} + +func BenchmarkUnmarshalTo_Pointer(b *testing.B) { + data := createTestData() + w := &go_hook.Wrapped{Wrapped: data} + var target *BenchmarkData + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalTo(&target) + } +} + +func BenchmarkUnmarshalToWithoutAssignable_Pointer(b *testing.B) { + data := createTestData() + w := &go_hook.Wrapped{Wrapped: data} + var target *BenchmarkData + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToWithoutAssignable(&target) + } +} + +func BenchmarkUnmarshalToOld_Pointer(b *testing.B) { + data := createTestData() + w := &go_hook.Wrapped{Wrapped: data} + var target *BenchmarkData + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToOld(&target) + } +} + +func BenchmarkUnmarshalTo_PointerToValue(b *testing.B) { + data := createTestData() + w := &go_hook.Wrapped{Wrapped: data} + var target BenchmarkData + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalTo(&target) + } +} + +func BenchmarkUnmarshalToWithoutAssignable_PointerToValue(b *testing.B) { + data := createTestData() + w := &go_hook.Wrapped{Wrapped: data} + var target BenchmarkData + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToWithoutAssignable(&target) + } +} + +func BenchmarkUnmarshalToOld_PointerToValue(b *testing.B) { + data := createTestData() + w := &go_hook.Wrapped{Wrapped: data} + var target BenchmarkData + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToOld(&target) + } +} + +func BenchmarkUnmarshalTo_ErrorCase(b *testing.B) { + w := &go_hook.Wrapped{Wrapped: 42} + var target string + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalTo(&target) + } +} + +func BenchmarkUnmarshalToWithoutAssignable_ErrorCase(b *testing.B) { + w := &go_hook.Wrapped{Wrapped: 42} + var target string + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToWithoutAssignable(&target) + } +} + +func BenchmarkUnmarshalTo_PointerTypeMismatch(b *testing.B) { + val := 42 + w := &go_hook.Wrapped{Wrapped: &val} + var target *string + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalTo(&target) + } +} + +func BenchmarkUnmarshalToWithoutAssignable_PointerTypeMismatch(b *testing.B) { + val := 42 + w := &go_hook.Wrapped{Wrapped: &val} + var target *string + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToWithoutAssignable(&target) + } +} + +func BenchmarkUnmarshalToOld_PointerTypeMismatch(b *testing.B) { + val := 42 + w := &go_hook.Wrapped{Wrapped: &val} + var target *string + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToOld(&target) + } +} + +func BenchmarkUnmarshalTo_DifferentStructs(b *testing.B) { + type StructA struct { + Field int + } + type StructB struct { + Field string + } + + w := &go_hook.Wrapped{Wrapped: StructA{Field: 42}} + var target StructB + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalTo(&target) + } +} + +func BenchmarkUnmarshalToWithoutAssignable_DifferentStructs(b *testing.B) { + type StructA struct { + Field int + } + type StructB struct { + Field string + } + + w := &go_hook.Wrapped{Wrapped: StructA{Field: 42}} + var target StructB + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToWithoutAssignable(&target) + } +} + +func BenchmarkUnmarshalToOld_DifferentStructs(b *testing.B) { + type StructA struct { + Field int + } + type StructB struct { + Field string + } + + w := &go_hook.Wrapped{Wrapped: StructA{Field: 42}} + var target StructB + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToOld(&target) + } +} + +func BenchmarkUnmarshalTo_PointerToDifferentStructs(b *testing.B) { + type StructA struct { + Field int + } + type StructB struct { + Field string + } + + val := StructA{Field: 42} + w := &go_hook.Wrapped{Wrapped: &val} + var target *StructB + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalTo(&target) + } +} + +func BenchmarkUnmarshalToWithoutAssignable_PointerToDifferentStructs(b *testing.B) { + type StructA struct { + Field int + } + type StructB struct { + Field string + } + + val := StructA{Field: 42} + w := &go_hook.Wrapped{Wrapped: &val} + var target *StructB + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToWithoutAssignable(&target) + } +} + +func BenchmarkUnmarshalToOld_PointerToDifferentStructs(b *testing.B) { + type StructA struct { + Field int + } + type StructB struct { + Field string + } + + val := StructA{Field: 42} + w := &go_hook.Wrapped{Wrapped: &val} + var target *StructB + + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToOld(&target) + } +} + +func BenchmarkCompareAllMethods_Int(b *testing.B) { + w := &go_hook.Wrapped{Wrapped: 42} + + b.Run("UnmarshalTo", func(b *testing.B) { + var target int + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalTo(&target) + } + }) + + b.Run("UnmarshalToWithoutAssignable", func(b *testing.B) { + var target int + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToWithoutAssignable(&target) + } + }) + + b.Run("UnmarshalToOld", func(b *testing.B) { + var target int + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToOld(&target) + } + }) +} + +func BenchmarkCompareAllMethods_Struct(b *testing.B) { + data := createTestData() + w := &go_hook.Wrapped{Wrapped: *data} + + b.Run("UnmarshalTo", func(b *testing.B) { + var target BenchmarkData + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalTo(&target) + } + }) + + b.Run("UnmarshalToWithoutAssignable", func(b *testing.B) { + var target BenchmarkData + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToWithoutAssignable(&target) + } + }) + + b.Run("UnmarshalToOld", func(b *testing.B) { + var target BenchmarkData + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToOld(&target) + } + }) +} + +func BenchmarkMemoryUsage_Int(b *testing.B) { + w := &go_hook.Wrapped{Wrapped: 42} + + b.Run("UnmarshalTo", func(b *testing.B) { + b.ReportAllocs() + var target int + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalTo(&target) + } + }) + + b.Run("UnmarshalToWithoutAssignable", func(b *testing.B) { + b.ReportAllocs() + var target int + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToWithoutAssignable(&target) + } + }) + + b.Run("UnmarshalToOld", func(b *testing.B) { + b.ReportAllocs() + var target int + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToOld(&target) + } + }) +} + +func BenchmarkMemoryUsage_Struct(b *testing.B) { + data := createTestData() + w := &go_hook.Wrapped{Wrapped: *data} + + b.Run("UnmarshalTo", func(b *testing.B) { + b.ReportAllocs() + var target BenchmarkData + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalTo(&target) + } + }) + + b.Run("UnmarshalToWithoutAssignable", func(b *testing.B) { + b.ReportAllocs() + var target BenchmarkData + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToWithoutAssignable(&target) + } + }) + + b.Run("UnmarshalToOld", func(b *testing.B) { + b.ReportAllocs() + var target BenchmarkData + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToOld(&target) + } + }) +} + +func BenchmarkErrorVsPanic(b *testing.B) { + b.Run("ErrorCase_UnmarshalTo", func(b *testing.B) { + w := &go_hook.Wrapped{Wrapped: 42} + var target string + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalTo(&target) + } + }) + + b.Run("ErrorCase_UnmarshalToWithoutAssignable", func(b *testing.B) { + w := &go_hook.Wrapped{Wrapped: 42} + var target string + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToWithoutAssignable(&target) + } + }) + + b.Run("ErrorCase_UnmarshalToOld_PointerMismatch", func(b *testing.B) { + val := 42 + w := &go_hook.Wrapped{Wrapped: &val} + var target *string + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToOld(&target) + } + }) + + b.Run("ErrorCase_UnmarshalToOld_PointerToPointerMismatch", func(b *testing.B) { + val := 42 + w := &go_hook.Wrapped{Wrapped: &val} + var target *string + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToOld(&target) + } + }) + + b.Run("ErrorCase_UnmarshalToOld_PointerToDifferentStructs", func(b *testing.B) { + type StructA struct { + Field int + } + type StructB struct { + Field string + } + + val := StructA{Field: 42} + w := &go_hook.Wrapped{Wrapped: &val} + var target *StructB + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToOld(&target) + } + }) +} + +func BenchmarkUnmarshalToOld_ErrorVsPanic(b *testing.B) { + b.Run("ErrorCase_PointerToDifferentStructs", func(b *testing.B) { + type StructA struct { + Field int + } + type StructB struct { + Field string + } + + val := StructA{Field: 42} + w := &go_hook.Wrapped{Wrapped: &val} + var target *StructB + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToOld(&target) + } + }) + + b.Run("ErrorCase_PointerTypeMismatch", func(b *testing.B) { + val := 42 + w := &go_hook.Wrapped{Wrapped: &val} + var target *string + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToOld(&target) + } + }) + + b.Run("ErrorCase_NilPointerToPointer", func(b *testing.B) { + var nilPtr *int + w := &go_hook.Wrapped{Wrapped: nilPtr} + var target *int + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToOld(&target) + } + }) +} + +func BenchmarkNilPointerCases(b *testing.B) { + b.Run("NilPointer_UnmarshalTo", func(b *testing.B) { + var nilPtr *int + w := &go_hook.Wrapped{Wrapped: nilPtr} + var target int + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalTo(&target) + } + }) + + b.Run("NilPointer_UnmarshalToWithoutAssignable", func(b *testing.B) { + var nilPtr *int + w := &go_hook.Wrapped{Wrapped: nilPtr} + var target int + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToWithoutAssignable(&target) + } + }) + + b.Run("NilPointer_UnmarshalToOld", func(b *testing.B) { + var nilPtr *int + w := &go_hook.Wrapped{Wrapped: nilPtr} + var target *int + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToOld(&target) + } + }) +} + +func BenchmarkCompareDirectCast_ValueStruct(b *testing.B) { + data := 10 + var anyVal any = data + + b.Run("DirectCast", func(b *testing.B) { + var target int + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + target = anyVal.(int) + } + _ = target + }) + + b.Run("UnmarshalToOld", func(b *testing.B) { + w := &go_hook.Wrapped{Wrapped: data} + var target int + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToOld(&target) + } + }) + + b.Run("UnmarshalToWithoutAssignable", func(b *testing.B) { + w := &go_hook.Wrapped{Wrapped: data} + var target int + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToWithoutAssignable(&target) + } + }) +} + +func BenchmarkCompareDirectCast_PointerStruct(b *testing.B) { + data := createTestData() + var anyVal any = data + + b.Run("DirectCast", func(b *testing.B) { + var target *BenchmarkData + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + target = anyVal.(*BenchmarkData) + } + _ = target + }) + + b.Run("UnmarshalToOld", func(b *testing.B) { + w := &go_hook.Wrapped{Wrapped: data} + var target BenchmarkData + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToOld(&target) + } + }) + + b.Run("UnmarshalToWithoutAssignable", func(b *testing.B) { + w := &go_hook.Wrapped{Wrapped: data} + var target BenchmarkData + b.ReportAllocs() + b.ResetTimer() + for i := 0; i < b.N; i++ { + _ = w.UnmarshalToWithoutAssignable(&target) + } + }) +} diff --git a/pkg/module_manager/go_hook/filter_result_test.go b/pkg/module_manager/go_hook/filter_result_test.go index b0b7a3bf6..223cba550 100644 --- a/pkg/module_manager/go_hook/filter_result_test.go +++ b/pkg/module_manager/go_hook/filter_result_test.go @@ -1,6 +1,8 @@ package go_hook_test import ( + "bytes" + "io" "testing" "github.com/stretchr/testify/assert" @@ -61,19 +63,203 @@ func Test_FilterResult(t *testing.T) { assert.Error(t, err) }) - t.Run("UnmarshalTo_NilTarget", func(t *testing.T) { + t.Run("UnmarshalTo_IntToFloat64Error", func(t *testing.T) { w := &go_hook.Wrapped{ - Wrapped: &SomeStruct{ - String: "INPUT STRING", + Wrapped: 32, + } + + var target float32 + err := w.UnmarshalTo(&target) + + assert.Error(t, err) + }) + + t.Run("UnmarshalTo_IntToFloat64PtrError", func(t *testing.T) { + w := &go_hook.Wrapped{ + Wrapped: 42, + } + + var target *float64 + err := w.UnmarshalTo(&target) + + assert.Error(t, err) + assert.Nil(t, target) + }) + + t.Run("UnmarshalTo_IntPtrToFloat64Error", func(t *testing.T) { + val := 42 + w := &go_hook.Wrapped{ + Wrapped: &val, + } + + var target float64 + err := w.UnmarshalTo(&target) + + assert.Error(t, err) + }) + + t.Run("UnmarshalTo_CustomIntTypeError", func(t *testing.T) { + type MyInt int + w := &go_hook.Wrapped{ + Wrapped: MyInt(42), + } + + var target int + err := w.UnmarshalTo(&target) + + assert.Error(t, err) + }) + + t.Run("UnmarshalTo_IntToEmptyInterface", func(t *testing.T) { + w := &go_hook.Wrapped{ + Wrapped: 42, + } + + var target interface{} + err := w.UnmarshalTo(&target) + + assert.NoError(t, err) + assert.Equal(t, 42, target) + }) + + t.Run("UnmarshalTo_DifferentStructs", func(t *testing.T) { + type StructA struct{ X int } + type StructB struct{ X int } + + w := &go_hook.Wrapped{ + Wrapped: StructA{X: 42}, + } + + var target StructB + err := w.UnmarshalTo(&target) + + assert.Error(t, err) + }) + + t.Run("UnmarshalTo_SameStructType", func(t *testing.T) { + type Person struct { + Name string + Age int + } + + w := &go_hook.Wrapped{ + Wrapped: Person{Name: "Alice", Age: 30}, + } + + var target Person + err := w.UnmarshalTo(&target) + + assert.NoError(t, err) + assert.Equal(t, "Alice", target.Name) + assert.Equal(t, 30, target.Age) + }) + + t.Run("UnmarshalTo_DifferentStructsWithSameFields", func(t *testing.T) { + type A struct{ X int } + type B struct{ X int } + + w := &go_hook.Wrapped{ + Wrapped: A{X: 42}, + } + + var target B + err := w.UnmarshalTo(&target) + + assert.Error(t, err) + }) + + t.Run("UnmarshalTo_AnonymousField", func(t *testing.T) { + type Base struct { + ID int + } + type Extended struct { + Base + Name string + } + + w := &go_hook.Wrapped{ + Wrapped: Extended{ + Base: Base{ID: 1}, + Name: "Test", }, } - var ss *SomeStruct + var target Extended + err := w.UnmarshalTo(&target) - err := w.UnmarshalTo(ss) + assert.NoError(t, err) + assert.Equal(t, 1, target.ID) + }) + + t.Run("UnmarshalTo_NoInitPointerToStruct", func(t *testing.T) { + type Item struct { + Value string + } + + w := &go_hook.Wrapped{ + Wrapped: &Item{Value: "pointer"}, + } + + var target *Item + err := w.UnmarshalTo(&target) + + assert.NoError(t, err) + assert.Equal(t, "pointer", target.Value) + }) + + t.Run("UnmarshalTo_NotPointerTarget", func(t *testing.T) { + w := &go_hook.Wrapped{Wrapped: 10} + var num int + err := w.UnmarshalTo(num) assert.Error(t, err) }) + t.Run("UnmarshalTo_CommonInterface", func(t *testing.T) { + var buf bytes.Buffer + + w := &go_hook.Wrapped{Wrapped: &buf} + var writer io.Writer + err := w.UnmarshalTo(&writer) + + assert.NoError(t, err) + assert.Equal(t, &buf, writer) + }) + + t.Run("UnmarshalTo_NilPointerWrappedToValue", func(t *testing.T) { + type MyStruct struct{ Field string } + + var nilPtr *MyStruct + w := &go_hook.Wrapped{Wrapped: nilPtr} + + var target MyStruct + err := w.UnmarshalTo(&target) + assert.NoError(t, err) + assert.Equal(t, "", target.Field) + }) + + t.Run("UnmarshalTo_DoublePointerToPointer", func(t *testing.T) { + val := 100 + ptr1 := &val + ptr2 := &ptr1 + w := &go_hook.Wrapped{Wrapped: ptr2} + + var target *int + err := w.UnmarshalTo(&target) + assert.NoError(t, err) + assert.NotNil(t, target) + assert.Equal(t, 100, *target) + }) + + t.Run("String_ComplexStruct", func(t *testing.T) { + type Person struct { + Name string `json:"Name"` + Age int `json:"Age"` + } + w := &go_hook.Wrapped{Wrapped: Person{Name: "John", Age: 25}} + result := w.String() + assert.Equal(t, `{"Name":"John","Age":25}`, result) + }) + t.Run("UnmarshalTo_DifferentTypes", func(t *testing.T) { type OtherStruct struct { Field int @@ -112,6 +298,142 @@ func Test_FilterResult(t *testing.T) { assert.Equal(t, os, w.Wrapped) }) + t.Run("UnmarshalTo_PointerToStruct", func(t *testing.T) { + type SomeStruct struct { + String string + } + + w := &go_hook.Wrapped{ + Wrapped: &SomeStruct{String: "pointer"}, + } + + ss := &SomeStruct{} + err := w.UnmarshalTo(ss) + assert.NoError(t, err) + assert.NotNil(t, ss) + assert.Equal(t, "pointer", ss.String) + }) + + t.Run("UnmarshalTo_Int32ToString", func(t *testing.T) { + w := &go_hook.Wrapped{ + Wrapped: int32(6443), + } + + var portData string + err := w.UnmarshalTo(&portData) + + assert.Error(t, err) + }) + + t.Run("UnmarshalTo_NestedStruct", func(t *testing.T) { + type InnerStruct struct { + Value string + } + type OuterStruct struct { + Inner InnerStruct + } + + w := &go_hook.Wrapped{ + Wrapped: OuterStruct{ + Inner: InnerStruct{Value: "nested"}, + }, + } + + os := OuterStruct{} + err := w.UnmarshalTo(&os) + assert.NoError(t, err) + assert.Equal(t, "nested", os.Inner.Value) + }) + + t.Run("UnmarshalTo_NilTarget", func(t *testing.T) { + w := &go_hook.Wrapped{ + Wrapped: &SomeStruct{ + String: "INPUT STRING", + }, + } + + var ss *SomeStruct + err := w.UnmarshalTo(ss) + assert.Error(t, err) + }) + + t.Run("UnmarshalTo_IncompatibleTypes", func(t *testing.T) { + w := &go_hook.Wrapped{ + Wrapped: "string value", + } + + var num int + err := w.UnmarshalTo(&num) + assert.Error(t, err) + }) + + t.Run("UnmarshalTo_NilPointer", func(t *testing.T) { + var nilPtr *SomeStruct + w := &go_hook.Wrapped{ + Wrapped: nilPtr, + } + + var ss *SomeStruct + err := w.UnmarshalTo(&ss) + assert.NoError(t, err) + assert.Nil(t, ss) + }) + + t.Run("UnmarshalTo_PrimitiveTypes", func(t *testing.T) { + w := &go_hook.Wrapped{ + Wrapped: 42, + } + + var num int + err := w.UnmarshalTo(&num) + assert.NoError(t, err) + assert.Equal(t, 42, num) + }) + + t.Run("UnmarshalTo_Slice", func(t *testing.T) { + w := &go_hook.Wrapped{ + Wrapped: []int{1, 2, 3}, + } + + var slice []int + err := w.UnmarshalTo(&slice) + assert.NoError(t, err) + assert.Equal(t, []int{1, 2, 3}, slice) + }) + + t.Run("UnmarshalTo_Map", func(t *testing.T) { + w := &go_hook.Wrapped{ + Wrapped: map[string]int{"one": 1, "two": 2}, + } + + var m map[string]int + err := w.UnmarshalTo(&m) + assert.NoError(t, err) + assert.Equal(t, map[string]int{"one": 1, "two": 2}, m) + }) + + t.Run("UnmarshalTo_ZeroValue", func(t *testing.T) { + w := &go_hook.Wrapped{ + Wrapped: 0, + } + + var num int + err := w.UnmarshalTo(&num) + assert.NoError(t, err) + assert.Equal(t, 0, num) + }) + + t.Run("UnmarshalTo_UninitializedTarget", func(t *testing.T) { + w := &go_hook.Wrapped{ + Wrapped: "test", + } + + var str string + err := w.UnmarshalTo(&str) + assert.NoError(t, err) + assert.Equal(t, "test", str) + }) + t.Run("AsString", func(t *testing.T) { w := &go_hook.Wrapped{ Wrapped: "test string", @@ -148,3 +470,357 @@ func Test_FilterResult(t *testing.T) { assert.Equal(t, "333", result) }) } + +func Test_FilterResult_UnmarshalToWithoutAssignable(t *testing.T) { + type SomeStruct struct { + String string + } + + t.Run("ExactTypeMatch", func(t *testing.T) { + w := &go_hook.Wrapped{ + Wrapped: &SomeStruct{ + String: "INPUT STRING", + }, + } + + ss := &SomeStruct{} + + err := w.UnmarshalToWithoutAssignable(ss) + assert.NoError(t, err) + + assert.Equal(t, "INPUT STRING", ss.String) + }) + + t.Run("ExactTypeMatch_ValueToValue", func(t *testing.T) { + w := &go_hook.Wrapped{ + Wrapped: SomeStruct{ + String: "INPUT STRING", + }, + } + + ss := SomeStruct{} + + err := w.UnmarshalToWithoutAssignable(&ss) + assert.NoError(t, err) + + assert.Equal(t, "INPUT STRING", ss.String) + }) + + t.Run("ExactTypeMatch_PointerToPointer", func(t *testing.T) { + w := &go_hook.Wrapped{ + Wrapped: &SomeStruct{ + String: "INPUT STRING", + }, + } + + var ss *SomeStruct + + err := w.UnmarshalToWithoutAssignable(&ss) + assert.NoError(t, err) + + assert.Equal(t, "INPUT STRING", ss.String) + }) + + t.Run("ExactTypeMatch_PrimitiveTypes", func(t *testing.T) { + w := &go_hook.Wrapped{ + Wrapped: 42, + } + + var num int + err := w.UnmarshalToWithoutAssignable(&num) + assert.NoError(t, err) + assert.Equal(t, 42, num) + }) + + t.Run("ExactTypeMatch_String", func(t *testing.T) { + w := &go_hook.Wrapped{ + Wrapped: "test string", + } + + var str string + err := w.UnmarshalToWithoutAssignable(&str) + assert.NoError(t, err) + assert.Equal(t, "test string", str) + }) + + t.Run("ExactTypeMatch_Slice", func(t *testing.T) { + w := &go_hook.Wrapped{ + Wrapped: []int{1, 2, 3}, + } + + var slice []int + err := w.UnmarshalToWithoutAssignable(&slice) + assert.NoError(t, err) + assert.Equal(t, []int{1, 2, 3}, slice) + }) + + t.Run("ExactTypeMatch_Map", func(t *testing.T) { + w := &go_hook.Wrapped{ + Wrapped: map[string]int{"one": 1, "two": 2}, + } + + var m map[string]int + err := w.UnmarshalToWithoutAssignable(&m) + assert.NoError(t, err) + assert.Equal(t, map[string]int{"one": 1, "two": 2}, m) + }) + + t.Run("NilPointerWrapped", func(t *testing.T) { + var nilPtr *SomeStruct + w := &go_hook.Wrapped{ + Wrapped: nilPtr, + } + + var ss *SomeStruct + err := w.UnmarshalToWithoutAssignable(&ss) + assert.NoError(t, err) + assert.Nil(t, ss) + }) + + t.Run("NilPointerWrappedToValue", func(t *testing.T) { + var nilPtr *SomeStruct + w := &go_hook.Wrapped{ + Wrapped: nilPtr, + } + + var ss SomeStruct + err := w.UnmarshalToWithoutAssignable(&ss) + assert.NoError(t, err) + assert.Equal(t, "", ss.String) + }) + + t.Run("DifferentTypes_ShouldFail", func(t *testing.T) { + type OtherStruct struct { + Field int + } + + w := &go_hook.Wrapped{ + Wrapped: &SomeStruct{ + String: "INPUT STRING", + }, + } + + os := &OtherStruct{} + + err := w.UnmarshalToWithoutAssignable(os) + assert.Error(t, err) + assert.Equal(t, go_hook.ErrUnmarshalToTypesNotMatch, err) + }) + + t.Run("DifferentPrimitiveTypes_ShouldFail", func(t *testing.T) { + w := &go_hook.Wrapped{ + Wrapped: 42, + } + + var target float64 + err := w.UnmarshalToWithoutAssignable(&target) + assert.Error(t, err) + assert.Equal(t, go_hook.ErrUnmarshalToTypesNotMatch, err) + }) + + t.Run("CustomTypeToBaseType_ShouldFail", func(t *testing.T) { + type MyInt int + w := &go_hook.Wrapped{ + Wrapped: MyInt(42), + } + + var target int + err := w.UnmarshalToWithoutAssignable(&target) + assert.Error(t, err) + assert.Equal(t, go_hook.ErrUnmarshalToTypesNotMatch, err) + }) + + t.Run("BaseTypeToCustomType_ShouldFail", func(t *testing.T) { + type MyInt int + w := &go_hook.Wrapped{ + Wrapped: 42, + } + + var target MyInt + err := w.UnmarshalToWithoutAssignable(&target) + assert.Error(t, err) + assert.Equal(t, go_hook.ErrUnmarshalToTypesNotMatch, err) + }) + + t.Run("DifferentStructsWithSameFields_ShouldFail", func(t *testing.T) { + type A struct{ X int } + type B struct{ X int } + + w := &go_hook.Wrapped{ + Wrapped: A{X: 42}, + } + + var target B + err := w.UnmarshalToWithoutAssignable(&target) + assert.Error(t, err) + assert.Equal(t, go_hook.ErrUnmarshalToTypesNotMatch, err) + }) + + t.Run("Int32ToString_ShouldFail", func(t *testing.T) { + w := &go_hook.Wrapped{ + Wrapped: int32(6443), + } + + var portData string + err := w.UnmarshalToWithoutAssignable(&portData) + assert.Error(t, err) + assert.Equal(t, go_hook.ErrUnmarshalToTypesNotMatch, err) + }) + + t.Run("NilWrapped_ShouldFail", func(t *testing.T) { + w := &go_hook.Wrapped{ + Wrapped: nil, + } + + ss := &SomeStruct{} + + err := w.UnmarshalToWithoutAssignable(ss) + assert.Error(t, err) + assert.Equal(t, go_hook.ErrEmptyWrapped, err) + }) + + t.Run("NotPointerTarget_ShouldFail", func(t *testing.T) { + w := &go_hook.Wrapped{Wrapped: 10} + var num int + err := w.UnmarshalToWithoutAssignable(num) + assert.Error(t, err) + }) + + t.Run("NilTarget_ShouldFail", func(t *testing.T) { + w := &go_hook.Wrapped{ + Wrapped: &SomeStruct{ + String: "INPUT STRING", + }, + } + + var ss *SomeStruct + err := w.UnmarshalToWithoutAssignable(ss) + assert.Error(t, err) + }) + + t.Run("CompareWithOriginalUnmarshalTo", func(t *testing.T) { + type MyInt int + + w := &go_hook.Wrapped{ + Wrapped: MyInt(42), + } + + var target int + + err1 := w.UnmarshalTo(&target) + assert.Error(t, err1) + + err2 := w.UnmarshalToWithoutAssignable(&target) + assert.Error(t, err2) + assert.Equal(t, go_hook.ErrUnmarshalToTypesNotMatch, err2) + }) + + t.Run("CompareUnmarshalToOldAndUnmarshalToWithoutAssignable", func(t *testing.T) { + t.Run("IntToString_ShouldFailInBoth", func(t *testing.T) { + w := &go_hook.Wrapped{ + Wrapped: 42, + } + + var target string + + assert.Panics(t, func() { + w.UnmarshalToOld(&target) + }) + + err2 := w.UnmarshalToWithoutAssignable(&target) + assert.Error(t, err2) + assert.Equal(t, go_hook.ErrUnmarshalToTypesNotMatch, err2) + }) + + t.Run("StringToInt_ShouldFailInBoth", func(t *testing.T) { + w := &go_hook.Wrapped{ + Wrapped: "42", + } + + var target int + + assert.Panics(t, func() { + w.UnmarshalToOld(&target) + }) + + err2 := w.UnmarshalToWithoutAssignable(&target) + assert.Error(t, err2) + assert.Equal(t, go_hook.ErrUnmarshalToTypesNotMatch, err2) + }) + + t.Run("ExactTypeMatch_ShouldWorkInBoth", func(t *testing.T) { + w := &go_hook.Wrapped{ + Wrapped: 42, + } + + var target int + + err1 := w.UnmarshalToOld(&target) + assert.NoError(t, err1) + assert.Equal(t, 42, target) + + target = 0 + + err2 := w.UnmarshalToWithoutAssignable(&target) + assert.NoError(t, err2) + assert.Equal(t, 42, target) + }) + + t.Run("PointerToValue_ShouldWorkInBoth", func(t *testing.T) { + val := 42 + w := &go_hook.Wrapped{ + Wrapped: &val, + } + + var target int + + err1 := w.UnmarshalToOld(&target) + assert.NoError(t, err1) + assert.Equal(t, 42, target) + + target = 0 + + err2 := w.UnmarshalToWithoutAssignable(&target) + assert.NoError(t, err2) + assert.Equal(t, 42, target) + }) + + t.Run("ValueToPointer_ShouldFailInBoth", func(t *testing.T) { + w := &go_hook.Wrapped{ + Wrapped: 42, + } + + var target *int + + assert.Panics(t, func() { + w.UnmarshalToOld(&target) + }) + + err2 := w.UnmarshalToWithoutAssignable(&target) + assert.Error(t, err2) + assert.Equal(t, go_hook.ErrUnmarshalToTypesNotMatch, err2) + }) + + t.Run("StructExactMatch_ShouldWorkInBoth", func(t *testing.T) { + type TestStruct struct { + Field string + } + + w := &go_hook.Wrapped{ + Wrapped: TestStruct{Field: "test"}, + } + + var target TestStruct + + err1 := w.UnmarshalToOld(&target) + assert.NoError(t, err1) + assert.Equal(t, "test", target.Field) + + target = TestStruct{} + + err2 := w.UnmarshalToWithoutAssignable(&target) + assert.NoError(t, err2) + assert.Equal(t, "test", target.Field) + }) + }) +}