github.1485827954.workers.dev/nektos/act@v0.2.63/pkg/runner/action_test.go (about)

     1  package runner
     2  
     3  import (
     4  	"context"
     5  	"io"
     6  	"io/fs"
     7  	"strings"
     8  	"testing"
     9  
    10  	"github.com/nektos/act/pkg/model"
    11  	"github.com/stretchr/testify/assert"
    12  	"github.com/stretchr/testify/mock"
    13  )
    14  
    15  type closerMock struct {
    16  	mock.Mock
    17  }
    18  
    19  func (m *closerMock) Close() error {
    20  	m.Called()
    21  	return nil
    22  }
    23  
    24  func TestActionReader(t *testing.T) {
    25  	yaml := strings.ReplaceAll(`
    26  name: 'name'
    27  runs:
    28  	using: 'node16'
    29  	main: 'main.js'
    30  `, "\t", "  ")
    31  
    32  	table := []struct {
    33  		name        string
    34  		step        *model.Step
    35  		filename    string
    36  		fileContent string
    37  		expected    *model.Action
    38  	}{
    39  		{
    40  			name:        "readActionYml",
    41  			step:        &model.Step{},
    42  			filename:    "action.yml",
    43  			fileContent: yaml,
    44  			expected: &model.Action{
    45  				Name: "name",
    46  				Runs: model.ActionRuns{
    47  					Using:  "node16",
    48  					Main:   "main.js",
    49  					PreIf:  "always()",
    50  					PostIf: "always()",
    51  				},
    52  			},
    53  		},
    54  		{
    55  			name:        "readActionYaml",
    56  			step:        &model.Step{},
    57  			filename:    "action.yaml",
    58  			fileContent: yaml,
    59  			expected: &model.Action{
    60  				Name: "name",
    61  				Runs: model.ActionRuns{
    62  					Using:  "node16",
    63  					Main:   "main.js",
    64  					PreIf:  "always()",
    65  					PostIf: "always()",
    66  				},
    67  			},
    68  		},
    69  		{
    70  			name:        "readDockerfile",
    71  			step:        &model.Step{},
    72  			filename:    "Dockerfile",
    73  			fileContent: "FROM ubuntu:20.04",
    74  			expected: &model.Action{
    75  				Name: "(Synthetic)",
    76  				Runs: model.ActionRuns{
    77  					Using: "docker",
    78  					Image: "Dockerfile",
    79  				},
    80  			},
    81  		},
    82  		{
    83  			name: "readWithArgs",
    84  			step: &model.Step{
    85  				With: map[string]string{
    86  					"args": "cmd",
    87  				},
    88  			},
    89  			expected: &model.Action{
    90  				Name: "(Synthetic)",
    91  				Inputs: map[string]model.Input{
    92  					"cwd": {
    93  						Description: "(Actual working directory)",
    94  						Required:    false,
    95  						Default:     "actionDir/actionPath",
    96  					},
    97  					"command": {
    98  						Description: "(Actual program)",
    99  						Required:    false,
   100  						Default:     "cmd",
   101  					},
   102  				},
   103  				Runs: model.ActionRuns{
   104  					Using: "node12",
   105  					Main:  "trampoline.js",
   106  				},
   107  			},
   108  		},
   109  	}
   110  
   111  	for _, tt := range table {
   112  		t.Run(tt.name, func(t *testing.T) {
   113  			closerMock := &closerMock{}
   114  
   115  			readFile := func(filename string) (io.Reader, io.Closer, error) {
   116  				if tt.filename != filename {
   117  					return nil, nil, fs.ErrNotExist
   118  				}
   119  
   120  				return strings.NewReader(tt.fileContent), closerMock, nil
   121  			}
   122  
   123  			writeFile := func(filename string, data []byte, perm fs.FileMode) error {
   124  				assert.Equal(t, "actionDir/actionPath/trampoline.js", filename)
   125  				assert.Equal(t, fs.FileMode(0400), perm)
   126  				return nil
   127  			}
   128  
   129  			if tt.filename != "" {
   130  				closerMock.On("Close")
   131  			}
   132  
   133  			action, err := readActionImpl(context.Background(), tt.step, "actionDir", "actionPath", readFile, writeFile)
   134  
   135  			assert.Nil(t, err)
   136  			assert.Equal(t, tt.expected, action)
   137  
   138  			closerMock.AssertExpectations(t)
   139  		})
   140  	}
   141  }
   142  
   143  func TestActionRunner(t *testing.T) {
   144  	table := []struct {
   145  		name        string
   146  		step        actionStep
   147  		expectedEnv map[string]string
   148  	}{
   149  		{
   150  			name: "with-input",
   151  			step: &stepActionRemote{
   152  				Step: &model.Step{
   153  					Uses: "org/repo/path@ref",
   154  				},
   155  				RunContext: &RunContext{
   156  					Config: &Config{},
   157  					Run: &model.Run{
   158  						JobID: "job",
   159  						Workflow: &model.Workflow{
   160  							Jobs: map[string]*model.Job{
   161  								"job": {
   162  									Name: "job",
   163  								},
   164  							},
   165  						},
   166  					},
   167  				},
   168  				action: &model.Action{
   169  					Inputs: map[string]model.Input{
   170  						"key": {
   171  							Default: "default value",
   172  						},
   173  					},
   174  					Runs: model.ActionRuns{
   175  						Using: "node16",
   176  					},
   177  				},
   178  				env: map[string]string{},
   179  			},
   180  			expectedEnv: map[string]string{"INPUT_KEY": "default value"},
   181  		},
   182  		{
   183  			name: "restore-saved-state",
   184  			step: &stepActionRemote{
   185  				Step: &model.Step{
   186  					ID:   "step",
   187  					Uses: "org/repo/path@ref",
   188  				},
   189  				RunContext: &RunContext{
   190  					ActionPath: "path",
   191  					Config:     &Config{},
   192  					Run: &model.Run{
   193  						JobID: "job",
   194  						Workflow: &model.Workflow{
   195  							Jobs: map[string]*model.Job{
   196  								"job": {
   197  									Name: "job",
   198  								},
   199  							},
   200  						},
   201  					},
   202  					CurrentStep: "post-step",
   203  					StepResults: map[string]*model.StepResult{
   204  						"step": {},
   205  					},
   206  					IntraActionState: map[string]map[string]string{
   207  						"step": {
   208  							"name": "state value",
   209  						},
   210  					},
   211  				},
   212  				action: &model.Action{
   213  					Runs: model.ActionRuns{
   214  						Using: "node16",
   215  					},
   216  				},
   217  				env: map[string]string{},
   218  			},
   219  			expectedEnv: map[string]string{"STATE_name": "state value"},
   220  		},
   221  	}
   222  
   223  	for _, tt := range table {
   224  		t.Run(tt.name, func(t *testing.T) {
   225  			ctx := context.Background()
   226  
   227  			cm := &containerMock{}
   228  			cm.On("CopyDir", "/var/run/act/actions/dir/", "dir/", false).Return(func(ctx context.Context) error { return nil })
   229  
   230  			envMatcher := mock.MatchedBy(func(env map[string]string) bool {
   231  				for k, v := range tt.expectedEnv {
   232  					if env[k] != v {
   233  						return false
   234  					}
   235  				}
   236  				return true
   237  			})
   238  
   239  			cm.On("Exec", []string{"node", "/var/run/act/actions/dir/path"}, envMatcher, "", "").Return(func(ctx context.Context) error { return nil })
   240  
   241  			tt.step.getRunContext().JobContainer = cm
   242  
   243  			err := runActionImpl(tt.step, "dir", newRemoteAction("org/repo/path@ref"))(ctx)
   244  
   245  			assert.Nil(t, err)
   246  			cm.AssertExpectations(t)
   247  		})
   248  	}
   249  }