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 }