github.com/MontFerret/ferret@v0.18.0/pkg/runtime/values/helpers_test.go (about)

     1  package values_test
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"testing"
     7  
     8  	"github.com/MontFerret/ferret/pkg/runtime/values/types"
     9  
    10  	"github.com/MontFerret/ferret/pkg/runtime/core"
    11  	"github.com/MontFerret/ferret/pkg/runtime/values"
    12  
    13  	. "github.com/smartystreets/goconvey/convey"
    14  )
    15  
    16  var CustomType = core.NewType("custom")
    17  
    18  type CustomValue struct {
    19  	properties map[core.Value]core.Value
    20  }
    21  
    22  func (t *CustomValue) MarshalJSON() ([]byte, error) {
    23  	return nil, core.ErrNotImplemented
    24  }
    25  
    26  func (t *CustomValue) Type() core.Type {
    27  	return CustomType
    28  }
    29  
    30  func (t *CustomValue) String() string {
    31  	return ""
    32  }
    33  
    34  func (t *CustomValue) Compare(other core.Value) int64 {
    35  	return other.Compare(t) * -1
    36  }
    37  
    38  func (t *CustomValue) Unwrap() interface{} {
    39  	return t
    40  }
    41  
    42  func (t *CustomValue) Hash() uint64 {
    43  	return 0
    44  }
    45  
    46  func (t *CustomValue) Copy() core.Value {
    47  	return values.None
    48  }
    49  
    50  func (t *CustomValue) GetIn(ctx context.Context, path []core.Value) (core.Value, core.PathError) {
    51  	if len(path) == 0 {
    52  		return values.None, nil
    53  	}
    54  
    55  	propKey := path[0]
    56  	propValue, ok := t.properties[propKey]
    57  
    58  	if !ok {
    59  		return values.None, nil
    60  	}
    61  
    62  	if len(path) == 1 {
    63  		return propValue, nil
    64  	}
    65  
    66  	return values.GetIn(ctx, propValue, path[1:])
    67  }
    68  
    69  func (t *CustomValue) SetIn(ctx context.Context, path []core.Value, value core.Value) core.PathError {
    70  	if len(path) == 0 {
    71  		return nil
    72  	}
    73  
    74  	propKey := path[0]
    75  	propValue, ok := t.properties[propKey]
    76  
    77  	if !ok {
    78  		return nil
    79  	}
    80  
    81  	if len(path) == 1 {
    82  		t.properties[propKey] = value
    83  
    84  		return nil
    85  	}
    86  
    87  	return values.SetIn(ctx, propValue, path[1:], value)
    88  }
    89  
    90  func TestHelpers(t *testing.T) {
    91  	Convey("Helpers", t, func() {
    92  		Convey("Getter", func() {
    93  			Convey("It should get a value by a given path", func() {
    94  				ct := &CustomValue{
    95  					properties: map[core.Value]core.Value{
    96  						values.NewString("foo"): values.NewInt(1),
    97  						values.NewString("bar"): &CustomValue{
    98  							properties: map[core.Value]core.Value{
    99  								values.NewString("qaz"): values.NewInt(2),
   100  							},
   101  						},
   102  					},
   103  				}
   104  
   105  				ctx := context.Background()
   106  
   107  				foo, err := values.GetIn(ctx, ct, []core.Value{
   108  					values.NewString("foo"),
   109  				})
   110  
   111  				So(err, ShouldBeNil)
   112  				So(foo, ShouldEqual, values.NewInt(1))
   113  
   114  				qaz, err := values.GetIn(ctx, ct, []core.Value{
   115  					values.NewString("bar"),
   116  					values.NewString("qaz"),
   117  				})
   118  
   119  				So(err, ShouldBeNil)
   120  				So(qaz, ShouldEqual, values.NewInt(2))
   121  			})
   122  		})
   123  
   124  		Convey("Setter", func() {
   125  			Convey("It should get a value by a given path", func() {
   126  				ct := &CustomValue{
   127  					properties: map[core.Value]core.Value{
   128  						values.NewString("foo"): values.NewInt(1),
   129  						values.NewString("bar"): &CustomValue{
   130  							properties: map[core.Value]core.Value{
   131  								values.NewString("qaz"): values.NewInt(2),
   132  							},
   133  						},
   134  					},
   135  				}
   136  
   137  				ctx := context.Background()
   138  
   139  				err := values.SetIn(ctx, ct, []core.Value{
   140  					values.NewString("foo"),
   141  				}, values.NewInt(2))
   142  
   143  				So(err, ShouldBeNil)
   144  				So(ct.properties[values.NewString("foo")], ShouldEqual, values.NewInt(2))
   145  
   146  				err = values.SetIn(ctx, ct, []core.Value{
   147  					values.NewString("bar"),
   148  					values.NewString("qaz"),
   149  				}, values.NewString("foobar"))
   150  
   151  				So(err, ShouldBeNil)
   152  
   153  				qaz, err := values.GetIn(ctx, ct, []core.Value{
   154  					values.NewString("bar"),
   155  					values.NewString("qaz"),
   156  				})
   157  
   158  				So(err, ShouldBeNil)
   159  				So(qaz, ShouldEqual, values.NewString("foobar"))
   160  			})
   161  		})
   162  
   163  		Convey("Parse", func() {
   164  			Convey("It should parse values", func() {
   165  				inputs := []struct {
   166  					Parsed core.Value
   167  					Raw    interface{}
   168  				}{
   169  					{Parsed: values.NewInt(1), Raw: int(1)},
   170  					{Parsed: values.NewInt(1), Raw: int8(1)},
   171  					{Parsed: values.NewInt(1), Raw: int16(1)},
   172  					{Parsed: values.NewInt(1), Raw: int32(1)},
   173  					{Parsed: values.NewInt(1), Raw: int64(1)},
   174  				}
   175  
   176  				for _, input := range inputs {
   177  					out := values.Parse(input.Raw)
   178  
   179  					So(out.Type().ID(), ShouldEqual, input.Parsed.Type().ID())
   180  					So(out.Unwrap(), ShouldEqual, input.Parsed.Unwrap())
   181  				}
   182  			})
   183  		})
   184  
   185  		Convey("ToBoolean", func() {
   186  			Convey("Should convert values", func() {
   187  				inputs := [][]core.Value{
   188  					{
   189  						values.None,
   190  						values.False,
   191  					},
   192  					{
   193  						values.True,
   194  						values.True,
   195  					},
   196  					{
   197  						values.False,
   198  						values.False,
   199  					},
   200  					{
   201  						values.NewInt(1),
   202  						values.True,
   203  					},
   204  					{
   205  						values.NewInt(0),
   206  						values.False,
   207  					},
   208  					{
   209  						values.NewFloat(1),
   210  						values.True,
   211  					},
   212  					{
   213  						values.NewFloat(0),
   214  						values.False,
   215  					},
   216  					{
   217  						values.NewString("Foo"),
   218  						values.True,
   219  					},
   220  					{
   221  						values.EmptyString,
   222  						values.False,
   223  					},
   224  					{
   225  						values.NewCurrentDateTime(),
   226  						values.True,
   227  					},
   228  					{
   229  						values.NewArray(1),
   230  						values.True,
   231  					},
   232  					{
   233  						values.NewObject(),
   234  						values.True,
   235  					},
   236  					{
   237  						values.NewBinary([]byte("")),
   238  						values.True,
   239  					},
   240  				}
   241  
   242  				for _, pair := range inputs {
   243  					actual := values.ToBoolean(pair[0])
   244  					expected := pair[1]
   245  
   246  					So(actual, ShouldEqual, expected)
   247  				}
   248  			})
   249  		})
   250  
   251  		Convey("ToFloat", func() {
   252  			Convey("Should convert Int", func() {
   253  				input := values.NewInt(100)
   254  				output := values.ToFloat(input)
   255  
   256  				So(output, ShouldEqual, values.NewFloat(100))
   257  			})
   258  
   259  			Convey("Should convert Float", func() {
   260  				input := values.NewFloat(100)
   261  				output := values.ToFloat(input)
   262  
   263  				So(output, ShouldEqual, values.NewFloat(100))
   264  			})
   265  
   266  			Convey("Should convert String", func() {
   267  				input := values.NewString("100.1")
   268  				output := values.ToFloat(input)
   269  
   270  				So(output, ShouldEqual, values.NewFloat(100.1))
   271  
   272  				output2 := values.ToFloat(values.NewString("foobar"))
   273  				So(output2, ShouldEqual, values.ZeroFloat)
   274  			})
   275  
   276  			Convey("Should convert Boolean", func() {
   277  				So(values.ToFloat(values.True), ShouldEqual, values.NewFloat(1))
   278  				So(values.ToFloat(values.False), ShouldEqual, values.NewFloat(0))
   279  			})
   280  
   281  			Convey("Should convert Array with single item", func() {
   282  				So(values.ToFloat(values.NewArrayWith(values.NewFloat(1))), ShouldEqual, values.NewFloat(1))
   283  			})
   284  
   285  			Convey("Should convert Array with multiple items", func() {
   286  				arg := values.NewArrayWith(values.NewFloat(1), values.NewFloat(1))
   287  
   288  				So(values.ToFloat(arg), ShouldEqual, values.NewFloat(2))
   289  			})
   290  
   291  			Convey("Should convert DateTime", func() {
   292  				dt := values.NewCurrentDateTime()
   293  				ts := dt.Time.Unix()
   294  
   295  				So(values.ToFloat(dt), ShouldEqual, values.NewFloat(float64(ts)))
   296  			})
   297  
   298  			Convey("Should NOT convert other types", func() {
   299  				inputs := []core.Value{
   300  					values.NewObject(),
   301  					values.NewBinary([]byte("")),
   302  				}
   303  
   304  				for _, input := range inputs {
   305  					So(values.ToFloat(input), ShouldEqual, values.ZeroFloat)
   306  				}
   307  			})
   308  		})
   309  
   310  		Convey("ToInt", func() {
   311  			Convey("Should convert Int", func() {
   312  				input := values.NewInt(100)
   313  				output := values.ToInt(input)
   314  
   315  				So(output, ShouldEqual, values.NewInt(100))
   316  			})
   317  
   318  			Convey("Should convert Float", func() {
   319  				input := values.NewFloat(100.1)
   320  				output := values.ToInt(input)
   321  
   322  				So(output, ShouldEqual, values.NewInt(100))
   323  			})
   324  
   325  			Convey("Should convert String", func() {
   326  				input := values.NewString("100")
   327  				output := values.ToInt(input)
   328  
   329  				So(output, ShouldEqual, values.NewInt(100))
   330  
   331  				output2 := values.ToInt(values.NewString("foobar"))
   332  				So(output2, ShouldEqual, values.ZeroInt)
   333  			})
   334  
   335  			Convey("Should convert Boolean", func() {
   336  				So(values.ToInt(values.True), ShouldEqual, values.NewInt(1))
   337  				So(values.ToInt(values.False), ShouldEqual, values.NewInt(0))
   338  			})
   339  
   340  			Convey("Should convert Array with single item", func() {
   341  				So(values.ToInt(values.NewArrayWith(values.NewFloat(1))), ShouldEqual, values.NewInt(1))
   342  			})
   343  
   344  			Convey("Should convert Array with multiple items", func() {
   345  				arg := values.NewArrayWith(values.NewFloat(1), values.NewFloat(1))
   346  
   347  				So(values.ToInt(arg), ShouldEqual, values.NewFloat(2))
   348  			})
   349  
   350  			Convey("Should convert DateTime", func() {
   351  				dt := values.NewCurrentDateTime()
   352  				ts := dt.Time.Unix()
   353  
   354  				So(values.ToInt(dt), ShouldEqual, values.NewInt(int(ts)))
   355  			})
   356  
   357  			Convey("Should NOT convert other types", func() {
   358  				inputs := []core.Value{
   359  					values.NewObject(),
   360  					values.NewBinary([]byte("")),
   361  				}
   362  
   363  				for _, input := range inputs {
   364  					So(values.ToInt(input), ShouldEqual, values.ZeroInt)
   365  				}
   366  			})
   367  		})
   368  
   369  		Convey("ToArray", func() {
   370  			Convey("Should convert primitives", func() {
   371  				dt := values.NewCurrentDateTime()
   372  
   373  				inputs := [][]core.Value{
   374  					{
   375  						values.None,
   376  						values.NewArray(0),
   377  					},
   378  					{
   379  						values.True,
   380  						values.NewArrayWith(values.True),
   381  					},
   382  					{
   383  						values.NewInt(1),
   384  						values.NewArrayWith(values.NewInt(1)),
   385  					},
   386  					{
   387  						values.NewFloat(1),
   388  						values.NewArrayWith(values.NewFloat(1)),
   389  					},
   390  					{
   391  						values.NewString("foo"),
   392  						values.NewArrayWith(values.NewString("foo")),
   393  					},
   394  					{
   395  						dt,
   396  						values.NewArrayWith(dt),
   397  					},
   398  				}
   399  
   400  				for _, pairs := range inputs {
   401  					actual := values.ToArray(context.Background(), pairs[0])
   402  					expected := pairs[1]
   403  
   404  					So(actual.Compare(expected), ShouldEqual, 0)
   405  				}
   406  			})
   407  
   408  			Convey("Should create a copy of a given array", func() {
   409  				vals := []core.Value{
   410  					values.NewInt(1),
   411  					values.NewInt(2),
   412  					values.NewInt(3),
   413  					values.NewInt(4),
   414  					values.NewArray(10),
   415  					values.NewObject(),
   416  				}
   417  
   418  				input := values.NewArrayWith(vals...)
   419  				arr := values.ToArray(context.Background(), input)
   420  
   421  				So(input == arr, ShouldBeFalse)
   422  				So(arr.Length() == input.Length(), ShouldBeTrue)
   423  
   424  				for idx := range vals {
   425  					expected := input.Get(values.NewInt(idx))
   426  					actual := arr.Get(values.NewInt(idx))
   427  
   428  					// same ref
   429  					So(actual == expected, ShouldBeTrue)
   430  					So(actual.Compare(expected), ShouldEqual, 0)
   431  				}
   432  			})
   433  
   434  			Convey("Should convert object to an array", func() {
   435  				input := values.NewObjectWith(
   436  					values.NewObjectProperty("foo", values.NewString("bar")),
   437  					values.NewObjectProperty("baz", values.NewInt(1)),
   438  					values.NewObjectProperty("qaz", values.NewObject()),
   439  				)
   440  
   441  				arr := values.ToArray(context.Background(), input).Sort()
   442  
   443  				So(arr.String(), ShouldEqual, "[1,\"bar\",{}]")
   444  				So(arr.Get(values.NewInt(2)) == input.MustGet("qaz"), ShouldBeTrue)
   445  			})
   446  		})
   447  
   448  		Convey("Unmarshal", func() {
   449  			Convey("Should deserialize object", func() {
   450  				input := map[string]interface{}{
   451  					"foo": []string{
   452  						"bar",
   453  						"qaz",
   454  					},
   455  				}
   456  				json1, err := json.Marshal(input)
   457  
   458  				So(err, ShouldBeNil)
   459  
   460  				val, err := values.Unmarshal(json1)
   461  
   462  				So(err, ShouldBeNil)
   463  				So(val.Type(), ShouldResemble, types.Object)
   464  
   465  				json2, err := val.MarshalJSON()
   466  
   467  				So(err, ShouldBeNil)
   468  				So(json2, ShouldResemble, json1)
   469  			})
   470  		})
   471  	})
   472  }