diff options
Diffstat (limited to 'pkg/runner/action_test.go')
-rw-r--r-- | pkg/runner/action_test.go | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/pkg/runner/action_test.go b/pkg/runner/action_test.go new file mode 100644 index 0000000..36ee14f --- /dev/null +++ b/pkg/runner/action_test.go @@ -0,0 +1,249 @@ +package runner + +import ( + "context" + "io" + "io/fs" + "strings" + "testing" + + "github.com/nektos/act/pkg/model" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +type closerMock struct { + mock.Mock +} + +func (m *closerMock) Close() error { + m.Called() + return nil +} + +func TestActionReader(t *testing.T) { + yaml := strings.ReplaceAll(` +name: 'name' +runs: + using: 'node16' + main: 'main.js' +`, "\t", " ") + + table := []struct { + name string + step *model.Step + filename string + fileContent string + expected *model.Action + }{ + { + name: "readActionYml", + step: &model.Step{}, + filename: "action.yml", + fileContent: yaml, + expected: &model.Action{ + Name: "name", + Runs: model.ActionRuns{ + Using: "node16", + Main: "main.js", + PreIf: "always()", + PostIf: "always()", + }, + }, + }, + { + name: "readActionYaml", + step: &model.Step{}, + filename: "action.yaml", + fileContent: yaml, + expected: &model.Action{ + Name: "name", + Runs: model.ActionRuns{ + Using: "node16", + Main: "main.js", + PreIf: "always()", + PostIf: "always()", + }, + }, + }, + { + name: "readDockerfile", + step: &model.Step{}, + filename: "Dockerfile", + fileContent: "FROM ubuntu:20.04", + expected: &model.Action{ + Name: "(Synthetic)", + Runs: model.ActionRuns{ + Using: "docker", + Image: "Dockerfile", + }, + }, + }, + { + name: "readWithArgs", + step: &model.Step{ + With: map[string]string{ + "args": "cmd", + }, + }, + expected: &model.Action{ + Name: "(Synthetic)", + Inputs: map[string]model.Input{ + "cwd": { + Description: "(Actual working directory)", + Required: false, + Default: "actionDir/actionPath", + }, + "command": { + Description: "(Actual program)", + Required: false, + Default: "cmd", + }, + }, + Runs: model.ActionRuns{ + Using: "node12", + Main: "trampoline.js", + }, + }, + }, + } + + for _, tt := range table { + t.Run(tt.name, func(t *testing.T) { + closerMock := &closerMock{} + + readFile := func(filename string) (io.Reader, io.Closer, error) { + if tt.filename != filename { + return nil, nil, fs.ErrNotExist + } + + return strings.NewReader(tt.fileContent), closerMock, nil + } + + writeFile := func(filename string, data []byte, perm fs.FileMode) error { + assert.Equal(t, "actionDir/actionPath/trampoline.js", filename) + assert.Equal(t, fs.FileMode(0400), perm) + return nil + } + + if tt.filename != "" { + closerMock.On("Close") + } + + action, err := readActionImpl(context.Background(), tt.step, "actionDir", "actionPath", readFile, writeFile) + + assert.Nil(t, err) + assert.Equal(t, tt.expected, action) + + closerMock.AssertExpectations(t) + }) + } +} + +func TestActionRunner(t *testing.T) { + table := []struct { + name string + step actionStep + expectedEnv map[string]string + }{ + { + name: "with-input", + step: &stepActionRemote{ + Step: &model.Step{ + Uses: "org/repo/path@ref", + }, + RunContext: &RunContext{ + Config: &Config{}, + Run: &model.Run{ + JobID: "job", + Workflow: &model.Workflow{ + Jobs: map[string]*model.Job{ + "job": { + Name: "job", + }, + }, + }, + }, + }, + action: &model.Action{ + Inputs: map[string]model.Input{ + "key": { + Default: "default value", + }, + }, + Runs: model.ActionRuns{ + Using: "node16", + }, + }, + env: map[string]string{}, + }, + expectedEnv: map[string]string{"INPUT_KEY": "default value"}, + }, + { + name: "restore-saved-state", + step: &stepActionRemote{ + Step: &model.Step{ + ID: "step", + Uses: "org/repo/path@ref", + }, + RunContext: &RunContext{ + ActionPath: "path", + Config: &Config{}, + Run: &model.Run{ + JobID: "job", + Workflow: &model.Workflow{ + Jobs: map[string]*model.Job{ + "job": { + Name: "job", + }, + }, + }, + }, + CurrentStep: "post-step", + StepResults: map[string]*model.StepResult{ + "step": {}, + }, + IntraActionState: map[string]map[string]string{ + "step": { + "name": "state value", + }, + }, + }, + action: &model.Action{ + Runs: model.ActionRuns{ + Using: "node16", + }, + }, + env: map[string]string{}, + }, + expectedEnv: map[string]string{"STATE_name": "state value"}, + }, + } + + for _, tt := range table { + t.Run(tt.name, func(t *testing.T) { + ctx := context.Background() + + cm := &containerMock{} + cm.On("CopyDir", "/var/run/act/actions/dir/", "dir/", false).Return(func(ctx context.Context) error { return nil }) + + envMatcher := mock.MatchedBy(func(env map[string]string) bool { + for k, v := range tt.expectedEnv { + if env[k] != v { + return false + } + } + return true + }) + + cm.On("Exec", []string{"node", "/var/run/act/actions/dir/path"}, envMatcher, "", "").Return(func(ctx context.Context) error { return nil }) + + tt.step.getRunContext().JobContainer = cm + + err := runActionImpl(tt.step, "dir", newRemoteAction("org/repo/path@ref"))(ctx) + + assert.Nil(t, err) + cm.AssertExpectations(t) + }) + } +} |