Skip to content
Snippets Groups Projects
array_test.go 9.56 KiB
package field

import (
	"context"
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
)

func TestArrayField_Decode(t *testing.T) {
	tests := []struct {
		name    string
		field   *Field
		data    interface{}
		want    interface{}
		wantErr bool
	}{
		{
			"Correct",
			Array(Number("float")),
			[]interface{}{1.0, 2.0},
			[]interface{}{1.0, 2.0},
			false,
		},
		{
			"With object inside with nil-data",
			Array(
				Object("a", String(),
					"b", String()),
			),
			nil,
			nil,
			false,
		},
		{
			"With object inside with data",
			Array(
				Object("a", String(),
					"b", String()),
			),
			[]interface{}{map[string]interface{}{"a": "1", "b": "2"}},
			[]interface{}{map[string]interface{}{"a": "1", "b": "2"}},
			false,
		},
		{
			"Incorrect type",
			Array(Number("int")),
			"1 2 3",
			"decode error: incorrect type: string, expected array or slice",
			true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := Decode(context.Background(), tt.field, tt.data)
			if tt.wantErr {
				require.Error(t, err)
				assert.EqualError(t, err, tt.want.(string), fmt.Sprintf("Decode() error = %v, want %v", err, tt.want.(string)))
			}
			if !tt.wantErr {
				require.NoError(t, err)
				assert.ElementsMatch(t, got, tt.want, fmt.Sprintf("Decode() got = %v, want %v", got, tt.want))
			}
		})
	}
}

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,
		},
		{
			"String array",
			Array(Number(NumberFormatFloat)),
			"1 2 3.5",
			[]interface{}{1.0, 2.0, 3.5},
			false,
		},
		{
			"Incoorect type",
			Array(Bool()),
			"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
		field   *Field
		data    interface{}
		want    interface{}
		wantErr bool
	}{
		{
			"Correct",
			Array(Number("float")),
			[]interface{}{1.0, 2.0},
			[]interface{}{1.0, 2.0},
			false,
		},
		{
			"Incorrect type",
			Array(Number("int")),
			"1 2 3",
			"encode error: incorrect type: string, expected array or slice",
			true,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := Encode(context.Background(), tt.field, tt.data)
			if tt.wantErr {
				require.Error(t, err)
				assert.EqualError(t, err, tt.want.(string), fmt.Sprintf("Decode() error = %v, want %v", err, tt.want.(string)))
			}
			if !tt.wantErr {
				require.NoError(t, err)
				assert.ElementsMatch(t, got, tt.want, fmt.Sprintf("Decode() got = %v, want %v", got, tt.want))
			}
		})
	}
}

func TestArrayType_Walk(t *testing.T) {
	tests := []struct {
		name    string
		field   *Field
		v       interface{}
		fn      WalkFunc
		opts    *WalkOptions
		want    interface{}
		want1   bool
		wantErr assert.ErrorAssertionFunc
	}{
		{
			name:    "With nil data and WalkSchema = false",
			field:   Array(Object("a", String(), "b", String())),
			v:       nil,
			opts:    &WalkOptions{WalkSchema: false},
			want:    nil,
			want1:   false,
			wantErr: assert.NoError,
		},
		{
			name:  "With nil data and WalkSchema = true",
			field: Array(Object("a", String(), "b", String())),
			v:     nil,
			opts:  &WalkOptions{WalkSchema: true},
			want:  nil,
			want1: false,
			fn: func(_ context.Context, _ *Field, _ interface{}) (WalkFuncResult, error) {
				return WalkFuncResult{}, nil
			},
			wantErr: assert.NoError,
		},
		{
			name:  "With empty data and WalkSchema = false",
			field: Array(Object("a", String(), "b", String())),
			v:     []interface{}{map[string]interface{}{}},
			opts:  &WalkOptions{WalkSchema: false},
			fn: func(ctx context.Context, fld *Field, v interface{}) (result WalkFuncResult, err error) {
				return WalkFuncResult{}, err
			},
			want:    []interface{}{map[string]interface{}{}},
			want1:   false,
			wantErr: assert.NoError,
		},
		{
			name:  "With data and WalkSchema = false",
			field: Array(Object("a", String(), "b", String())),
			v:     []interface{}{map[string]interface{}{"a": "1", "b": "2"}},
			opts:  &WalkOptions{WalkSchema: false},
			fn: func(ctx context.Context, fld *Field, v interface{}) (result WalkFuncResult, err error) {
				return WalkFuncResult{}, err
			},
			want:    []interface{}{map[string]interface{}{"a": "1", "b": "2"}},
			want1:   false,
			wantErr: assert.NoError,
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			ar := ArrayType{}
			got, got1, err := ar.Walk(context.Background(), tt.field, tt.v, tt.fn, tt.opts)
			if !tt.wantErr(t, err, fmt.Sprintf("Walk(%v, %v, %v, %v)", tt.field, tt.v, tt.fn, tt.opts)) {
				return
			}
			assert.Equalf(t, tt.want, got, "Walk(%v, %v, %v, %v)", tt.field, tt.v, tt.fn, tt.opts)
			assert.Equalf(t, tt.want1, got1, "Walk(%v, %v, %v, %v)", tt.field, tt.v, tt.fn, tt.opts)
		})
	}
}

