github.com/nektos/act@v0.2.63/pkg/exprparser/interpreter_test.go (about)

     1  package exprparser
     2  
     3  import (
     4  	"math"
     5  	"testing"
     6  
     7  	"github.com/nektos/act/pkg/model"
     8  	"github.com/stretchr/testify/assert"
     9  )
    10  
    11  func TestLiterals(t *testing.T) {
    12  	table := []struct {
    13  		input    string
    14  		expected interface{}
    15  		name     string
    16  	}{
    17  		{"true", true, "true"},
    18  		{"false", false, "false"},
    19  		{"null", nil, "null"},
    20  		{"123", 123, "integer"},
    21  		{"-9.7", -9.7, "float"},
    22  		{"0xff", 255, "hex"},
    23  		{"-2.99e-2", -2.99e-2, "exponential"},
    24  		{"'foo'", "foo", "string"},
    25  		{"'it''s foo'", "it's foo", "string"},
    26  	}
    27  
    28  	env := &EvaluationEnvironment{}
    29  
    30  	for _, tt := range table {
    31  		t.Run(tt.name, func(t *testing.T) {
    32  			output, err := NewInterpeter(env, Config{}).Evaluate(tt.input, DefaultStatusCheckNone)
    33  			assert.Nil(t, err)
    34  
    35  			assert.Equal(t, tt.expected, output)
    36  		})
    37  	}
    38  }
    39  
    40  func TestOperators(t *testing.T) {
    41  	table := []struct {
    42  		input    string
    43  		expected interface{}
    44  		name     string
    45  		error    string
    46  	}{
    47  		{"(false || (false || true))", true, "logical-grouping", ""},
    48  		{"github.action", "push", "property-dereference", ""},
    49  		{"github['action']", "push", "property-index", ""},
    50  		{"github.action[0]", nil, "string-index", ""},
    51  		{"github.action['0']", nil, "string-index", ""},
    52  		{"fromJSON('[0,1]')[1]", 1.0, "array-index", ""},
    53  		{"fromJSON('[0,1]')[1.1]", nil, "array-index", ""},
    54  		// Disabled weird things are happening
    55  		// {"fromJSON('[0,1]')['1.1']", nil, "array-index", ""},
    56  		{"(github.event.commits.*.author.username)[0]", "someone", "array-index-0", ""},
    57  		{"fromJSON('[0,1]')[2]", nil, "array-index-out-of-bounds-0", ""},
    58  		{"fromJSON('[0,1]')[34553]", nil, "array-index-out-of-bounds-1", ""},
    59  		{"fromJSON('[0,1]')[-1]", nil, "array-index-out-of-bounds-2", ""},
    60  		{"fromJSON('[0,1]')[-34553]", nil, "array-index-out-of-bounds-3", ""},
    61  		{"!true", false, "not", ""},
    62  		{"1 < 2", true, "less-than", ""},
    63  		{`'b' <= 'a'`, false, "less-than-or-equal", ""},
    64  		{"1 > 2", false, "greater-than", ""},
    65  		{`'b' >= 'a'`, true, "greater-than-or-equal", ""},
    66  		{`'a' == 'a'`, true, "equal", ""},
    67  		{`'a' != 'a'`, false, "not-equal", ""},
    68  		{`true && false`, false, "and", ""},
    69  		{`true || false`, true, "or", ""},
    70  		{`fromJSON('{}') && true`, true, "and-boolean-object", ""},
    71  		{`fromJSON('{}') || false`, make(map[string]interface{}), "or-boolean-object", ""},
    72  		{"github.event.commits[0].author.username != github.event.commits[1].author.username", true, "property-comparison1", ""},
    73  		{"github.event.commits[0].author.username1 != github.event.commits[1].author.username", true, "property-comparison2", ""},
    74  		{"github.event.commits[0].author.username != github.event.commits[1].author.username1", true, "property-comparison3", ""},
    75  		{"github.event.commits[0].author.username1 != github.event.commits[1].author.username2", true, "property-comparison4", ""},
    76  		{"secrets != env", nil, "property-comparison5", "Compare not implemented for types: left: map, right: map"},
    77  	}
    78  
    79  	env := &EvaluationEnvironment{
    80  		Github: &model.GithubContext{
    81  			Action: "push",
    82  			Event: map[string]interface{}{
    83  				"commits": []interface{}{
    84  					map[string]interface{}{
    85  						"author": map[string]interface{}{
    86  							"username": "someone",
    87  						},
    88  					},
    89  					map[string]interface{}{
    90  						"author": map[string]interface{}{
    91  							"username": "someone-else",
    92  						},
    93  					},
    94  				},
    95  			},
    96  		},
    97  	}
    98  
    99  	for _, tt := range table {
   100  		t.Run(tt.name, func(t *testing.T) {
   101  			output, err := NewInterpeter(env, Config{}).Evaluate(tt.input, DefaultStatusCheckNone)
   102  			if tt.error != "" {
   103  				assert.NotNil(t, err)
   104  				assert.Equal(t, tt.error, err.Error())
   105  			} else {
   106  				assert.Nil(t, err)
   107  			}
   108  
   109  			assert.Equal(t, tt.expected, output)
   110  		})
   111  	}
   112  }
   113  
   114  func TestOperatorsCompare(t *testing.T) {
   115  	table := []struct {
   116  		input    string
   117  		expected interface{}
   118  		name     string
   119  	}{
   120  		{"!null", true, "not-null"},
   121  		{"!-10", false, "not-neg-num"},
   122  		{"!0", true, "not-zero"},
   123  		{"!3.14", false, "not-pos-float"},
   124  		{"!''", true, "not-empty-str"},
   125  		{"!'abc'", false, "not-str"},
   126  		{"!fromJSON('{}')", false, "not-obj"},
   127  		{"!fromJSON('[]')", false, "not-arr"},
   128  		{`null == 0 }}`, true, "null-coercion"},
   129  		{`true == 1 }}`, true, "boolean-coercion"},
   130  		{`'' == 0 }}`, true, "string-0-coercion"},
   131  		{`'3' == 3 }}`, true, "string-3-coercion"},
   132  		{`0 == null }}`, true, "null-coercion-alt"},
   133  		{`1 == true }}`, true, "boolean-coercion-alt"},
   134  		{`0 == '' }}`, true, "string-0-coercion-alt"},
   135  		{`3 == '3' }}`, true, "string-3-coercion-alt"},
   136  		{`'TEST' == 'test' }}`, true, "string-casing"},
   137  		{"true > false }}", true, "bool-greater-than"},
   138  		{"true >= false }}", true, "bool-greater-than-eq"},
   139  		{"true >= true }}", true, "bool-greater-than-1"},
   140  		{"true != false }}", true, "bool-not-equal"},
   141  		{`fromJSON('{}') < 2 }}`, false, "object-with-less"},
   142  		{`fromJSON('{}') < fromJSON('[]') }}`, false, "object/arr-with-lt"},
   143  		{`fromJSON('{}') > fromJSON('[]') }}`, false, "object/arr-with-gt"},
   144  	}
   145  
   146  	env := &EvaluationEnvironment{
   147  		Github: &model.GithubContext{
   148  			Action: "push",
   149  		},
   150  	}
   151  
   152  	for _, tt := range table {
   153  		t.Run(tt.name, func(t *testing.T) {
   154  			output, err := NewInterpeter(env, Config{}).Evaluate(tt.input, DefaultStatusCheckNone)
   155  			assert.Nil(t, err)
   156  
   157  			assert.Equal(t, tt.expected, output)
   158  		})
   159  	}
   160  }
   161  
   162  func TestOperatorsBooleanEvaluation(t *testing.T) {
   163  	table := []struct {
   164  		input    string
   165  		expected interface{}
   166  		name     string
   167  	}{
   168  		// true &&
   169  		{"true && true", true, "true-and"},
   170  		{"true && false", false, "true-and"},
   171  		{"true && null", nil, "true-and"},
   172  		{"true && -10", -10, "true-and"},
   173  		{"true && 0", 0, "true-and"},
   174  		{"true && 10", 10, "true-and"},
   175  		{"true && 3.14", 3.14, "true-and"},
   176  		{"true && 0.0", 0, "true-and"},
   177  		{"true && Infinity", math.Inf(1), "true-and"},
   178  		// {"true && -Infinity", math.Inf(-1), "true-and"},
   179  		{"true && NaN", math.NaN(), "true-and"},
   180  		{"true && ''", "", "true-and"},
   181  		{"true && 'abc'", "abc", "true-and"},
   182  		// false &&
   183  		{"false && true", false, "false-and"},
   184  		{"false && false", false, "false-and"},
   185  		{"false && null", false, "false-and"},
   186  		{"false && -10", false, "false-and"},
   187  		{"false && 0", false, "false-and"},
   188  		{"false && 10", false, "false-and"},
   189  		{"false && 3.14", false, "false-and"},
   190  		{"false && 0.0", false, "false-and"},
   191  		{"false && Infinity", false, "false-and"},
   192  		// {"false && -Infinity", false, "false-and"},
   193  		{"false && NaN", false, "false-and"},
   194  		{"false && ''", false, "false-and"},
   195  		{"false && 'abc'", false, "false-and"},
   196  		// true ||
   197  		{"true || true", true, "true-or"},
   198  		{"true || false", true, "true-or"},
   199  		{"true || null", true, "true-or"},
   200  		{"true || -10", true, "true-or"},
   201  		{"true || 0", true, "true-or"},
   202  		{"true || 10", true, "true-or"},
   203  		{"true || 3.14", true, "true-or"},
   204  		{"true || 0.0", true, "true-or"},
   205  		{"true || Infinity", true, "true-or"},
   206  		// {"true || -Infinity", true, "true-or"},
   207  		{"true || NaN", true, "true-or"},
   208  		{"true || ''", true, "true-or"},
   209  		{"true || 'abc'", true, "true-or"},
   210  		// false ||
   211  		{"false || true", true, "false-or"},
   212  		{"false || false", false, "false-or"},
   213  		{"false || null", nil, "false-or"},
   214  		{"false || -10", -10, "false-or"},
   215  		{"false || 0", 0, "false-or"},
   216  		{"false || 10", 10, "false-or"},
   217  		{"false || 3.14", 3.14, "false-or"},
   218  		{"false || 0.0", 0, "false-or"},
   219  		{"false || Infinity", math.Inf(1), "false-or"},
   220  		// {"false || -Infinity", math.Inf(-1), "false-or"},
   221  		{"false || NaN", math.NaN(), "false-or"},
   222  		{"false || ''", "", "false-or"},
   223  		{"false || 'abc'", "abc", "false-or"},
   224  		// null &&
   225  		{"null && true", nil, "null-and"},
   226  		{"null && false", nil, "null-and"},
   227  		{"null && null", nil, "null-and"},
   228  		{"null && -10", nil, "null-and"},
   229  		{"null && 0", nil, "null-and"},
   230  		{"null && 10", nil, "null-and"},
   231  		{"null && 3.14", nil, "null-and"},
   232  		{"null && 0.0", nil, "null-and"},
   233  		{"null && Infinity", nil, "null-and"},
   234  		// {"null && -Infinity", nil, "null-and"},
   235  		{"null && NaN", nil, "null-and"},
   236  		{"null && ''", nil, "null-and"},
   237  		{"null && 'abc'", nil, "null-and"},
   238  		// null ||
   239  		{"null || true", true, "null-or"},
   240  		{"null || false", false, "null-or"},
   241  		{"null || null", nil, "null-or"},
   242  		{"null || -10", -10, "null-or"},
   243  		{"null || 0", 0, "null-or"},
   244  		{"null || 10", 10, "null-or"},
   245  		{"null || 3.14", 3.14, "null-or"},
   246  		{"null || 0.0", 0, "null-or"},
   247  		{"null || Infinity", math.Inf(1), "null-or"},
   248  		// {"null || -Infinity", math.Inf(-1), "null-or"},
   249  		{"null || NaN", math.NaN(), "null-or"},
   250  		{"null || ''", "", "null-or"},
   251  		{"null || 'abc'", "abc", "null-or"},
   252  		// -10 &&
   253  		{"-10 && true", true, "neg-num-and"},
   254  		{"-10 && false", false, "neg-num-and"},
   255  		{"-10 && null", nil, "neg-num-and"},
   256  		{"-10 && -10", -10, "neg-num-and"},
   257  		{"-10 && 0", 0, "neg-num-and"},
   258  		{"-10 && 10", 10, "neg-num-and"},
   259  		{"-10 && 3.14", 3.14, "neg-num-and"},
   260  		{"-10 && 0.0", 0, "neg-num-and"},
   261  		{"-10 && Infinity", math.Inf(1), "neg-num-and"},
   262  		// {"-10 && -Infinity", math.Inf(-1), "neg-num-and"},
   263  		{"-10 && NaN", math.NaN(), "neg-num-and"},
   264  		{"-10 && ''", "", "neg-num-and"},
   265  		{"-10 && 'abc'", "abc", "neg-num-and"},
   266  		// -10 ||
   267  		{"-10 || true", -10, "neg-num-or"},
   268  		{"-10 || false", -10, "neg-num-or"},
   269  		{"-10 || null", -10, "neg-num-or"},
   270  		{"-10 || -10", -10, "neg-num-or"},
   271  		{"-10 || 0", -10, "neg-num-or"},
   272  		{"-10 || 10", -10, "neg-num-or"},
   273  		{"-10 || 3.14", -10, "neg-num-or"},
   274  		{"-10 || 0.0", -10, "neg-num-or"},
   275  		{"-10 || Infinity", -10, "neg-num-or"},
   276  		// {"-10 || -Infinity", -10, "neg-num-or"},
   277  		{"-10 || NaN", -10, "neg-num-or"},
   278  		{"-10 || ''", -10, "neg-num-or"},
   279  		{"-10 || 'abc'", -10, "neg-num-or"},
   280  		// 0 &&
   281  		{"0 && true", 0, "zero-and"},
   282  		{"0 && false", 0, "zero-and"},
   283  		{"0 && null", 0, "zero-and"},
   284  		{"0 && -10", 0, "zero-and"},
   285  		{"0 && 0", 0, "zero-and"},
   286  		{"0 && 10", 0, "zero-and"},
   287  		{"0 && 3.14", 0, "zero-and"},
   288  		{"0 && 0.0", 0, "zero-and"},
   289  		{"0 && Infinity", 0, "zero-and"},
   290  		// {"0 && -Infinity", 0, "zero-and"},
   291  		{"0 && NaN", 0, "zero-and"},
   292  		{"0 && ''", 0, "zero-and"},
   293  		{"0 && 'abc'", 0, "zero-and"},
   294  		// 0 ||
   295  		{"0 || true", true, "zero-or"},
   296  		{"0 || false", false, "zero-or"},
   297  		{"0 || null", nil, "zero-or"},
   298  		{"0 || -10", -10, "zero-or"},
   299  		{"0 || 0", 0, "zero-or"},
   300  		{"0 || 10", 10, "zero-or"},
   301  		{"0 || 3.14", 3.14, "zero-or"},
   302  		{"0 || 0.0", 0, "zero-or"},
   303  		{"0 || Infinity", math.Inf(1), "zero-or"},
   304  		// {"0 || -Infinity", math.Inf(-1), "zero-or"},
   305  		{"0 || NaN", math.NaN(), "zero-or"},
   306  		{"0 || ''", "", "zero-or"},
   307  		{"0 || 'abc'", "abc", "zero-or"},
   308  		// 10 &&
   309  		{"10 && true", true, "pos-num-and"},
   310  		{"10 && false", false, "pos-num-and"},
   311  		{"10 && null", nil, "pos-num-and"},
   312  		{"10 && -10", -10, "pos-num-and"},
   313  		{"10 && 0", 0, "pos-num-and"},
   314  		{"10 && 10", 10, "pos-num-and"},
   315  		{"10 && 3.14", 3.14, "pos-num-and"},
   316  		{"10 && 0.0", 0, "pos-num-and"},
   317  		{"10 && Infinity", math.Inf(1), "pos-num-and"},
   318  		// {"10 && -Infinity", math.Inf(-1), "pos-num-and"},
   319  		{"10 && NaN", math.NaN(), "pos-num-and"},
   320  		{"10 && ''", "", "pos-num-and"},
   321  		{"10 && 'abc'", "abc", "pos-num-and"},
   322  		// 10 ||
   323  		{"10 || true", 10, "pos-num-or"},
   324  		{"10 || false", 10, "pos-num-or"},
   325  		{"10 || null", 10, "pos-num-or"},
   326  		{"10 || -10", 10, "pos-num-or"},
   327  		{"10 || 0", 10, "pos-num-or"},
   328  		{"10 || 10", 10, "pos-num-or"},
   329  		{"10 || 3.14", 10, "pos-num-or"},
   330  		{"10 || 0.0", 10, "pos-num-or"},
   331  		{"10 || Infinity", 10, "pos-num-or"},
   332  		// {"10 || -Infinity", 10, "pos-num-or"},
   333  		{"10 || NaN", 10, "pos-num-or"},
   334  		{"10 || ''", 10, "pos-num-or"},
   335  		{"10 || 'abc'", 10, "pos-num-or"},
   336  		// 3.14 &&
   337  		{"3.14 && true", true, "pos-float-and"},
   338  		{"3.14 && false", false, "pos-float-and"},
   339  		{"3.14 && null", nil, "pos-float-and"},
   340  		{"3.14 && -10", -10, "pos-float-and"},
   341  		{"3.14 && 0", 0, "pos-float-and"},
   342  		{"3.14 && 10", 10, "pos-float-and"},
   343  		{"3.14 && 3.14", 3.14, "pos-float-and"},
   344  		{"3.14 && 0.0", 0, "pos-float-and"},
   345  		{"3.14 && Infinity", math.Inf(1), "pos-float-and"},
   346  		// {"3.14 && -Infinity", math.Inf(-1), "pos-float-and"},
   347  		{"3.14 && NaN", math.NaN(), "pos-float-and"},
   348  		{"3.14 && ''", "", "pos-float-and"},
   349  		{"3.14 && 'abc'", "abc", "pos-float-and"},
   350  		// 3.14 ||
   351  		{"3.14 || true", 3.14, "pos-float-or"},
   352  		{"3.14 || false", 3.14, "pos-float-or"},
   353  		{"3.14 || null", 3.14, "pos-float-or"},
   354  		{"3.14 || -10", 3.14, "pos-float-or"},
   355  		{"3.14 || 0", 3.14, "pos-float-or"},
   356  		{"3.14 || 10", 3.14, "pos-float-or"},
   357  		{"3.14 || 3.14", 3.14, "pos-float-or"},
   358  		{"3.14 || 0.0", 3.14, "pos-float-or"},
   359  		{"3.14 || Infinity", 3.14, "pos-float-or"},
   360  		// {"3.14 || -Infinity", 3.14, "pos-float-or"},
   361  		{"3.14 || NaN", 3.14, "pos-float-or"},
   362  		{"3.14 || ''", 3.14, "pos-float-or"},
   363  		{"3.14 || 'abc'", 3.14, "pos-float-or"},
   364  		// Infinity &&
   365  		{"Infinity && true", true, "pos-inf-and"},
   366  		{"Infinity && false", false, "pos-inf-and"},
   367  		{"Infinity && null", nil, "pos-inf-and"},
   368  		{"Infinity && -10", -10, "pos-inf-and"},
   369  		{"Infinity && 0", 0, "pos-inf-and"},
   370  		{"Infinity && 10", 10, "pos-inf-and"},
   371  		{"Infinity && 3.14", 3.14, "pos-inf-and"},
   372  		{"Infinity && 0.0", 0, "pos-inf-and"},
   373  		{"Infinity && Infinity", math.Inf(1), "pos-inf-and"},
   374  		// {"Infinity && -Infinity", math.Inf(-1), "pos-inf-and"},
   375  		{"Infinity && NaN", math.NaN(), "pos-inf-and"},
   376  		{"Infinity && ''", "", "pos-inf-and"},
   377  		{"Infinity && 'abc'", "abc", "pos-inf-and"},
   378  		// Infinity ||
   379  		{"Infinity || true", math.Inf(1), "pos-inf-or"},
   380  		{"Infinity || false", math.Inf(1), "pos-inf-or"},
   381  		{"Infinity || null", math.Inf(1), "pos-inf-or"},
   382  		{"Infinity || -10", math.Inf(1), "pos-inf-or"},
   383  		{"Infinity || 0", math.Inf(1), "pos-inf-or"},
   384  		{"Infinity || 10", math.Inf(1), "pos-inf-or"},
   385  		{"Infinity || 3.14", math.Inf(1), "pos-inf-or"},
   386  		{"Infinity || 0.0", math.Inf(1), "pos-inf-or"},
   387  		{"Infinity || Infinity", math.Inf(1), "pos-inf-or"},
   388  		// {"Infinity || -Infinity", math.Inf(1), "pos-inf-or"},
   389  		{"Infinity || NaN", math.Inf(1), "pos-inf-or"},
   390  		{"Infinity || ''", math.Inf(1), "pos-inf-or"},
   391  		{"Infinity || 'abc'", math.Inf(1), "pos-inf-or"},
   392  		// -Infinity &&
   393  		// {"-Infinity && true", true, "neg-inf-and"},
   394  		// {"-Infinity && false", false, "neg-inf-and"},
   395  		// {"-Infinity && null", nil, "neg-inf-and"},
   396  		// {"-Infinity && -10", -10, "neg-inf-and"},
   397  		// {"-Infinity && 0", 0, "neg-inf-and"},
   398  		// {"-Infinity && 10", 10, "neg-inf-and"},
   399  		// {"-Infinity && 3.14", 3.14, "neg-inf-and"},
   400  		// {"-Infinity && 0.0", 0, "neg-inf-and"},
   401  		// {"-Infinity && Infinity", math.Inf(1), "neg-inf-and"},
   402  		// {"-Infinity && -Infinity", math.Inf(-1), "neg-inf-and"},
   403  		// {"-Infinity && NaN", math.NaN(), "neg-inf-and"},
   404  		// {"-Infinity && ''", "", "neg-inf-and"},
   405  		// {"-Infinity && 'abc'", "abc", "neg-inf-and"},
   406  		// -Infinity ||
   407  		// {"-Infinity || true", math.Inf(-1), "neg-inf-or"},
   408  		// {"-Infinity || false", math.Inf(-1), "neg-inf-or"},
   409  		// {"-Infinity || null", math.Inf(-1), "neg-inf-or"},
   410  		// {"-Infinity || -10", math.Inf(-1), "neg-inf-or"},
   411  		// {"-Infinity || 0", math.Inf(-1), "neg-inf-or"},
   412  		// {"-Infinity || 10", math.Inf(-1), "neg-inf-or"},
   413  		// {"-Infinity || 3.14", math.Inf(-1), "neg-inf-or"},
   414  		// {"-Infinity || 0.0", math.Inf(-1), "neg-inf-or"},
   415  		// {"-Infinity || Infinity", math.Inf(-1), "neg-inf-or"},
   416  		// {"-Infinity || -Infinity", math.Inf(-1), "neg-inf-or"},
   417  		// {"-Infinity || NaN", math.Inf(-1), "neg-inf-or"},
   418  		// {"-Infinity || ''", math.Inf(-1), "neg-inf-or"},
   419  		// {"-Infinity || 'abc'", math.Inf(-1), "neg-inf-or"},
   420  		// NaN &&
   421  		{"NaN && true", math.NaN(), "nan-and"},
   422  		{"NaN && false", math.NaN(), "nan-and"},
   423  		{"NaN && null", math.NaN(), "nan-and"},
   424  		{"NaN && -10", math.NaN(), "nan-and"},
   425  		{"NaN && 0", math.NaN(), "nan-and"},
   426  		{"NaN && 10", math.NaN(), "nan-and"},
   427  		{"NaN && 3.14", math.NaN(), "nan-and"},
   428  		{"NaN && 0.0", math.NaN(), "nan-and"},
   429  		{"NaN && Infinity", math.NaN(), "nan-and"},
   430  		// {"NaN && -Infinity", math.NaN(), "nan-and"},
   431  		{"NaN && NaN", math.NaN(), "nan-and"},
   432  		{"NaN && ''", math.NaN(), "nan-and"},
   433  		{"NaN && 'abc'", math.NaN(), "nan-and"},
   434  		// NaN ||
   435  		{"NaN || true", true, "nan-or"},
   436  		{"NaN || false", false, "nan-or"},
   437  		{"NaN || null", nil, "nan-or"},
   438  		{"NaN || -10", -10, "nan-or"},
   439  		{"NaN || 0", 0, "nan-or"},
   440  		{"NaN || 10", 10, "nan-or"},
   441  		{"NaN || 3.14", 3.14, "nan-or"},
   442  		{"NaN || 0.0", 0, "nan-or"},
   443  		{"NaN || Infinity", math.Inf(1), "nan-or"},
   444  		// {"NaN || -Infinity", math.Inf(-1), "nan-or"},
   445  		{"NaN || NaN", math.NaN(), "nan-or"},
   446  		{"NaN || ''", "", "nan-or"},
   447  		{"NaN || 'abc'", "abc", "nan-or"},
   448  		// "" &&
   449  		{"'' && true", "", "empty-str-and"},
   450  		{"'' && false", "", "empty-str-and"},
   451  		{"'' && null", "", "empty-str-and"},
   452  		{"'' && -10", "", "empty-str-and"},
   453  		{"'' && 0", "", "empty-str-and"},
   454  		{"'' && 10", "", "empty-str-and"},
   455  		{"'' && 3.14", "", "empty-str-and"},
   456  		{"'' && 0.0", "", "empty-str-and"},
   457  		{"'' && Infinity", "", "empty-str-and"},
   458  		// {"'' && -Infinity", "", "empty-str-and"},
   459  		{"'' && NaN", "", "empty-str-and"},
   460  		{"'' && ''", "", "empty-str-and"},
   461  		{"'' && 'abc'", "", "empty-str-and"},
   462  		// "" ||
   463  		{"'' || true", true, "empty-str-or"},
   464  		{"'' || false", false, "empty-str-or"},
   465  		{"'' || null", nil, "empty-str-or"},
   466  		{"'' || -10", -10, "empty-str-or"},
   467  		{"'' || 0", 0, "empty-str-or"},
   468  		{"'' || 10", 10, "empty-str-or"},
   469  		{"'' || 3.14", 3.14, "empty-str-or"},
   470  		{"'' || 0.0", 0, "empty-str-or"},
   471  		{"'' || Infinity", math.Inf(1), "empty-str-or"},
   472  		// {"'' || -Infinity", math.Inf(-1), "empty-str-or"},
   473  		{"'' || NaN", math.NaN(), "empty-str-or"},
   474  		{"'' || ''", "", "empty-str-or"},
   475  		{"'' || 'abc'", "abc", "empty-str-or"},
   476  		// "abc" &&
   477  		{"'abc' && true", true, "str-and"},
   478  		{"'abc' && false", false, "str-and"},
   479  		{"'abc' && null", nil, "str-and"},
   480  		{"'abc' && -10", -10, "str-and"},
   481  		{"'abc' && 0", 0, "str-and"},
   482  		{"'abc' && 10", 10, "str-and"},
   483  		{"'abc' && 3.14", 3.14, "str-and"},
   484  		{"'abc' && 0.0", 0, "str-and"},
   485  		{"'abc' && Infinity", math.Inf(1), "str-and"},
   486  		// {"'abc' && -Infinity", math.Inf(-1), "str-and"},
   487  		{"'abc' && NaN", math.NaN(), "str-and"},
   488  		{"'abc' && ''", "", "str-and"},
   489  		{"'abc' && 'abc'", "abc", "str-and"},
   490  		// "abc" ||
   491  		{"'abc' || true", "abc", "str-or"},
   492  		{"'abc' || false", "abc", "str-or"},
   493  		{"'abc' || null", "abc", "str-or"},
   494  		{"'abc' || -10", "abc", "str-or"},
   495  		{"'abc' || 0", "abc", "str-or"},
   496  		{"'abc' || 10", "abc", "str-or"},
   497  		{"'abc' || 3.14", "abc", "str-or"},
   498  		{"'abc' || 0.0", "abc", "str-or"},
   499  		{"'abc' || Infinity", "abc", "str-or"},
   500  		// {"'abc' || -Infinity", "abc", "str-or"},
   501  		{"'abc' || NaN", "abc", "str-or"},
   502  		{"'abc' || ''", "abc", "str-or"},
   503  		{"'abc' || 'abc'", "abc", "str-or"},
   504  		// extra tests
   505  		{"0.0 && true", 0, "float-evaluation-0-alt"},
   506  		{"-1.5 && true", true, "float-evaluation-neg-alt"},
   507  	}
   508  
   509  	env := &EvaluationEnvironment{
   510  		Github: &model.GithubContext{
   511  			Action: "push",
   512  		},
   513  	}
   514  
   515  	for _, tt := range table {
   516  		t.Run(tt.name, func(t *testing.T) {
   517  			output, err := NewInterpeter(env, Config{}).Evaluate(tt.input, DefaultStatusCheckNone)
   518  			assert.Nil(t, err)
   519  
   520  			if expected, ok := tt.expected.(float64); ok && math.IsNaN(expected) {
   521  				assert.True(t, math.IsNaN(output.(float64)))
   522  			} else {
   523  				assert.Equal(t, tt.expected, output)
   524  			}
   525  		})
   526  	}
   527  }
   528  
   529  func TestContexts(t *testing.T) {
   530  	table := []struct {
   531  		input    string
   532  		expected interface{}
   533  		name     string
   534  	}{
   535  		{"github.action", "push", "github-context"},
   536  		{"github.event.commits[0].message", nil, "github-context-noexist-prop"},
   537  		{"fromjson('{\"commits\":[]}').commits[0].message", nil, "github-context-noexist-prop"},
   538  		{"github.event.pull_request.labels.*.name", nil, "github-context-noexist-prop"},
   539  		{"env.TEST", "value", "env-context"},
   540  		{"job.status", "success", "job-context"},
   541  		{"steps.step-id.outputs.name", "value", "steps-context"},
   542  		{"steps.step-id.conclusion", "success", "steps-context-conclusion"},
   543  		{"steps.step-id.conclusion && true", true, "steps-context-conclusion"},
   544  		{"steps.step-id2.conclusion", "skipped", "steps-context-conclusion"},
   545  		{"steps.step-id2.conclusion && true", true, "steps-context-conclusion"},
   546  		{"steps.step-id.outcome", "success", "steps-context-outcome"},
   547  		{"steps.step-id['outcome']", "success", "steps-context-outcome"},
   548  		{"steps.step-id.outcome == 'success'", true, "steps-context-outcome"},
   549  		{"steps.step-id['outcome'] == 'success'", true, "steps-context-outcome"},
   550  		{"steps.step-id.outcome && true", true, "steps-context-outcome"},
   551  		{"steps['step-id']['outcome'] && true", true, "steps-context-outcome"},
   552  		{"steps.step-id2.outcome", "failure", "steps-context-outcome"},
   553  		{"steps.step-id2.outcome && true", true, "steps-context-outcome"},
   554  		// Disabled, since the interpreter is still too broken
   555  		// {"contains(steps.*.outcome, 'success')", true, "steps-context-array-outcome"},
   556  		// {"contains(steps.*.outcome, 'failure')", true, "steps-context-array-outcome"},
   557  		// {"contains(steps.*.outputs.name, 'value')", true, "steps-context-array-outputs"},
   558  		{"runner.os", "Linux", "runner-context"},
   559  		{"secrets.name", "value", "secrets-context"},
   560  		{"vars.name", "value", "vars-context"},
   561  		{"strategy.fail-fast", true, "strategy-context"},
   562  		{"matrix.os", "Linux", "matrix-context"},
   563  		{"needs.job-id.outputs.output-name", "value", "needs-context"},
   564  		{"needs.job-id.result", "success", "needs-context"},
   565  		{"inputs.name", "value", "inputs-context"},
   566  	}
   567  
   568  	env := &EvaluationEnvironment{
   569  		Github: &model.GithubContext{
   570  			Action: "push",
   571  		},
   572  		Env: map[string]string{
   573  			"TEST": "value",
   574  		},
   575  		Job: &model.JobContext{
   576  			Status: "success",
   577  		},
   578  		Steps: map[string]*model.StepResult{
   579  			"step-id": {
   580  				Outputs: map[string]string{
   581  					"name": "value",
   582  				},
   583  			},
   584  			"step-id2": {
   585  				Outcome:    model.StepStatusFailure,
   586  				Conclusion: model.StepStatusSkipped,
   587  			},
   588  		},
   589  		Runner: map[string]interface{}{
   590  			"os":         "Linux",
   591  			"temp":       "/tmp",
   592  			"tool_cache": "/opt/hostedtoolcache",
   593  		},
   594  		Secrets: map[string]string{
   595  			"name": "value",
   596  		},
   597  		Vars: map[string]string{
   598  			"name": "value",
   599  		},
   600  		Strategy: map[string]interface{}{
   601  			"fail-fast": true,
   602  		},
   603  		Matrix: map[string]interface{}{
   604  			"os": "Linux",
   605  		},
   606  		Needs: map[string]Needs{
   607  			"job-id": {
   608  				Outputs: map[string]string{
   609  					"output-name": "value",
   610  				},
   611  				Result: "success",
   612  			},
   613  		},
   614  		Inputs: map[string]interface{}{
   615  			"name": "value",
   616  		},
   617  	}
   618  
   619  	for _, tt := range table {
   620  		t.Run(tt.name, func(t *testing.T) {
   621  			output, err := NewInterpeter(env, Config{}).Evaluate(tt.input, DefaultStatusCheckNone)
   622  			assert.Nil(t, err)
   623  
   624  			assert.Equal(t, tt.expected, output)
   625  		})
   626  	}
   627  }