Replace expressions engine (#133)

This commit is contained in:
ChristopherHX
2025-10-06 13:53:15 +02:00
committed by GitHub
parent 418c708bb0
commit 82dccc7820
40 changed files with 6876 additions and 1304 deletions

View File

@@ -17,9 +17,9 @@ func TestLiterals(t *testing.T) {
{"true", true, "true"},
{"false", false, "false"},
{"null", nil, "null"},
{"123", 123, "integer"},
{"123", float64(123), "integer"},
{"-9.7", -9.7, "float"},
{"0xff", 255, "hex"},
{"0xff", float64(255), "hex"},
{"-2.99e-2", -2.99e-2, "exponential"},
{"'foo'", "foo", "string"},
{"'it''s foo'", "it's foo", "string"},
@@ -50,10 +50,11 @@ func TestOperators(t *testing.T) {
{"github.action[0]", nil, "string-index", ""},
{"github.action['0']", nil, "string-index", ""},
{"fromJSON('[0,1]')[1]", 1.0, "array-index", ""},
{"fromJSON('[0,1]')[1.1]", nil, "array-index", ""},
// Disabled weird things are happening
// {"fromJSON('[0,1]')['1.1']", nil, "array-index", ""},
{"(github.event.commits.*.author.username)[0]", "someone", "array-index-0", ""},
{"fromJSON('[0,1]')[1.1]", 1.0, "array-index", ""},
{"fromJSON('[0,1]')['1.1']", nil, "array-index", ""},
// Invalid Test
// {"(github.event.commits.*.author.username)[0]", "someone", "array-index-0", ""},
{"fromjson(tojson(github.event.commits.*.author.username))[0]", "someone", "array-index-0", ""},
{"fromJSON('[0,1]')[2]", nil, "array-index-out-of-bounds-0", ""},
{"fromJSON('[0,1]')[34553]", nil, "array-index-out-of-bounds-1", ""},
{"fromJSON('[0,1]')[-1]", nil, "array-index-out-of-bounds-2", ""},
@@ -72,8 +73,9 @@ func TestOperators(t *testing.T) {
{"github.event.commits[0].author.username != github.event.commits[1].author.username", true, "property-comparison1", ""},
{"github.event.commits[0].author.username1 != github.event.commits[1].author.username", true, "property-comparison2", ""},
{"github.event.commits[0].author.username != github.event.commits[1].author.username1", true, "property-comparison3", ""},
{"github.event.commits[0].author.username1 != github.event.commits[1].author.username2", true, "property-comparison4", ""},
{"secrets != env", nil, "property-comparison5", "compare not implemented for types: left: map, right: map"},
{"github.event.commits[0].author.username1 != github.event.commits[1].author.username2", false, "property-comparison4", ""},
{"secrets != env", true, "property-comparison5", ""},
{"job.container && 'failure' || 'ok'", "ok", "object-truth", ""},
}
env := &EvaluationEnvironment{
@@ -175,7 +177,7 @@ func TestOperatorsBooleanEvaluation(t *testing.T) {
{"true && 3.14", 3.14, "true-and"},
{"true && 0.0", 0, "true-and"},
{"true && Infinity", math.Inf(1), "true-and"},
// {"true && -Infinity", math.Inf(-1), "true-and"},
{"true && -Infinity", math.Inf(-1), "true-and"},
{"true && NaN", math.NaN(), "true-and"},
{"true && ''", "", "true-and"},
{"true && 'abc'", "abc", "true-and"},
@@ -189,7 +191,7 @@ func TestOperatorsBooleanEvaluation(t *testing.T) {
{"false && 3.14", false, "false-and"},
{"false && 0.0", false, "false-and"},
{"false && Infinity", false, "false-and"},
// {"false && -Infinity", false, "false-and"},
{"false && -Infinity", false, "false-and"},
{"false && NaN", false, "false-and"},
{"false && ''", false, "false-and"},
{"false && 'abc'", false, "false-and"},
@@ -203,7 +205,7 @@ func TestOperatorsBooleanEvaluation(t *testing.T) {
{"true || 3.14", true, "true-or"},
{"true || 0.0", true, "true-or"},
{"true || Infinity", true, "true-or"},
// {"true || -Infinity", true, "true-or"},
{"true || -Infinity", true, "true-or"},
{"true || NaN", true, "true-or"},
{"true || ''", true, "true-or"},
{"true || 'abc'", true, "true-or"},
@@ -217,7 +219,7 @@ func TestOperatorsBooleanEvaluation(t *testing.T) {
{"false || 3.14", 3.14, "false-or"},
{"false || 0.0", 0, "false-or"},
{"false || Infinity", math.Inf(1), "false-or"},
// {"false || -Infinity", math.Inf(-1), "false-or"},
{"false || -Infinity", math.Inf(-1), "false-or"},
{"false || NaN", math.NaN(), "false-or"},
{"false || ''", "", "false-or"},
{"false || 'abc'", "abc", "false-or"},
@@ -231,7 +233,7 @@ func TestOperatorsBooleanEvaluation(t *testing.T) {
{"null && 3.14", nil, "null-and"},
{"null && 0.0", nil, "null-and"},
{"null && Infinity", nil, "null-and"},
// {"null && -Infinity", nil, "null-and"},
{"null && -Infinity", nil, "null-and"},
{"null && NaN", nil, "null-and"},
{"null && ''", nil, "null-and"},
{"null && 'abc'", nil, "null-and"},
@@ -245,7 +247,7 @@ func TestOperatorsBooleanEvaluation(t *testing.T) {
{"null || 3.14", 3.14, "null-or"},
{"null || 0.0", 0, "null-or"},
{"null || Infinity", math.Inf(1), "null-or"},
// {"null || -Infinity", math.Inf(-1), "null-or"},
{"null || -Infinity", math.Inf(-1), "null-or"},
{"null || NaN", math.NaN(), "null-or"},
{"null || ''", "", "null-or"},
{"null || 'abc'", "abc", "null-or"},
@@ -259,7 +261,7 @@ func TestOperatorsBooleanEvaluation(t *testing.T) {
{"-10 && 3.14", 3.14, "neg-num-and"},
{"-10 && 0.0", 0, "neg-num-and"},
{"-10 && Infinity", math.Inf(1), "neg-num-and"},
// {"-10 && -Infinity", math.Inf(-1), "neg-num-and"},
{"-10 && -Infinity", math.Inf(-1), "neg-num-and"},
{"-10 && NaN", math.NaN(), "neg-num-and"},
{"-10 && ''", "", "neg-num-and"},
{"-10 && 'abc'", "abc", "neg-num-and"},
@@ -273,7 +275,7 @@ func TestOperatorsBooleanEvaluation(t *testing.T) {
{"-10 || 3.14", -10, "neg-num-or"},
{"-10 || 0.0", -10, "neg-num-or"},
{"-10 || Infinity", -10, "neg-num-or"},
// {"-10 || -Infinity", -10, "neg-num-or"},
{"-10 || -Infinity", -10, "neg-num-or"},
{"-10 || NaN", -10, "neg-num-or"},
{"-10 || ''", -10, "neg-num-or"},
{"-10 || 'abc'", -10, "neg-num-or"},
@@ -287,7 +289,7 @@ func TestOperatorsBooleanEvaluation(t *testing.T) {
{"0 && 3.14", 0, "zero-and"},
{"0 && 0.0", 0, "zero-and"},
{"0 && Infinity", 0, "zero-and"},
// {"0 && -Infinity", 0, "zero-and"},
{"0 && -Infinity", 0, "zero-and"},
{"0 && NaN", 0, "zero-and"},
{"0 && ''", 0, "zero-and"},
{"0 && 'abc'", 0, "zero-and"},
@@ -301,7 +303,7 @@ func TestOperatorsBooleanEvaluation(t *testing.T) {
{"0 || 3.14", 3.14, "zero-or"},
{"0 || 0.0", 0, "zero-or"},
{"0 || Infinity", math.Inf(1), "zero-or"},
// {"0 || -Infinity", math.Inf(-1), "zero-or"},
{"0 || -Infinity", math.Inf(-1), "zero-or"},
{"0 || NaN", math.NaN(), "zero-or"},
{"0 || ''", "", "zero-or"},
{"0 || 'abc'", "abc", "zero-or"},
@@ -343,7 +345,7 @@ func TestOperatorsBooleanEvaluation(t *testing.T) {
{"3.14 && 3.14", 3.14, "pos-float-and"},
{"3.14 && 0.0", 0, "pos-float-and"},
{"3.14 && Infinity", math.Inf(1), "pos-float-and"},
// {"3.14 && -Infinity", math.Inf(-1), "pos-float-and"},
{"3.14 && -Infinity", math.Inf(-1), "pos-float-and"},
{"3.14 && NaN", math.NaN(), "pos-float-and"},
{"3.14 && ''", "", "pos-float-and"},
{"3.14 && 'abc'", "abc", "pos-float-and"},
@@ -357,7 +359,7 @@ func TestOperatorsBooleanEvaluation(t *testing.T) {
{"3.14 || 3.14", 3.14, "pos-float-or"},
{"3.14 || 0.0", 3.14, "pos-float-or"},
{"3.14 || Infinity", 3.14, "pos-float-or"},
// {"3.14 || -Infinity", 3.14, "pos-float-or"},
{"3.14 || -Infinity", 3.14, "pos-float-or"},
{"3.14 || NaN", 3.14, "pos-float-or"},
{"3.14 || ''", 3.14, "pos-float-or"},
{"3.14 || 'abc'", 3.14, "pos-float-or"},
@@ -371,7 +373,7 @@ func TestOperatorsBooleanEvaluation(t *testing.T) {
{"Infinity && 3.14", 3.14, "pos-inf-and"},
{"Infinity && 0.0", 0, "pos-inf-and"},
{"Infinity && Infinity", math.Inf(1), "pos-inf-and"},
// {"Infinity && -Infinity", math.Inf(-1), "pos-inf-and"},
{"Infinity && -Infinity", math.Inf(-1), "pos-inf-and"},
{"Infinity && NaN", math.NaN(), "pos-inf-and"},
{"Infinity && ''", "", "pos-inf-and"},
{"Infinity && 'abc'", "abc", "pos-inf-and"},
@@ -385,38 +387,38 @@ func TestOperatorsBooleanEvaluation(t *testing.T) {
{"Infinity || 3.14", math.Inf(1), "pos-inf-or"},
{"Infinity || 0.0", math.Inf(1), "pos-inf-or"},
{"Infinity || Infinity", math.Inf(1), "pos-inf-or"},
// {"Infinity || -Infinity", math.Inf(1), "pos-inf-or"},
{"Infinity || -Infinity", math.Inf(1), "pos-inf-or"},
{"Infinity || NaN", math.Inf(1), "pos-inf-or"},
{"Infinity || ''", math.Inf(1), "pos-inf-or"},
{"Infinity || 'abc'", math.Inf(1), "pos-inf-or"},
// -Infinity &&
// {"-Infinity && true", true, "neg-inf-and"},
// {"-Infinity && false", false, "neg-inf-and"},
// {"-Infinity && null", nil, "neg-inf-and"},
// {"-Infinity && -10", -10, "neg-inf-and"},
// {"-Infinity && 0", 0, "neg-inf-and"},
// {"-Infinity && 10", 10, "neg-inf-and"},
// {"-Infinity && 3.14", 3.14, "neg-inf-and"},
// {"-Infinity && 0.0", 0, "neg-inf-and"},
// {"-Infinity && Infinity", math.Inf(1), "neg-inf-and"},
// {"-Infinity && -Infinity", math.Inf(-1), "neg-inf-and"},
// {"-Infinity && NaN", math.NaN(), "neg-inf-and"},
// {"-Infinity && ''", "", "neg-inf-and"},
// {"-Infinity && 'abc'", "abc", "neg-inf-and"},
{"-Infinity && true", true, "neg-inf-and"},
{"-Infinity && false", false, "neg-inf-and"},
{"-Infinity && null", nil, "neg-inf-and"},
{"-Infinity && -10", -10, "neg-inf-and"},
{"-Infinity && 0", 0, "neg-inf-and"},
{"-Infinity && 10", 10, "neg-inf-and"},
{"-Infinity && 3.14", 3.14, "neg-inf-and"},
{"-Infinity && 0.0", 0, "neg-inf-and"},
{"-Infinity && Infinity", math.Inf(1), "neg-inf-and"},
{"-Infinity && -Infinity", math.Inf(-1), "neg-inf-and"},
{"-Infinity && NaN", math.NaN(), "neg-inf-and"},
{"-Infinity && ''", "", "neg-inf-and"},
{"-Infinity && 'abc'", "abc", "neg-inf-and"},
// -Infinity ||
// {"-Infinity || true", math.Inf(-1), "neg-inf-or"},
// {"-Infinity || false", math.Inf(-1), "neg-inf-or"},
// {"-Infinity || null", math.Inf(-1), "neg-inf-or"},
// {"-Infinity || -10", math.Inf(-1), "neg-inf-or"},
// {"-Infinity || 0", math.Inf(-1), "neg-inf-or"},
// {"-Infinity || 10", math.Inf(-1), "neg-inf-or"},
// {"-Infinity || 3.14", math.Inf(-1), "neg-inf-or"},
// {"-Infinity || 0.0", math.Inf(-1), "neg-inf-or"},
// {"-Infinity || Infinity", math.Inf(-1), "neg-inf-or"},
// {"-Infinity || -Infinity", math.Inf(-1), "neg-inf-or"},
// {"-Infinity || NaN", math.Inf(-1), "neg-inf-or"},
// {"-Infinity || ''", math.Inf(-1), "neg-inf-or"},
// {"-Infinity || 'abc'", math.Inf(-1), "neg-inf-or"},
{"-Infinity || true", math.Inf(-1), "neg-inf-or"},
{"-Infinity || false", math.Inf(-1), "neg-inf-or"},
{"-Infinity || null", math.Inf(-1), "neg-inf-or"},
{"-Infinity || -10", math.Inf(-1), "neg-inf-or"},
{"-Infinity || 0", math.Inf(-1), "neg-inf-or"},
{"-Infinity || 10", math.Inf(-1), "neg-inf-or"},
{"-Infinity || 3.14", math.Inf(-1), "neg-inf-or"},
{"-Infinity || 0.0", math.Inf(-1), "neg-inf-or"},
{"-Infinity || Infinity", math.Inf(-1), "neg-inf-or"},
{"-Infinity || -Infinity", math.Inf(-1), "neg-inf-or"},
{"-Infinity || NaN", math.Inf(-1), "neg-inf-or"},
{"-Infinity || ''", math.Inf(-1), "neg-inf-or"},
{"-Infinity || 'abc'", math.Inf(-1), "neg-inf-or"},
// NaN &&
{"NaN && true", math.NaN(), "nan-and"},
{"NaN && false", math.NaN(), "nan-and"},
@@ -427,7 +429,7 @@ func TestOperatorsBooleanEvaluation(t *testing.T) {
{"NaN && 3.14", math.NaN(), "nan-and"},
{"NaN && 0.0", math.NaN(), "nan-and"},
{"NaN && Infinity", math.NaN(), "nan-and"},
// {"NaN && -Infinity", math.NaN(), "nan-and"},
{"NaN && -Infinity", math.NaN(), "nan-and"},
{"NaN && NaN", math.NaN(), "nan-and"},
{"NaN && ''", math.NaN(), "nan-and"},
{"NaN && 'abc'", math.NaN(), "nan-and"},
@@ -441,7 +443,7 @@ func TestOperatorsBooleanEvaluation(t *testing.T) {
{"NaN || 3.14", 3.14, "nan-or"},
{"NaN || 0.0", 0, "nan-or"},
{"NaN || Infinity", math.Inf(1), "nan-or"},
// {"NaN || -Infinity", math.Inf(-1), "nan-or"},
{"NaN || -Infinity", math.Inf(-1), "nan-or"},
{"NaN || NaN", math.NaN(), "nan-or"},
{"NaN || ''", "", "nan-or"},
{"NaN || 'abc'", "abc", "nan-or"},
@@ -455,7 +457,7 @@ func TestOperatorsBooleanEvaluation(t *testing.T) {
{"'' && 3.14", "", "empty-str-and"},
{"'' && 0.0", "", "empty-str-and"},
{"'' && Infinity", "", "empty-str-and"},
// {"'' && -Infinity", "", "empty-str-and"},
{"'' && -Infinity", "", "empty-str-and"},
{"'' && NaN", "", "empty-str-and"},
{"'' && ''", "", "empty-str-and"},
{"'' && 'abc'", "", "empty-str-and"},
@@ -469,7 +471,7 @@ func TestOperatorsBooleanEvaluation(t *testing.T) {
{"'' || 3.14", 3.14, "empty-str-or"},
{"'' || 0.0", 0, "empty-str-or"},
{"'' || Infinity", math.Inf(1), "empty-str-or"},
// {"'' || -Infinity", math.Inf(-1), "empty-str-or"},
{"'' || -Infinity", math.Inf(-1), "empty-str-or"},
{"'' || NaN", math.NaN(), "empty-str-or"},
{"'' || ''", "", "empty-str-or"},
{"'' || 'abc'", "abc", "empty-str-or"},
@@ -483,7 +485,7 @@ func TestOperatorsBooleanEvaluation(t *testing.T) {
{"'abc' && 3.14", 3.14, "str-and"},
{"'abc' && 0.0", 0, "str-and"},
{"'abc' && Infinity", math.Inf(1), "str-and"},
// {"'abc' && -Infinity", math.Inf(-1), "str-and"},
{"'abc' && -Infinity", math.Inf(-1), "str-and"},
{"'abc' && NaN", math.NaN(), "str-and"},
{"'abc' && ''", "", "str-and"},
{"'abc' && 'abc'", "abc", "str-and"},
@@ -497,7 +499,7 @@ func TestOperatorsBooleanEvaluation(t *testing.T) {
{"'abc' || 3.14", "abc", "str-or"},
{"'abc' || 0.0", "abc", "str-or"},
{"'abc' || Infinity", "abc", "str-or"},
// {"'abc' || -Infinity", "abc", "str-or"},
{"'abc' || -Infinity", "abc", "str-or"},
{"'abc' || NaN", "abc", "str-or"},
{"'abc' || ''", "abc", "str-or"},
{"'abc' || 'abc'", "abc", "str-or"},
@@ -517,6 +519,11 @@ func TestOperatorsBooleanEvaluation(t *testing.T) {
output, err := NewInterpeter(env, Config{}).Evaluate(tt.input, DefaultStatusCheckNone)
assert.Nil(t, err)
// Normalize int => float64
if i, ok := tt.expected.(int); ok {
tt.expected = (float64)(i)
}
if expected, ok := tt.expected.(float64); ok && math.IsNaN(expected) {
assert.True(t, math.IsNaN(output.(float64)))
} else {
@@ -543,9 +550,9 @@ func TestContexts(t *testing.T) {
{input: "github.event.pull_request.labels.*.name", expected: nil, name: "github-context-noexist-prop"},
{input: "env.TEST", expected: "value", name: "env-context"},
{input: "env.TEST", expected: "value", name: "env-context", caseSensitiveEnv: true},
{input: "env.test", expected: "", name: "env-context", caseSensitiveEnv: true},
{input: "env.test", expected: nil, name: "env-context", caseSensitiveEnv: true},
{input: "env['TEST']", expected: "value", name: "env-context", caseSensitiveEnv: true},
{input: "env['test']", expected: "", name: "env-context", caseSensitiveEnv: true},
{input: "env['test']", expected: nil, name: "env-context", caseSensitiveEnv: true},
{input: "env.test", expected: "value", name: "env-context"},
{input: "job.status", expected: "success", name: "job-context"},
{input: "steps.step-id.outputs.name", expected: "value", name: "steps-context"},
@@ -561,10 +568,9 @@ func TestContexts(t *testing.T) {
{input: "steps['step-id']['outcome'] && true", expected: true, name: "steps-context-outcome"},
{input: "steps.step-id2.outcome", expected: "failure", name: "steps-context-outcome"},
{input: "steps.step-id2.outcome && true", expected: true, name: "steps-context-outcome"},
// Disabled, since the interpreter is still too broken
// {"contains(steps.*.outcome, 'success')", true, "steps-context-array-outcome"},
// {"contains(steps.*.outcome, 'failure')", true, "steps-context-array-outcome"},
// {"contains(steps.*.outputs.name, 'value')", true, "steps-context-array-outputs"},
{input: "contains(steps.*.outcome, 'success')", expected: true, name: "steps-context-array-outcome"},
{input: "contains(steps.*.outcome, 'failure')", expected: true, name: "steps-context-array-outcome"},
{input: "contains(steps.*.outputs.name, 'value')", expected: true, name: "steps-context-array-outputs"},
{input: "runner.os", expected: "Linux", name: "runner-context"},
{input: "secrets.name", expected: "value", name: "secrets-context"},
{input: "vars.name", expected: "value", name: "vars-context"},