Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
204 changes: 111 additions & 93 deletions eval/eval_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,39 +73,48 @@ func TestEval(t *testing.T) {
},
{
name: "list",
exp: "object.items.isSorted() && object.items.sum() == 6 && object.items.max() == 3 && object.items.indexOf(1) == 0",
// For some reason object.items == sort(object.items) is false here, but the playground evaluates it as true.
// Needs further investigation.
exp: `object.items == sort(object.items) && sum(object.items) == 6 && sort(object.items)[-1] == 3 && findIndex(object.items, # == 1) == 0`,
want: true,
skip: true, // https://github.com/polds/expr-playground/issues/5
},
{
name: "optional",
exp: `object.?foo.orValue("fallback")`,
exp: `object?.foo ?? "fallback"`,
want: "fallback",
skip: true, // https://github.com/polds/expr-playground/issues/6
},
{
name: "strings",
exp: "object.abc.join(', ')",
exp: "join(object.abc, ', ')",
want: "a, b, c",
skip: true, // https://github.com/polds/expr-playground/issues/7
},
{
name: "cross type numeric comparisons",
exp: "object.replicas > 1.4",
want: true,
},
{
name: "split pipe",
exp: "object.image | split(':') | len() == 2",
want: true,
},
{
name: "split",
exp: "object.image.split(':').size() == 2",
exp: "len(split(object.image, ':')) == 2",
want: true,
skip: true, // https://github.com/polds/expr-playground/issues/8
},
{
name: "quantity",
exp: `isQuantity(object.memory) && quantity(object.memory).add(quantity("700M")).sub(1).isLessThan(quantity("2G"))`,
want: true,
skip: true, // https://github.com/polds/expr-playground/issues/9
},
{
name: "duration",
exp: `duration('1d')`,
wantErr: true,
},
{
name: "sets.contains test 1",
exp: `sets.contains([], [])`,
Expand Down Expand Up @@ -201,166 +210,175 @@ func TestEval(t *testing.T) {
}
}

