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 }