type customFloat float64
type customInt int
type customStr string
type customMap map[string]interface{}

func TestArrayField_WithType(t *testing.T) {
	t.Run("Nil", func(t *testing.T) {
		got, err := Decode(context.Background(), Array(Number("float")), nil)
		require.NoError(t, err)
		assert.ElementsMatch(t, got, nil)

		got, err = Encode(context.Background(), Array(Number("float")), nil)
		require.NoError(t, err)
		assert.ElementsMatch(t, got, nil)
	})
	t.Run("With []float64", func(t *testing.T) {
		got, err := Decode(context.Background(), Array(Number("float")), []float64{1.0, 2.0})
		require.NoError(t, err)
		assert.ElementsMatch(t, got, []interface{}{1.0, 2.0})

		got, err = Encode(context.Background(), Array(Number("float")), []float64{1.0, 2.0})
		require.NoError(t, err)
		assert.ElementsMatch(t, got, []interface{}{1.0, 2.0})
	})
	t.Run("With []int", func(t *testing.T) {
		got, err := Decode(context.Background(), Array(Number("int")), []int{1, 2})
		require.NoError(t, err)
		assert.ElementsMatch(t, got, []interface{}{int64(1), int64(2)})

		got, err = Encode(context.Background(), Array(Number("int")), []int{1, 2})
		require.NoError(t, err)
		assert.ElementsMatch(t, got, []interface{}{int64(1), int64(2)})
	})
	t.Run("With []string", func(t *testing.T) {
		got, err := Decode(context.Background(), Array(String()), []string{"1", "2"})
		require.NoError(t, err)
		assert.ElementsMatch(t, got, []interface{}{"1", "2"})

		got, err = Encode(context.Background(), Array(String()), []string{"1", "2"})
		require.NoError(t, err)
		assert.ElementsMatch(t, got, []interface{}{"1", "2"})
	})
	t.Run("With []map", func(t *testing.T) {
		got, err := Decode(context.Background(), Array(Object("a", String(), "b", String())),
			[]map[string]interface{}{{"a": "1", "b": "2"}})
		require.NoError(t, err)
		assert.ElementsMatch(t, got, []interface{}{map[string]interface{}{"a": "1", "b": "2"}})

		got, err = Encode(context.Background(), Array(Object("a", String(), "b", String())),
			[]map[string]interface{}{{"a": "1", "b": "2"}})
		require.NoError(t, err)
		assert.ElementsMatch(t, got, []interface{}{map[string]interface{}{"a": "1", "b": "2"}})
	})
	t.Run("With []customFloat", func(t *testing.T) {
		got, err := Decode(context.Background(), Array(Number("float")), []customFloat{1.0, 2.0})
		require.NoError(t, err)
		assert.ElementsMatch(t, got, []interface{}{1.0, 2.0})

		got, err = Encode(context.Background(), Array(Number("float")), []customFloat{1.0, 2.0})
		require.NoError(t, err)
		assert.ElementsMatch(t, got, []interface{}{1.0, 2.0})
	})
	t.Run("With []customInt", func(t *testing.T) {
		got, err := Decode(context.Background(), Array(Number("int")), []customInt{1, 2})
		require.NoError(t, err)
		assert.ElementsMatch(t, got, []interface{}{int64(1), int64(2)})

		got, err = Encode(context.Background(), Array(Number("int")), []customInt{1, 2})
		require.NoError(t, err)
		assert.ElementsMatch(t, got, []interface{}{int64(1), int64(2)})
	})
	t.Run("With []customStr", func(t *testing.T) {
		got, err := Decode(context.Background(), Array(String()), []customStr{"1", "2"})
		require.NoError(t, err)
		assert.ElementsMatch(t, got, []interface{}{"1", "2"})

		got, err = Encode(context.Background(), Array(String()), []customStr{"1", "2"})
		require.NoError(t, err)
		assert.ElementsMatch(t, got, []interface{}{"1", "2"})
	})
	t.Run("With []customMap", func(t *testing.T) {
		got, err := Decode(context.Background(), Array(Object("a", String(), "b", String())),
			[]customMap{{"a": "1", "b": "2"}})
		require.NoError(t, err)
		assert.ElementsMatch(t, got, []interface{}{map[string]interface{}{"a": "1", "b": "2"}})

		got, err = Encode(context.Background(), Array(Object("a", String(), "b", String())),
			[]customMap{{"a": "1", "b": "2"}})
		require.NoError(t, err)
		assert.ElementsMatch(t, got, []interface{}{map[string]interface{}{"a": "1", "b": "2"}})
	})
}