// TestValidation compiles the expr expression and then runs it against the given input.
func TestValidation(t *testing.T) {
tests := []struct {
name string
exp string
wantErr bool
skip bool
name string
exp string
shouldCompile bool
shouldRun bool
skip bool
}{
{
name: "garbage",
exp: `blue ~!< 1`,
},
// Duration Literals
{
name: "Duration Validation test 1",
exp: `duration('1')`,
wantErr: true,
skip: true, // https://github.com/polds/expr-playground/issues/21
name: "Duration Validation test 1",
exp: `duration('1')`, // Missing unit in duration.
shouldCompile: true,
},
{
name: "Duration Validation test 2",
exp: `duration('1d')`,
wantErr: true,
skip: true, // https://github.com/polds/expr-playground/issues/22
// Note: This duration is invalid. There are two things going on here:
// - In vanilla go this is a rune literal and is invalid because it's more than one character.
// - But the Expr compilation type casts the any to a string and go parses it as a string. But this is
// still invalid because day is not a duration valid unit. So Expr compiles is successfully, but
// will fail Eval. So we run the program after compilation to catch this.
//
// https://github.com/expr-lang/expr/blob/master/builtin/builtin.go#L615
name: "Duration Validation test 2",
exp: `duration('1d')`, // No day unit in Go duration.
shouldCompile: true,
},
{
name: "Duration Validation test 3",
exp: `duration('1us') < duration('1nns')`,
wantErr: true,
skip: true, // https://github.com/polds/expr-playground/issues/23
name: "Duration Validation test 3",
exp: `duration('1us') < duration('1nns')`,
shouldCompile: true,
},
{
name: "Duration Validation test 4",
exp: `duration('2h3m4s5us')`,
name: "Duration Validation test 4",
exp: `duration('2h3m4s5us')`,
shouldCompile: true,
shouldRun: true,
},
{
name: "Duration Validation test 5",
exp: `duration(x)`,
name: "Duration Validation test 5",
exp: `duration(x)`,
shouldCompile: true,
},

// Timestamp Literals
{
name: "Timestamp Validation test 1",
exp: `timestamp('1000-00-00T00:00:00Z')`,
wantErr: true,
name: "Timestamp Validation test 1",
exp: `date('1000-00-00T00:00:00Z')`, // This is an invalid date.
shouldCompile: true,
},
{
name: "Timestamp Validation test 2",
exp: `timestamp('1000-01-01T00:00:00ZZ')`,
wantErr: true,
name: "Timestamp Validation test 2",
exp: `date('1000-01-01T00:00:00Z')`, // This is a valid date. But it's the min date.
shouldCompile: true,
shouldRun: true,
},

{
name: "Timestamp Validation test 3",
exp: `timestamp('1000-01-01T00:00:00Z')`,
skip: true, // https://github.com/polds/expr-playground/issues/24
name: "Timestamp Validation test 3",
exp: `date('1000-01-01T00:00:00ZZ')`, // Two Z's is invalid.
shouldCompile: true,
},
{
name: "Timestamp Validation test 4",
exp: `timestamp(-6213559680)`, // min unix epoch time.
skip: true, // https://github.com/polds/expr-playground/issues/25
},
{
name: "Timestamp Validation test 5",
exp: `timestamp(-62135596801)`,
wantErr: true,
exp: `date(-6213559680)`, // unit missing
},
{
name: "Timestamp Validation test 6",
exp: `timestamp(x)`,
skip: true, // https://github.com/polds/expr-playground/issues/26
name: "Timestamp Validation test 5",
exp: `date(x)`,
shouldCompile: true,
},

// Regex Literals
{
name: "Regex Validation test 1",
exp: `'hello'.matches('el*')`,
skip: true, // https://github.com/polds/expr-playground/issues/27
},
{
name: "Regex Validation test 2",
exp: `'hello'.matches('x++')`,
wantErr: true,
name: "Regex Validation test 2",
exp: `'hello'.matches('x++')`,
},
{
name: "Regex Validation test 3",
exp: `'hello'.matches('(?<name%>el*)')`,
wantErr: true,
name: "Regex Validation test 3",
exp: `'hello'.matches('(?<name%>el*)')`,
},
{
name: "Regex Validation test 4",
exp: `'hello'.matches('??el*')`,
wantErr: true,
name: "Regex Validation test 4",
exp: `'hello'.matches('??el*')`,
},
{
name: "Regex Validation test 5",
exp: `'hello'.matches(x)`,
skip: true, // https://github.com/polds/expr-playground/issues/28
},

// Homogeneous Aggregate Literals
{
name: "Homogeneous Aggregate Validation test 1",
exp: `name in ['hello', 0]`,
wantErr: true,
skip: true, // https://github.com/polds/expr-playground/issues/29
name: "Homogeneous Aggregate Validation test 1",
exp: `name in ['hello', 0]`, // Expr allows the type mixed array.
shouldCompile: true,
shouldRun: true,
},
{
name: "Homogeneous Aggregate Validation test 2",
exp: `{'hello':'world', 1:'!'}`,
wantErr: true,
skip: true, // https://github.com/polds/expr-playground/issues/30
},
{
name: "Homogeneous Aggregate Validation test 3",
exp: `name in {'hello':'world', 'goodbye':true}`,
wantErr: true,
skip: true, // https://github.com/polds/expr-playground/issues/31
name: "Homogeneous Aggregate Validation test 2",
exp: `{'hello':'world', 1:'!'}`, // Expr casts the integer to a string literal. This may be a bug.
shouldCompile: true,
shouldRun: true,
},
{
name: "Homogeneous Aggregate Validation test 4",
exp: `name in ['hello', 'world']`,
skip: true, // https://github.com/polds/expr-playground/issues/31
name: "Homogeneous Aggregate Validation test 3",
exp: `name in {'hello':'world', 'goodbye':true}`, // Expr correctly handles the boolean value.
shouldCompile: true,
shouldRun: true,
},
{
name: "Homogeneous Aggregate Validation test 5",
exp: `name in ['hello', ?optional.ofNonZeroValue('')]`,
skip: true, // https://github.com/polds/expr-playground/issues/32
name: "Homogeneous Aggregate Validation test 4",
exp: `name in ['hello', 'world']`, // This is a valid Expr expression.
shouldCompile: true,
shouldRun: true,
},
{
name: "Homogeneous Aggregate Validation test 6",
exp: `name in [?optional.ofNonZeroValue(''), 'hello', ?optional.of('')]`,
skip: true, // https://github.com/polds/expr-playground/issues/33
name: "Homogeneous Aggregate Validation test 5",
exp: `name in ['hello', optional ?? '']`,
shouldCompile: true,
shouldRun: true,
},
{
name: "Homogeneous Aggregate Validation test 7",
exp: `name in {'hello': false, 'world': true}`,
name: "Homogeneous Aggregate Validation test 7",
exp: `name in {'hello': false, 'world': true}`, // Expr allows this even though the compared type is a string.
shouldCompile: true,
shouldRun: true,
},
{
name: "Homogeneous Aggregate Validation test 8",
exp: `{'hello': false, ?'world': optional.ofNonZeroValue(true)}`,
skip: true, // https://github.com/polds/expr-playground/issues/34
name: "Homogeneous Aggregate Validation test 8",
exp: `{'hello': false, 'world': optional ?? true }`,
shouldCompile: true,
shouldRun: true,
},
{
name: "Homogeneous Aggregate Validation test 9",
exp: `{?'hello': optional.ofNonZeroValue(false), 'world': true}`,
skip: true, // https://github.com/polds/expr-playground/issues/35
name: "Homogeneous Aggregate Validation test 9",
exp: `{'hello': optional ?? false, 'world': true}`,
shouldCompile: true,
shouldRun: true,
},
}

env := map[string]any{
"x": "",
"name": "",
}
opts := append(exprEnvOptions, expr.Env(env))
opts := append(exprEnvOptions, expr.Env(env), expr.AllowUndefinedVariables())

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.skip {
t.Skip("Skipping broken test due to CEL -> Expr migration.")
}

_, err := expr.Compile(tt.exp, opts...)
if (err != nil) != tt.wantErr {
t.Errorf("Compile() got error = %v, wantErr %t", err, tt.wantErr)
return
program, err := expr.Compile(tt.exp, opts...)
if (err != nil) == tt.shouldCompile {
t.Errorf("Compile() got error = %v, shouldCompile %t", err, tt.shouldCompile)
}

_, err = expr.Run(program, env)
if (err != nil) == tt.shouldRun {
t.Errorf("Run() got error = %v, shouldRun %t", err, tt.shouldRun)
}
})
}
Expand Down