github.com/nektos/act@v0.2.83/pkg/exprparser/functions_test.go (about)

     1  package exprparser
     2  
     3  import (
     4  	"path/filepath"
     5  	"testing"
     6  
     7  	"github.com/nektos/act/pkg/model"
     8  	"github.com/stretchr/testify/assert"
     9  )
    10  
    11  func TestFunctionContains(t *testing.T) {
    12  	table := []struct {
    13  		input    string
    14  		expected interface{}
    15  		name     string
    16  	}{
    17  		{"contains('search', 'item') }}", false, "contains-str-str"},
    18  		{`cOnTaInS('Hello', 'll') }}`, true, "contains-str-casing"},
    19  		{`contains('HELLO', 'll') }}`, true, "contains-str-casing"},
    20  		{`contains('3.141592', 3.14) }}`, true, "contains-str-number"},
    21  		{`contains(3.141592, '3.14') }}`, true, "contains-number-str"},
    22  		{`contains(3.141592, 3.14) }}`, true, "contains-number-number"},
    23  		{`contains(true, 'u') }}`, true, "contains-bool-str"},
    24  		{`contains(null, '') }}`, true, "contains-null-str"},
    25  		{`contains(fromJSON('["first","second"]'), 'first') }}`, true, "contains-item"},
    26  		{`contains(fromJSON('[null,"second"]'), '') }}`, true, "contains-item-null-empty-str"},
    27  		{`contains(fromJSON('["","second"]'), null) }}`, true, "contains-item-empty-str-null"},
    28  		{`contains(fromJSON('[true,"second"]'), 'true') }}`, false, "contains-item-bool-arr"},
    29  		{`contains(fromJSON('["true","second"]'), true) }}`, false, "contains-item-str-bool"},
    30  		{`contains(fromJSON('[3.14,"second"]'), '3.14') }}`, true, "contains-item-number-str"},
    31  		{`contains(fromJSON('[3.14,"second"]'), 3.14) }}`, true, "contains-item-number-number"},
    32  		{`contains(fromJSON('["","second"]'), fromJSON('[]')) }}`, false, "contains-item-str-arr"},
    33  		{`contains(fromJSON('["","second"]'), fromJSON('{}')) }}`, false, "contains-item-str-obj"},
    34  		{`contains(fromJSON('[{ "first": { "result": "success" }},{ "second": { "result": "success" }}]').first.result, 'success') }}`, true, "multiple-contains-item"},
    35  		{`contains(fromJSON('[{ "result": "success" },{ "result": "failure" }]').*.result, 'failure') }}`, true, "multiple-contains-dereferenced-failure-item"},
    36  		{`contains(fromJSON('[{ "result": "failure" },{ "result": "success" }]').*.result, 'success') }}`, true, "multiple-contains-dereferenced-success-item"},
    37  		{`contains(fromJSON('[{ "result": "failure" },{ "result": "success" }]').*.result, 'notthere') }}`, false, "multiple-contains-dereferenced-missing-item"},
    38  		{`contains(fromJSON('[{ "result": "failure", "outputs": { "key": "val1" } },{ "result": "success", "outputs": { "key": "val2" } }]').*.outputs.key, 'val1') }}`, true, "multiple-contains-dereferenced-output-item"},
    39  		{`contains(fromJSON('[{ "result": "failure", "outputs": { "key": "val1" } },{ "result": "success", "outputs": { "key": "val2" } }]').*.outputs.key, 'val2') }}`, true, "multiple-contains-dereferenced-output-item-2"},
    40  		{`contains(fromJSON('[{ "result": "failure", "outputs": { "key": "val1" } },{ "result": "success", "outputs": { "key": "val2" } }]').*.outputs.key, 'missing') }}`, false, "multiple-contains-dereferenced-output-misssing-item"},
    41  	}
    42  
    43  	env := &EvaluationEnvironment{}
    44  
    45  	for _, tt := range table {
    46  		t.Run(tt.name, func(t *testing.T) {
    47  			output, err := NewInterpeter(env, Config{}).Evaluate(tt.input, DefaultStatusCheckNone)
    48  			assert.Nil(t, err)
    49  
    50  			assert.Equal(t, tt.expected, output)
    51  		})
    52  	}
    53  }
    54  
    55  func TestFunctionStartsWith(t *testing.T) {
    56  	table := []struct {
    57  		input    string
    58  		expected interface{}
    59  		name     string
    60  	}{
    61  		{"startsWith('search', 'se') }}", true, "startswith-string"},
    62  		{"startsWith('search', 'sa') }}", false, "startswith-string"},
    63  		{"startsWith('123search', '123s') }}", true, "startswith-string"},
    64  		{"startsWith(123, 's') }}", false, "startswith-string"},
    65  		{"startsWith(123, '12') }}", true, "startswith-string"},
    66  		{"startsWith('123', 12) }}", true, "startswith-string"},
    67  		{"startsWith(null, '42') }}", false, "startswith-string"},
    68  		{"startsWith('null', null) }}", true, "startswith-string"},
    69  		{"startsWith('null', '') }}", true, "startswith-string"},
    70  	}
    71  
    72  	env := &EvaluationEnvironment{}
    73  
    74  	for _, tt := range table {
    75  		t.Run(tt.name, func(t *testing.T) {
    76  			output, err := NewInterpeter(env, Config{}).Evaluate(tt.input, DefaultStatusCheckNone)
    77  			assert.Nil(t, err)
    78  
    79  			assert.Equal(t, tt.expected, output)
    80  		})
    81  	}
    82  }
    83  
    84  func TestFunctionEndsWith(t *testing.T) {
    85  	table := []struct {
    86  		input    string
    87  		expected interface{}
    88  		name     string
    89  	}{
    90  		{"endsWith('search', 'ch') }}", true, "endsWith-string"},
    91  		{"endsWith('search', 'sa') }}", false, "endsWith-string"},
    92  		{"endsWith('search123s', '123s') }}", true, "endsWith-string"},
    93  		{"endsWith(123, 's') }}", false, "endsWith-string"},
    94  		{"endsWith(123, '23') }}", true, "endsWith-string"},
    95  		{"endsWith('123', 23) }}", true, "endsWith-string"},
    96  		{"endsWith(null, '42') }}", false, "endsWith-string"},
    97  		{"endsWith('null', null) }}", true, "endsWith-string"},
    98  		{"endsWith('null', '') }}", true, "endsWith-string"},
    99  	}
   100  
   101  	env := &EvaluationEnvironment{}
   102  
   103  	for _, tt := range table {
   104  		t.Run(tt.name, func(t *testing.T) {
   105  			output, err := NewInterpeter(env, Config{}).Evaluate(tt.input, DefaultStatusCheckNone)
   106  			assert.Nil(t, err)
   107  
   108  			assert.Equal(t, tt.expected, output)
   109  		})
   110  	}
   111  }
   112  
   113  func TestFunctionJoin(t *testing.T) {
   114  	table := []struct {
   115  		input    string
   116  		expected interface{}
   117  		name     string
   118  	}{
   119  		{"join(fromJSON('[\"a\", \"b\"]'), ',')", "a,b", "join-arr"},
   120  		{"join('string', ',')", "string", "join-str"},
   121  		{"join(1, ',')", "1", "join-number"},
   122  		{"join(null, ',')", "", "join-number"},
   123  		{"join(fromJSON('[\"a\", \"b\", null]'), null)", "ab", "join-number"},
   124  		{"join(fromJSON('[\"a\", \"b\"]'))", "a,b", "join-number"},
   125  		{"join(fromJSON('[\"a\", \"b\", null]'), 1)", "a1b1", "join-number"},
   126  	}
   127  
   128  	env := &EvaluationEnvironment{}
   129  
   130  	for _, tt := range table {
   131  		t.Run(tt.name, func(t *testing.T) {
   132  			output, err := NewInterpeter(env, Config{}).Evaluate(tt.input, DefaultStatusCheckNone)
   133  			assert.Nil(t, err)
   134  
   135  			assert.Equal(t, tt.expected, output)
   136  		})
   137  	}
   138  }
   139  
   140  func TestFunctionToJSON(t *testing.T) {
   141  	table := []struct {
   142  		input    string
   143  		expected interface{}
   144  		name     string
   145  	}{
   146  		{"toJSON(env) }}", "{\n  \"key\": \"value\"\n}", "toJSON"},
   147  		{"toJSON(null)", "null", "toJSON-null"},
   148  	}
   149  
   150  	env := &EvaluationEnvironment{
   151  		Env: map[string]string{
   152  			"key": "value",
   153  		},
   154  	}
   155  
   156  	for _, tt := range table {
   157  		t.Run(tt.name, func(t *testing.T) {
   158  			output, err := NewInterpeter(env, Config{}).Evaluate(tt.input, DefaultStatusCheckNone)
   159  			assert.Nil(t, err)
   160  
   161  			assert.Equal(t, tt.expected, output)
   162  		})
   163  	}
   164  }
   165  
   166  func TestFunctionFromJSON(t *testing.T) {
   167  	table := []struct {
   168  		input    string
   169  		expected interface{}
   170  		name     string
   171  	}{
   172  		{"fromJSON('{\"foo\":\"bar\"}') }}", map[string]interface{}{
   173  			"foo": "bar",
   174  		}, "fromJSON"},
   175  	}
   176  
   177  	env := &EvaluationEnvironment{}
   178  
   179  	for _, tt := range table {
   180  		t.Run(tt.name, func(t *testing.T) {
   181  			output, err := NewInterpeter(env, Config{}).Evaluate(tt.input, DefaultStatusCheckNone)
   182  			assert.Nil(t, err)
   183  
   184  			assert.Equal(t, tt.expected, output)
   185  		})
   186  	}
   187  }
   188  
   189  func TestFunctionHashFiles(t *testing.T) {
   190  	table := []struct {
   191  		input    string
   192  		expected interface{}
   193  		name     string
   194  	}{
   195  		{"hashFiles('**/non-extant-files') }}", "", "hash-non-existing-file"},
   196  		{"hashFiles('**/non-extant-files', '**/more-non-extant-files') }}", "", "hash-multiple-non-existing-files"},
   197  		{"hashFiles('./for-hashing-1.txt') }}", "66a045b452102c59d840ec097d59d9467e13a3f34f6494e539ffd32c1bb35f18", "hash-single-file"},
   198  		{"hashFiles('./for-hashing-*.txt') }}", "8e5935e7e13368cd9688fe8f48a0955293676a021562582c7e848dafe13fb046", "hash-multiple-files"},
   199  		{"hashFiles('./for-hashing-*.txt', '!./for-hashing-2.txt') }}", "66a045b452102c59d840ec097d59d9467e13a3f34f6494e539ffd32c1bb35f18", "hash-negative-pattern"},
   200  		{"hashFiles('./for-hashing-**') }}", "c418ba693753c84115ced0da77f876cddc662b9054f4b129b90f822597ee2f94", "hash-multiple-files-and-directories"},
   201  		{"hashFiles('./for-hashing-3/**') }}", "6f5696b546a7a9d6d42a449dc9a56bef244aaa826601ef27466168846139d2c2", "hash-nested-directories"},
   202  		{"hashFiles('./for-hashing-3/**/nested-data.txt') }}", "8ecadfb49f7f978d0a9f3a957e9c8da6cc9ab871f5203b5d9f9d1dc87d8af18c", "hash-nested-directories-2"},
   203  	}
   204  
   205  	env := &EvaluationEnvironment{}
   206  
   207  	for _, tt := range table {
   208  		t.Run(tt.name, func(t *testing.T) {
   209  			workdir, err := filepath.Abs("testdata")
   210  			assert.Nil(t, err)
   211  			output, err := NewInterpeter(env, Config{WorkingDir: workdir}).Evaluate(tt.input, DefaultStatusCheckNone)
   212  			assert.Nil(t, err)
   213  
   214  			assert.Equal(t, tt.expected, output)
   215  		})
   216  	}
   217  }
   218  
   219  func TestFunctionFormat(t *testing.T) {
   220  	table := []struct {
   221  		input    string
   222  		expected interface{}
   223  		error    interface{}
   224  		name     string
   225  	}{
   226  		{"format('text')", "text", nil, "format-plain-string"},
   227  		{"format('Hello {0} {1} {2}!', 'Mona', 'the', 'Octocat')", "Hello Mona the Octocat!", nil, "format-with-placeholders"},
   228  		{"format('{{Hello {0} {1} {2}!}}', 'Mona', 'the', 'Octocat')", "{Hello Mona the Octocat!}", nil, "format-with-escaped-braces"},
   229  		{"format('{{0}}', 'test')", "{0}", nil, "format-with-escaped-braces"},
   230  		{"format('{{{0}}}', 'test')", "{test}", nil, "format-with-escaped-braces-and-value"},
   231  		{"format('}}')", "}", nil, "format-output-closing-brace"},
   232  		{`format('Hello "{0}" {1} {2} {3} {4}', null, true, -3.14, NaN, Infinity)`, `Hello "" true -3.14 NaN Infinity`, nil, "format-with-primitives"},
   233  		{`format('Hello "{0}" {1} {2}', fromJSON('[0, true, "abc"]'), fromJSON('[{"a":1}]'), fromJSON('{"a":{"b":1}}'))`, `Hello "Array" Array Object`, nil, "format-with-complex-types"},
   234  		{"format(true)", "true", nil, "format-with-primitive-args"},
   235  		{"format('echo Hello {0} ${{Test}}', github.undefined_property)", "echo Hello  ${Test}", nil, "format-with-undefined-value"},
   236  		{"format('{0}}', '{1}', 'World')", nil, "Closing bracket without opening one. The following format string is invalid: '{0}}'", "format-invalid-format-string"},
   237  		{"format('{0', '{1}', 'World')", nil, "Unclosed brackets. The following format string is invalid: '{0'", "format-invalid-format-string"},
   238  		{"format('{2}', '{1}', 'World')", "", "The following format string references more arguments than were supplied: '{2}'", "format-invalid-replacement-reference"},
   239  		{"format('{2147483648}')", "", "The following format string is invalid: '{2147483648}'", "format-invalid-replacement-reference"},
   240  		{"format('{0} {1} {2} {3}', 1.0, 1.1, 1234567890.0, 12345678901234567890.0)", "1 1.1 1234567890 1.23456789012346E+19", nil, "format-floats"},
   241  	}
   242  
   243  	env := &EvaluationEnvironment{
   244  		Github: &model.GithubContext{},
   245  	}
   246  
   247  	for _, tt := range table {
   248  		t.Run(tt.name, func(t *testing.T) {
   249  			output, err := NewInterpeter(env, Config{}).Evaluate(tt.input, DefaultStatusCheckNone)
   250  			if tt.error != nil {
   251  				assert.Equal(t, tt.error, err.Error())
   252  			} else {
   253  				assert.Nil(t, err)
   254  				assert.Equal(t, tt.expected, output)
   255  			}
   256  		})
   257  	}
   258  }
   259  
   260  func TestMapContains(t *testing.T) {
   261  	env := &EvaluationEnvironment{
   262  		Needs: map[string]Needs{
   263  			"first-job": {
   264  				Outputs: map[string]string{},
   265  				Result:  "success",
   266  			},
   267  			"second-job": {
   268  				Outputs: map[string]string{},
   269  				Result:  "failure",
   270  			},
   271  		},
   272  	}
   273  
   274  	output, err := NewInterpeter(env, Config{}).Evaluate("contains(needs.*.result, 'failure')", DefaultStatusCheckNone)
   275  	assert.Nil(t, err)
   276  
   277  	assert.Equal(t, true, output)
   278  }