Additional tests & bool handling
This commit is contained in:
@@ -58,6 +58,9 @@ func TestRecordFieldValuesAccess(t *testing.T) {
|
||||
&types.ModuleField{Name: "s2", Multi: false, Kind: "String"},
|
||||
&types.ModuleField{Name: "b0", Multi: false, Kind: "Bool"},
|
||||
&types.ModuleField{Name: "b1", Multi: false, Kind: "Bool"},
|
||||
&types.ModuleField{Name: "n1", Multi: false, Kind: "Number"},
|
||||
&types.ModuleField{Name: "n2", Multi: false, Kind: "Number"},
|
||||
&types.ModuleField{Name: "n3", Multi: false, Kind: "Number"},
|
||||
}}
|
||||
|
||||
raw = &types.Record{Values: types.RecordValueSet{
|
||||
@@ -67,10 +70,15 @@ func TestRecordFieldValuesAccess(t *testing.T) {
|
||||
&types.RecordValue{Name: "m1", Value: "mVal1.2", Place: 2},
|
||||
&types.RecordValue{Name: "m2", Value: "mVal2.0", Place: 0},
|
||||
&types.RecordValue{Name: "b1", Value: "1", Place: 0},
|
||||
&types.RecordValue{Name: "n2", Value: "0", Place: 0},
|
||||
&types.RecordValue{Name: "n3", Value: "2", Place: 0},
|
||||
}}
|
||||
|
||||
tval = &ComposeRecord{value: raw}
|
||||
scope = expr.RVars{"rec": tval}.Vars()
|
||||
scope = expr.RVars{
|
||||
"rec": tval,
|
||||
"nil": nil,
|
||||
}.Vars()
|
||||
)
|
||||
|
||||
// @todo see not above re. back-ref to record
|
||||
@@ -108,6 +116,8 @@ func TestRecordFieldValuesAccess(t *testing.T) {
|
||||
test bool
|
||||
expr string
|
||||
}{
|
||||
{false, `nil`},
|
||||
|
||||
// interaction with set values
|
||||
{true, `rec.values.s1 == "sVal1"`},
|
||||
{false, `rec.values.s1 == "sVal2"`},
|
||||
@@ -130,16 +140,37 @@ func TestRecordFieldValuesAccess(t *testing.T) {
|
||||
{false, `rec.values.b0`},
|
||||
{true, `rec.values.b1`},
|
||||
{false, `!rec.values.b1`},
|
||||
|
||||
// numbers
|
||||
{false, `rec.values.n1`},
|
||||
{false, `rec.values.n2`},
|
||||
{true, `rec.values.n3`},
|
||||
|
||||
{true, `rec.values.n1 == 0`},
|
||||
{true, `rec.values.n2 == 0`},
|
||||
{false, `rec.values.n3 == 0`},
|
||||
|
||||
{false, `rec.values.n1 == 2`},
|
||||
{false, `rec.values.n2 == 2`},
|
||||
{true, `rec.values.n3 == 2`},
|
||||
|
||||
//{true, `rec.values.n1 < 3`}, // invalid op <nil> < 3
|
||||
{true, `rec.values.n2 < 3`},
|
||||
{true, `rec.values.n3 < 3`},
|
||||
|
||||
//{false, `rec.values.n1 > 1`}, // invalid op <nil> > 3
|
||||
{false, `rec.values.n2 > 2`},
|
||||
{false, `rec.values.n3 > 2`},
|
||||
}
|
||||
|
||||
for _, tc := range tcc {
|
||||
var (
|
||||
req = require.New(t)
|
||||
parser = expr.NewParser()
|
||||
)
|
||||
|
||||
t.Run(tc.expr, func(t *testing.T) {
|
||||
eval, err := parser.Parse(tc.expr)
|
||||
var (
|
||||
req = require.New(t)
|
||||
parser = expr.NewParser()
|
||||
eval, err = parser.Parse(tc.expr)
|
||||
)
|
||||
|
||||
req.NoError(err)
|
||||
|
||||
test, err := eval.Test(context.Background(), scope)
|
||||
|
||||
@@ -229,12 +229,27 @@ func (t Array) Select(k string) (TypedValue, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// emptyStringFailsafe returns 0 on empty strings
|
||||
func emptyStringFailsafe(val interface{}) interface{} {
|
||||
val = UntypedValue(val)
|
||||
if aux, is := val.(string); is && len(strings.TrimSpace(aux)) == 0 {
|
||||
return 0
|
||||
} else {
|
||||
return val
|
||||
}
|
||||
}
|
||||
|
||||
func (t ID) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(fmt.Sprintf("%d", t.value))
|
||||
}
|
||||
|
||||
func CastToBoolean(val interface{}) (out bool, err error) {
|
||||
return cast.ToBoolE(UntypedValue(val))
|
||||
val = UntypedValue(val)
|
||||
if aux, is := val.(string); is && len(strings.TrimSpace(aux)) == 0 {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return cast.ToBoolE(val)
|
||||
}
|
||||
|
||||
func CastToString(val interface{}) (out string, err error) {
|
||||
@@ -258,7 +273,7 @@ func CastToHandle(val interface{}) (string, error) {
|
||||
}
|
||||
|
||||
func CastToDuration(val interface{}) (out time.Duration, err error) {
|
||||
return cast.ToDurationE(UntypedValue(val))
|
||||
return cast.ToDurationE(emptyStringFailsafe(val))
|
||||
}
|
||||
|
||||
func CastToDateTime(val interface{}) (out *time.Time, err error) {
|
||||
@@ -279,21 +294,19 @@ func CastToDateTime(val interface{}) (out *time.Time, err error) {
|
||||
}
|
||||
|
||||
func CastToFloat(val interface{}) (out float64, err error) {
|
||||
return cast.ToFloat64E(UntypedValue(val))
|
||||
return cast.ToFloat64E(emptyStringFailsafe(val))
|
||||
}
|
||||
|
||||
func CastToID(val interface{}) (out uint64, err error) {
|
||||
out, err = cast.ToUint64E(UntypedValue(val))
|
||||
|
||||
return
|
||||
return cast.ToUint64E(emptyStringFailsafe(val))
|
||||
}
|
||||
|
||||
func CastToInteger(val interface{}) (out int64, err error) {
|
||||
return cast.ToInt64E(UntypedValue(val))
|
||||
return cast.ToInt64E(emptyStringFailsafe(val))
|
||||
}
|
||||
|
||||
func CastToUnsignedInteger(val interface{}) (out uint64, err error) {
|
||||
return cast.ToUint64E(UntypedValue(val))
|
||||
return cast.ToUint64E(emptyStringFailsafe(val))
|
||||
}
|
||||
|
||||
func (t *KV) Has(k string) bool {
|
||||
|
||||
@@ -1,12 +1,60 @@
|
||||
package expr
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/stretchr/testify/require"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestTypedValueOperations(t *testing.T) {
|
||||
scope := RVars{
|
||||
"xUint": Must(NewUnsignedInteger(1)),
|
||||
"xInt": Must(NewInteger(1)),
|
||||
"xBoolT": Must(NewBoolean(true)),
|
||||
"xBoolF": Must(NewBoolean(false)),
|
||||
"xString": Must(NewString("foo")),
|
||||
}.Vars()
|
||||
|
||||
tcc := []struct {
|
||||
expects interface{}
|
||||
expr string
|
||||
}{
|
||||
// sanity check
|
||||
{true, "xBoolT"},
|
||||
{false, "xBoolF"},
|
||||
|
||||
// uint ops
|
||||
{true, "xUint == 1"},
|
||||
{uint64(1), "xUint"},
|
||||
|
||||
// uint ops
|
||||
{true, "xInt == 1"},
|
||||
{int64(1), "xInt"},
|
||||
|
||||
// string ops
|
||||
{true, `xString == "foo"`},
|
||||
{"foo", "xString"},
|
||||
}
|
||||
|
||||
for _, tc := range tcc {
|
||||
t.Run(tc.expr, func(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
parser = NewParser()
|
||||
eval, err = parser.Parse(tc.expr)
|
||||
)
|
||||
|
||||
req.NoError(err)
|
||||
result, err := eval.Eval(context.Background(), scope)
|
||||
req.NoError(err)
|
||||
req.Equal(tc.expects, UntypedValue(result))
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestKV_Set(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
@@ -46,6 +94,37 @@ func TestKVV_Set(t *testing.T) {
|
||||
req.Equal([]string{"bar"}, kvv.value["foo"])
|
||||
}
|
||||
|
||||
func TestCastEmptyString(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
)
|
||||
{
|
||||
f, err := CastToFloat("")
|
||||
req.NoError(err)
|
||||
req.Zero(f)
|
||||
}
|
||||
{
|
||||
i, err := CastToInteger("")
|
||||
req.NoError(err)
|
||||
req.Zero(i)
|
||||
}
|
||||
{
|
||||
u, err := CastToUnsignedInteger("")
|
||||
req.NoError(err)
|
||||
req.Zero(u)
|
||||
}
|
||||
{
|
||||
u, err := CastToDuration("")
|
||||
req.NoError(err)
|
||||
req.Zero(u)
|
||||
}
|
||||
{
|
||||
u, err := CastToBoolean("")
|
||||
req.NoError(err)
|
||||
req.False(u)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCastToArray(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
|
||||
@@ -66,13 +66,11 @@ func (p *gvalParser) ParseEvaluators(ee ...Evaluator) error {
|
||||
}
|
||||
|
||||
func (e *gvalEval) Eval(ctx context.Context, scope *Vars) (interface{}, error) {
|
||||
return e.evaluable(ctx, scope)
|
||||
//return e.evaluable(ctx, scope.Dict())
|
||||
return e.evaluable(ctx, scope.Dict())
|
||||
}
|
||||
|
||||
func (e *gvalEval) Test(ctx context.Context, scope *Vars) (bool, error) {
|
||||
r, err := e.evaluable(ctx, scope)
|
||||
//r, err := e.evaluable(ctx, scope.Dict())
|
||||
r, err := e.evaluable(ctx, scope.Dict())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user