github.com/awesome-flow/flow@v0.0.3-0.20190918184116-508d75d68a2c/pkg/cast/mapper_test.go (about)

     1  package cast
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"testing"
     7  
     8  	"github.com/awesome-flow/flow/pkg/types"
     9  )
    10  
    11  type TestMapper struct {
    12  	conv func(kv *types.KeyValue) (*types.KeyValue, error)
    13  }
    14  
    15  func NewTestMapper(conv func(kv *types.KeyValue) (*types.KeyValue, error)) *TestMapper {
    16  	return &TestMapper{
    17  		conv: conv,
    18  	}
    19  }
    20  
    21  func (tm *TestMapper) Map(kv *types.KeyValue) (*types.KeyValue, error) {
    22  	return tm.conv(kv)
    23  }
    24  
    25  func TestMapperNodeInsert(t *testing.T) {
    26  	mpr := NewTestMapper(func(kv *types.KeyValue) (*types.KeyValue, error) {
    27  		return kv, nil
    28  	})
    29  	tests := []struct {
    30  		path string
    31  		exp  *MapperNode
    32  	}{
    33  
    34  		{
    35  			"",
    36  			&MapperNode{},
    37  		},
    38  		{
    39  			"foo",
    40  			&MapperNode{
    41  				Children: map[string]*MapperNode{
    42  					"foo": &MapperNode{
    43  						Mpr: mpr,
    44  					},
    45  				},
    46  			},
    47  		},
    48  		{
    49  			"foo.bar",
    50  			&MapperNode{
    51  				Children: map[string]*MapperNode{
    52  					"foo": &MapperNode{
    53  						Children: map[string]*MapperNode{
    54  							"bar": &MapperNode{
    55  								Mpr: mpr,
    56  							},
    57  						},
    58  					},
    59  				},
    60  			},
    61  		},
    62  		{
    63  			"foo.*.bar",
    64  			&MapperNode{
    65  				Children: map[string]*MapperNode{
    66  					"foo": &MapperNode{
    67  						Children: map[string]*MapperNode{
    68  							"*": &MapperNode{
    69  								Children: map[string]*MapperNode{
    70  									"bar": &MapperNode{
    71  										Mpr: mpr,
    72  									},
    73  								},
    74  							},
    75  						},
    76  					},
    77  				},
    78  			},
    79  		},
    80  	}
    81  
    82  	t.Parallel()
    83  
    84  	for _, testCase := range tests {
    85  		t.Run(testCase.path, func(t *testing.T) {
    86  			root := NewMapperNode()
    87  			root.Insert(types.NewKey(testCase.path), mpr)
    88  			if !reflect.DeepEqual(testCase.exp, root) {
    89  				t.Errorf("Unexpected node structure: want: %#v, got: %#v", testCase.exp, root)
    90  			}
    91  		})
    92  	}
    93  }
    94  
    95  func TestMapperNodeFindSingleEntryLookup(t *testing.T) {
    96  	tests := []struct {
    97  		insertPaths []string
    98  		lookupPath  string
    99  	}{
   100  		{
   101  			[]string{"foo", "*"},
   102  			"foo",
   103  		},
   104  		{
   105  			[]string{"foo.bar", "foo.*", "*.bar", "*.*"},
   106  			"foo.bar",
   107  		},
   108  		{
   109  			[]string{"foo.bar.baz", "foo.bar.*", "foo.*.baz", "foo.*.*", "*.bar.baz", "*.bar.*", "*.*.baz", "*.*.*"},
   110  			"foo.bar.baz",
   111  		},
   112  	}
   113  
   114  	t.Parallel()
   115  
   116  	for _, testCase := range tests {
   117  		for _, insertPath := range testCase.insertPaths {
   118  			t.Run(insertPath, func(t *testing.T) {
   119  				mpr := NewTestMapper(func(kv *types.KeyValue) (*types.KeyValue, error) { return kv, nil })
   120  				root := NewMapperNode()
   121  				root.Insert(types.NewKey(insertPath), mpr)
   122  				v := root.Find(types.NewKey(testCase.lookupPath))
   123  				if v == nil {
   124  					t.Fatalf("Expected to get a lookup result for key %q, got nil", testCase.lookupPath)
   125  				}
   126  				if v.Mpr != mpr {
   127  					t.Fatalf("Unexpected mapper value returned by the key %q lookup: %#v, want: %#v", testCase.lookupPath, v.Mpr, mpr)
   128  				}
   129  			})
   130  		}
   131  	}
   132  }
   133  
   134  func TestMapperNodeFindPrecedence(t *testing.T) {
   135  	convFunc := func(kv *types.KeyValue) (*types.KeyValue, error) { return kv, nil }
   136  	mprAstrx, mprExct := NewTestMapper(convFunc), NewTestMapper(convFunc)
   137  
   138  	tests := []struct {
   139  		exactPath  string
   140  		astrxPaths []string
   141  	}{
   142  		{
   143  			"foo",
   144  			[]string{"*"},
   145  		},
   146  		{
   147  			"foo.bar",
   148  			[]string{"foo.*", "*.bar", "*.*"},
   149  		},
   150  		{
   151  			"foo.bar.baz",
   152  			[]string{"foo.bar.*", "foo.*.baz", "foo.*.*", "*.bar.baz", "*.bar.*", "*.*.baz", "*.*.*"},
   153  		},
   154  	}
   155  
   156  	t.Parallel()
   157  
   158  	for _, testCase := range tests {
   159  		t.Run(testCase.exactPath, func(t *testing.T) {
   160  			root := NewMapperNode()
   161  			root.Insert(types.NewKey(testCase.exactPath), mprExct)
   162  			for _, astrxPath := range testCase.astrxPaths {
   163  				root.Insert(types.NewKey(astrxPath), mprAstrx)
   164  			}
   165  			v := root.Find(types.NewKey(testCase.exactPath))
   166  			if v == nil {
   167  				t.Fatalf("Expected to get a non-nil lookup result for key %q, git nil", testCase.exactPath)
   168  			}
   169  			if v.Mpr != mprExct {
   170  				t.Fatalf("Unexpected value returned by the key %q lookup: got: %#v, want: %#v", testCase.exactPath, v.Mpr, mprExct)
   171  			}
   172  		})
   173  	}
   174  }
   175  
   176  func TestConvMapper(t *testing.T) {
   177  	tests := []struct {
   178  		name      string
   179  		conv      Converter
   180  		expVal    types.Value
   181  		validIn   []types.Value
   182  		invalidIn []types.Value
   183  	}{
   184  		{
   185  			name:      "conversion to Int",
   186  			conv:      ToInt,
   187  			expVal:    42,
   188  			validIn:   []types.Value{42, "42", intptr(42)},
   189  			invalidIn: []types.Value{true, "", '0', nil},
   190  		},
   191  		{
   192  			name:      "conversion to Str",
   193  			conv:      ToStr,
   194  			expVal:    "42",
   195  			validIn:   []types.Value{"42", 42, strptr("42")},
   196  			invalidIn: []types.Value{intptr(42), nil, false, '0'},
   197  		},
   198  		{
   199  			name:      "conversion to Bool",
   200  			conv:      ToBool,
   201  			expVal:    true,
   202  			validIn:   []types.Value{true, boolptr(true), "true", "y", 1, "1"},
   203  			invalidIn: []types.Value{123, "asdf", nil},
   204  		},
   205  	}
   206  
   207  	t.Parallel()
   208  
   209  	for _, testCase := range tests {
   210  		t.Run(testCase.name, func(t *testing.T) {
   211  			mpr := NewConvMapper(testCase.conv)
   212  			for _, val := range testCase.validIn {
   213  				conv, convErr := mpr.Map(&types.KeyValue{Key: nil, Value: val})
   214  				if convErr != nil {
   215  					t.Fatalf("Unexpected mapping error for input value %#v", val)
   216  				}
   217  				if !reflect.DeepEqual(conv.Value, testCase.expVal) {
   218  					t.Fatalf("Unexpected mapping value for input value %#v: got: %#v, want: %#v", val, conv.Value, testCase.expVal)
   219  				}
   220  			}
   221  			for _, val := range testCase.invalidIn {
   222  				_, convErr := mpr.Map(&types.KeyValue{Key: nil, Value: val})
   223  				if convErr == nil {
   224  					t.Fatalf("Expected to get an error while converting %#v, got nil", val)
   225  				}
   226  			}
   227  		})
   228  	}
   229  }
   230  
   231  func TestDefineSchema(t *testing.T) {
   232  
   233  	conv := func(kv *types.KeyValue) (*types.KeyValue, error) {
   234  		return kv, nil
   235  	}
   236  
   237  	mpr := NewTestMapper(conv)
   238  	mpr1, mpr2 := NewTestMapper(conv), NewTestMapper(conv)
   239  
   240  	tests := []struct {
   241  		name   string
   242  		schema Schema
   243  		want   MapperNode
   244  	}{
   245  		{
   246  			"Nil-schema",
   247  			nil,
   248  			MapperNode{},
   249  		},
   250  		{
   251  			"A mapper",
   252  			NewTestMapper(conv),
   253  			MapperNode{
   254  				Mpr: nil,
   255  			},
   256  		},
   257  		{
   258  			"A converter",
   259  			newTestConverter(convAct{1, true}),
   260  			MapperNode{
   261  				Mpr: nil,
   262  			},
   263  		},
   264  		{
   265  			"A mapper, flat key",
   266  			map[string]Schema{
   267  				"foo": mpr,
   268  			},
   269  			MapperNode{
   270  				Mpr: nil,
   271  				Children: map[string]*MapperNode{
   272  					"foo": &MapperNode{
   273  						Mpr: mpr,
   274  					},
   275  				},
   276  			},
   277  		},
   278  		{
   279  			"Simple __self__",
   280  			map[string]Schema{
   281  				"foo": map[string]Schema{
   282  					"__self__": mpr,
   283  				},
   284  			},
   285  			MapperNode{
   286  				Mpr: nil,
   287  				Children: map[string]*MapperNode{
   288  					"foo": &MapperNode{
   289  						Mpr: mpr,
   290  					},
   291  				},
   292  			},
   293  		},
   294  		{
   295  			"Nested structure",
   296  			map[string]Schema{
   297  				"foo": map[string]Schema{
   298  					"bar": map[string]Schema{
   299  						"baz": mpr1,
   300  					},
   301  				},
   302  				"moo": mpr2,
   303  			},
   304  			MapperNode{
   305  				Children: map[string]*MapperNode{
   306  					"foo": &MapperNode{
   307  						Children: map[string]*MapperNode{
   308  							"bar": &MapperNode{
   309  								Children: map[string]*MapperNode{
   310  									"baz": &MapperNode{
   311  										Mpr: mpr1,
   312  									},
   313  								},
   314  							},
   315  						},
   316  					},
   317  					"moo": &MapperNode{
   318  						Mpr: mpr2,
   319  					},
   320  				},
   321  			},
   322  		},
   323  	}
   324  
   325  	t.Parallel()
   326  
   327  	for _, testCase := range tests {
   328  		t.Run(testCase.name, func(t *testing.T) {
   329  			mn := NewMapperNode()
   330  			if err := mn.DefineSchema(testCase.schema); err != nil {
   331  				t.Fatalf("Failed to call DefineSchema(): %s", err)
   332  			}
   333  			if !reflect.DeepEqual(testCase.want, *mn) {
   334  				t.Fatalf("Unexpected value after DefineSchema(): got: %#v, want: %#v", *mn, testCase.want)
   335  			}
   336  
   337  		})
   338  	}
   339  }
   340  
   341  type fooStruct struct {
   342  	Bar int
   343  }
   344  
   345  func TestMap(t *testing.T) {
   346  	convSq := NewTestMapper(func(kv *types.KeyValue) (*types.KeyValue, error) {
   347  		v := kv.Value.(int)
   348  		return &types.KeyValue{Key: kv.Key, Value: v * v}, nil
   349  	})
   350  	fooMpr := NewTestMapper(func(kv *types.KeyValue) (*types.KeyValue, error) {
   351  		v := kv.Value.(map[string]types.Value)
   352  		return &types.KeyValue{Key: kv.Key, Value: &fooStruct{Bar: v["bar"].(int)}}, nil
   353  	})
   354  	errMpr := NewTestMapper(func(kv *types.KeyValue) (*types.KeyValue, error) {
   355  		return nil, fmt.Errorf("This mapper returns an error")
   356  	})
   357  	tests := []struct {
   358  		name    string
   359  		schema  Schema
   360  		inputKV *types.KeyValue
   361  		wantKV  *types.KeyValue
   362  		wantErr error
   363  	}{
   364  		{
   365  			"nil-schema",
   366  			nil,
   367  			&types.KeyValue{Key: types.NewKey("foo"), Value: 42},
   368  			&types.KeyValue{Key: types.NewKey("foo"), Value: 42},
   369  			nil,
   370  		},
   371  		{
   372  			"Simple mapper matching the key",
   373  			map[string]Schema{
   374  				"foo": convSq,
   375  			},
   376  			&types.KeyValue{Key: types.NewKey("foo"), Value: 4},
   377  			&types.KeyValue{Key: types.NewKey("foo"), Value: 16},
   378  			nil,
   379  		},
   380  		{
   381  			"Simple mapper with unknown key",
   382  			map[string]Schema{
   383  				"foo": convSq,
   384  			},
   385  			&types.KeyValue{Key: types.NewKey("bar"), Value: 4},
   386  			&types.KeyValue{Key: types.NewKey("bar"), Value: 4},
   387  			nil,
   388  		},
   389  		{
   390  			"Nesting schema definition",
   391  			map[string]Schema{
   392  				"foo": map[string]Schema{
   393  					"__self__": fooMpr,
   394  					"bar":      convSq,
   395  				},
   396  			},
   397  			&types.KeyValue{Key: types.NewKey("foo.bar"), Value: 4},
   398  			&types.KeyValue{Key: types.NewKey("foo.bar"), Value: 16},
   399  			nil,
   400  		},
   401  		{
   402  			"Composite key lookup",
   403  			map[string]Schema{
   404  				"foo": map[string]Schema{
   405  					"__self__": fooMpr,
   406  					"bar":      convSq,
   407  				},
   408  			},
   409  			&types.KeyValue{Key: types.NewKey("foo"), Value: map[string]types.Value{"bar": 4}},
   410  			&types.KeyValue{Key: types.NewKey("foo"), Value: &fooStruct{Bar: 4}},
   411  			nil,
   412  		},
   413  		{
   414  			"Failing mapper",
   415  			map[string]Schema{
   416  				"foo": errMpr,
   417  			},
   418  			&types.KeyValue{Key: types.NewKey("foo"), Value: 42},
   419  			nil,
   420  			fmt.Errorf("This mapper returns an error"),
   421  		},
   422  	}
   423  
   424  	t.Parallel()
   425  
   426  	for _, testCase := range tests {
   427  		t.Run(testCase.name, func(t *testing.T) {
   428  			mn := &MapperNode{}
   429  			if err := mn.DefineSchema(testCase.schema); err != nil {
   430  				t.Fatalf("Failed to call DefineSchema(): %s", err)
   431  			}
   432  			gotKV, gotErr := mn.Map(testCase.inputKV)
   433  			if !reflect.DeepEqual(gotErr, testCase.wantErr) {
   434  				t.Fatalf("Unexpected error on Map() call: got: %s, want: %s", gotErr, testCase.wantErr)
   435  			}
   436  			if testCase.wantKV != nil && !reflect.DeepEqual(gotKV, testCase.wantKV) {
   437  				t.Fatalf("Unexpected value: Map(%#v) = %#v, want: %#v", testCase.inputKV, gotKV, testCase.wantKV)
   438  			}
   439  		})
   440  	}
   441  }