From ea8660bad9aeba7f66d1a5c0ae5e6c051f6303ec Mon Sep 17 00:00:00 2001 From: Danis Kirasirov <dbgbbu@gmail.com> Date: Tue, 18 Mar 2025 14:46:21 +0300 Subject: [PATCH] add tests --- pkg/schema/field/array.go | 20 +++------- pkg/schema/field/array_test.go | 71 +++++++++++++++++++++++++++++++++ pkg/schema/field/encode.go | 6 ++- pkg/schema/field/number_test.go | 30 ++++++++++++++ pkg/schema/field/string_test.go | 29 ++++++++++++++ pkg/schema/field/walk.go | 12 +++++- 6 files changed, 152 insertions(+), 16 deletions(-) diff --git a/pkg/schema/field/array.go b/pkg/schema/field/array.go index 1dc44679..cc3a903d 100644 --- a/pkg/schema/field/array.go +++ b/pkg/schema/field/array.go @@ -120,19 +120,6 @@ func (ArrayType) IsEmpty(v interface{}) bool { // return nil //} -func (ArrayType) NonStrictConverter(_ context.Context, _ *Field, v interface{}) interface{} { - if v == nil { - return nil - } - - // Если в данных хранится не слайс или массив, преобразуем в слайс из одного элемента - if arr := reflect.ValueOf(v); arr.Kind() != reflect.Slice && arr.Kind() != reflect.Array { - return []interface{}{v} - } - - return v -} - func (ArrayType) Walk(ctx context.Context, field *Field, v interface{}, fn WalkFunc, opts *WalkOptions) (interface{}, bool, error) { var changed bool params, ok := field.Params.(*ArrayParameters) @@ -147,7 +134,12 @@ func (ArrayType) Walk(ctx context.Context, field *Field, v interface{}, fn WalkF arr := reflect.ValueOf(v) if v != nil && arr.Kind() != reflect.Slice && arr.Kind() != reflect.Array { - return nil, false, fmt.Errorf("incorrect type: %s, expected array or slice", arr.Kind()) + if !opts.WalkNonStrict { + return nil, false, fmt.Errorf("incorrect type: %s, expected array or slice", arr.Kind()) + } + + // При использовании опции WalkNonStrict преобразовываем в слайс + arr = reflect.ValueOf([]interface{}{v}) } var length int diff --git a/pkg/schema/field/array_test.go b/pkg/schema/field/array_test.go index ab8eae74..5685aa4a 100644 --- a/pkg/schema/field/array_test.go +++ b/pkg/schema/field/array_test.go @@ -67,6 +67,77 @@ func TestArrayField_Decode(t *testing.T) { } } +func TestArrayField_DecodeNonStrict(t *testing.T) { + tests := []struct { + name string + field *Field + data interface{} + want interface{} + wantErr bool + }{ + { + "Nil data", + Array(Number(NumberFormatFloat)), + nil, + nil, + false, + }, + { + "Correct data", + Array(Number(NumberFormatFloat)), + []interface{}{1.0, 2.0}, + []interface{}{1.0, 2.0}, + false, + }, + { + "String as float", + Array(Number(NumberFormatFloat)), + []interface{}{"1.0", "2.0"}, + []interface{}{1.0, 2.0}, + false, + }, + { + "String as int", + Array(Number("int")), + []interface{}{"1", "2"}, + []interface{}{int64(1), int64(2)}, + false, + }, + { + "String as array", + Array(String()), + "string", + []interface{}{"string"}, + false, + }, + { + "Single string number", + Array(Number("int")), + "2", + []interface{}{int64(2)}, + false, + }, + { + "Incorrect data type", + Array(Number(NumberFormatInt)), + "1 2 3", + nil, + true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := Decode(context.Background(), tt.field, tt.data, NonStrictMode()) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + assert.ElementsMatch(t, got, tt.want, "Decode() got = %v, want %v", got, tt.want) + }) + } +} + func TestArrayField_Encode(t *testing.T) { tests := []struct { name string diff --git a/pkg/schema/field/encode.go b/pkg/schema/field/encode.go index 4fedee2b..0ece800f 100644 --- a/pkg/schema/field/encode.go +++ b/pkg/schema/field/encode.go @@ -42,6 +42,10 @@ func NewEncodeOptions(opt ...EncodeOption) *EncodeOptions { func Decode(ctx context.Context, w Walker, v interface{}, opts ...EncodeOption) (interface{}, error) { opt := NewEncodeOptions(opts...) + var walkOpts []WalkOption + if opt.NonStrictMode { + walkOpts = append(walkOpts, WalkNonStrict()) + } val, _, err := w.Walk(ctx, v, func(ctx context.Context, f *Field, v interface{}) (res WalkFuncResult, err error) { if opt.NonStrictMode { @@ -60,7 +64,7 @@ func Decode(ctx context.Context, w Walker, v interface{}, opts ...EncodeOption) } res.Value = v return - }) + }, walkOpts...) if err != nil { return nil, errors.Wrap(err, "decode error") diff --git a/pkg/schema/field/number_test.go b/pkg/schema/field/number_test.go index 26d76f95..43da0842 100644 --- a/pkg/schema/field/number_test.go +++ b/pkg/schema/field/number_test.go @@ -100,6 +100,36 @@ func TestNumberField_Encode(t *testing.T) { } } +func TestNumberField_DecodeNonStrict(t *testing.T) { + tests := []struct { + name string + field *Field + data interface{} + want interface{} + wantErr bool + }{ + {"Nil data", Number(NumberFormatInt), nil, nil, false}, + {"Correct int", Number(NumberFormatInt), int64(2), int64(2), false}, + {"Correct float", Number(NumberFormatFloat), 2.2, 2.2, false}, + {"String as float", Number(NumberFormatFloat), "2.2", 2.2, false}, + {"String as int", Number(NumberFormatInt), "2", int64(2), false}, + {"Incorrect data", Number(NumberFormatInt), "invalid", nil, true}, + {"Incorrect data type", Number(NumberFormatInt), []byte("invalid"), nil, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := Decode(context.Background(), tt.field, tt.data, NonStrictMode()) + if (err != nil) != tt.wantErr { + t.Errorf("Decode() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Decode() got = %v, want %v", got, tt.want) + } + }) + } +} + // Добавлен полные функции, чтобы результаты теста не поменялись, в случае изменения Decode // Реализация cast type. func toNumber(i interface{}) (interface{}, error) { diff --git a/pkg/schema/field/string_test.go b/pkg/schema/field/string_test.go index c858f607..38ce350d 100644 --- a/pkg/schema/field/string_test.go +++ b/pkg/schema/field/string_test.go @@ -36,6 +36,35 @@ func TestStringField_Decode(t *testing.T) { } } +func TestStringField_DecodeNonStrict(t *testing.T) { + tests := []struct { + name string + field *Field + data interface{} + want interface{} + wantErr bool + }{ + {"Correct", String(), "string", "string", false}, + {"Nil", String(), nil, nil, false}, + {"Int", String(), 42, "42", false}, + {"Float", String(), 3.14, "3.14", false}, + {"Bool", String(), true, "true", false}, + {"Array", String(), []interface{}{1, "2"}, "[1 2]", false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := Decode(context.Background(), tt.field, tt.data, NonStrictMode()) + if (err != nil) != tt.wantErr { + t.Errorf("Decode() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("Decode() got = %v, want %v", got, tt.want) + } + }) + } +} + func TestStringField_Encode(t *testing.T) { tests := []struct { name string diff --git a/pkg/schema/field/walk.go b/pkg/schema/field/walk.go index d7b24a10..d394c64a 100644 --- a/pkg/schema/field/walk.go +++ b/pkg/schema/field/walk.go @@ -22,7 +22,8 @@ type FieldWalker interface { } type WalkOptions struct { - WalkSchema bool + WalkSchema bool + WalkNonStrict bool } type WalkOption func(opts *WalkOptions) @@ -36,6 +37,15 @@ func WalkSchema() WalkOption { } } +// WalkNonStrict указывает что обход может выполняться в нестрогом режиме. +// При обходе по данным в случае несоответствия их типа будет выполнена +// попытка преобразования типа с использованием NonStrictConverter. +func WalkNonStrict() WalkOption { + return func(opts *WalkOptions) { + opts.WalkNonStrict = true + } +} + func WalkOpts(o *WalkOptions) WalkOption { return func(opts *WalkOptions) { *opts = *o -- GitLab