github.com/nektos/act@v0.2.63/pkg/runner/job_executor_test.go (about) 1 package runner 2 3 import ( 4 "context" 5 "fmt" 6 "io" 7 "testing" 8 9 "github.com/nektos/act/pkg/common" 10 "github.com/nektos/act/pkg/container" 11 "github.com/nektos/act/pkg/model" 12 "github.com/stretchr/testify/assert" 13 "github.com/stretchr/testify/mock" 14 ) 15 16 func TestJobExecutor(t *testing.T) { 17 tables := []TestJobFileInfo{ 18 {workdir, "uses-and-run-in-one-step", "push", "Invalid run/uses syntax for job:test step:Test", platforms, secrets}, 19 {workdir, "uses-github-empty", "push", "Expected format {org}/{repo}[/path]@ref", platforms, secrets}, 20 {workdir, "uses-github-noref", "push", "Expected format {org}/{repo}[/path]@ref", platforms, secrets}, 21 {workdir, "uses-github-root", "push", "", platforms, secrets}, 22 {workdir, "uses-github-path", "push", "", platforms, secrets}, 23 {workdir, "uses-docker-url", "push", "", platforms, secrets}, 24 {workdir, "uses-github-full-sha", "push", "", platforms, secrets}, 25 {workdir, "uses-github-short-sha", "push", "Unable to resolve action `actions/hello-world-docker-action@b136eb8`, the provided ref `b136eb8` is the shortened version of a commit SHA, which is not supported. Please use the full commit SHA `b136eb8894c5cb1dd5807da824be97ccdf9b5423` instead", platforms, secrets}, 26 {workdir, "job-nil-step", "push", "invalid Step 0: missing run or uses key", platforms, secrets}, 27 } 28 // These tests are sufficient to only check syntax. 29 ctx := common.WithDryrun(context.Background(), true) 30 for _, table := range tables { 31 t.Run(table.workflowPath, func(t *testing.T) { 32 table.runTest(ctx, t, &Config{}) 33 }) 34 } 35 } 36 37 type jobInfoMock struct { 38 mock.Mock 39 } 40 41 func (jim *jobInfoMock) matrix() map[string]interface{} { 42 args := jim.Called() 43 return args.Get(0).(map[string]interface{}) 44 } 45 46 func (jim *jobInfoMock) steps() []*model.Step { 47 args := jim.Called() 48 49 return args.Get(0).([]*model.Step) 50 } 51 52 func (jim *jobInfoMock) startContainer() common.Executor { 53 args := jim.Called() 54 55 return args.Get(0).(func(context.Context) error) 56 } 57 58 func (jim *jobInfoMock) stopContainer() common.Executor { 59 args := jim.Called() 60 61 return args.Get(0).(func(context.Context) error) 62 } 63 64 func (jim *jobInfoMock) closeContainer() common.Executor { 65 args := jim.Called() 66 67 return args.Get(0).(func(context.Context) error) 68 } 69 70 func (jim *jobInfoMock) interpolateOutputs() common.Executor { 71 args := jim.Called() 72 73 return args.Get(0).(func(context.Context) error) 74 } 75 76 func (jim *jobInfoMock) result(result string) { 77 jim.Called(result) 78 } 79 80 type jobContainerMock struct { 81 container.Container 82 container.LinuxContainerEnvironmentExtensions 83 } 84 85 func (jcm *jobContainerMock) ReplaceLogWriter(_, _ io.Writer) (io.Writer, io.Writer) { 86 return nil, nil 87 } 88 89 type stepFactoryMock struct { 90 mock.Mock 91 } 92 93 func (sfm *stepFactoryMock) newStep(model *model.Step, rc *RunContext) (step, error) { 94 args := sfm.Called(model, rc) 95 return args.Get(0).(step), args.Error(1) 96 } 97 98 func TestNewJobExecutor(t *testing.T) { 99 table := []struct { 100 name string 101 steps []*model.Step 102 preSteps []bool 103 postSteps []bool 104 executedSteps []string 105 result string 106 hasError bool 107 }{ 108 { 109 name: "zeroSteps", 110 steps: []*model.Step{}, 111 preSteps: []bool{}, 112 postSteps: []bool{}, 113 executedSteps: []string{}, 114 result: "success", 115 hasError: false, 116 }, 117 { 118 name: "stepWithoutPrePost", 119 steps: []*model.Step{{ 120 ID: "1", 121 }}, 122 preSteps: []bool{false}, 123 postSteps: []bool{false}, 124 executedSteps: []string{ 125 "startContainer", 126 "step1", 127 "stopContainer", 128 "interpolateOutputs", 129 "closeContainer", 130 }, 131 result: "success", 132 hasError: false, 133 }, 134 { 135 name: "stepWithFailure", 136 steps: []*model.Step{{ 137 ID: "1", 138 }}, 139 preSteps: []bool{false}, 140 postSteps: []bool{false}, 141 executedSteps: []string{ 142 "startContainer", 143 "step1", 144 "interpolateOutputs", 145 "closeContainer", 146 }, 147 result: "failure", 148 hasError: true, 149 }, 150 { 151 name: "stepWithPre", 152 steps: []*model.Step{{ 153 ID: "1", 154 }}, 155 preSteps: []bool{true}, 156 postSteps: []bool{false}, 157 executedSteps: []string{ 158 "startContainer", 159 "pre1", 160 "step1", 161 "stopContainer", 162 "interpolateOutputs", 163 "closeContainer", 164 }, 165 result: "success", 166 hasError: false, 167 }, 168 { 169 name: "stepWithPost", 170 steps: []*model.Step{{ 171 ID: "1", 172 }}, 173 preSteps: []bool{false}, 174 postSteps: []bool{true}, 175 executedSteps: []string{ 176 "startContainer", 177 "step1", 178 "post1", 179 "stopContainer", 180 "interpolateOutputs", 181 "closeContainer", 182 }, 183 result: "success", 184 hasError: false, 185 }, 186 { 187 name: "stepWithPreAndPost", 188 steps: []*model.Step{{ 189 ID: "1", 190 }}, 191 preSteps: []bool{true}, 192 postSteps: []bool{true}, 193 executedSteps: []string{ 194 "startContainer", 195 "pre1", 196 "step1", 197 "post1", 198 "stopContainer", 199 "interpolateOutputs", 200 "closeContainer", 201 }, 202 result: "success", 203 hasError: false, 204 }, 205 { 206 name: "stepsWithPreAndPost", 207 steps: []*model.Step{{ 208 ID: "1", 209 }, { 210 ID: "2", 211 }, { 212 ID: "3", 213 }}, 214 preSteps: []bool{true, false, true}, 215 postSteps: []bool{false, true, true}, 216 executedSteps: []string{ 217 "startContainer", 218 "pre1", 219 "pre3", 220 "step1", 221 "step2", 222 "step3", 223 "post3", 224 "post2", 225 "stopContainer", 226 "interpolateOutputs", 227 "closeContainer", 228 }, 229 result: "success", 230 hasError: false, 231 }, 232 } 233 234 contains := func(needle string, haystack []string) bool { 235 for _, item := range haystack { 236 if item == needle { 237 return true 238 } 239 } 240 return false 241 } 242 243 for _, tt := range table { 244 t.Run(tt.name, func(t *testing.T) { 245 fmt.Printf("::group::%s\n", tt.name) 246 247 ctx := common.WithJobErrorContainer(context.Background()) 248 jim := &jobInfoMock{} 249 sfm := &stepFactoryMock{} 250 rc := &RunContext{ 251 JobContainer: &jobContainerMock{}, 252 Run: &model.Run{ 253 JobID: "test", 254 Workflow: &model.Workflow{ 255 Jobs: map[string]*model.Job{ 256 "test": {}, 257 }, 258 }, 259 }, 260 Config: &Config{}, 261 } 262 rc.ExprEval = rc.NewExpressionEvaluator(ctx) 263 executorOrder := make([]string, 0) 264 265 jim.On("steps").Return(tt.steps) 266 267 if len(tt.steps) > 0 { 268 jim.On("startContainer").Return(func(ctx context.Context) error { 269 executorOrder = append(executorOrder, "startContainer") 270 return nil 271 }) 272 } 273 274 for i, stepModel := range tt.steps { 275 i := i 276 stepModel := stepModel 277 278 sm := &stepMock{} 279 280 sfm.On("newStep", stepModel, rc).Return(sm, nil) 281 282 sm.On("pre").Return(func(ctx context.Context) error { 283 if tt.preSteps[i] { 284 executorOrder = append(executorOrder, "pre"+stepModel.ID) 285 } 286 return nil 287 }) 288 289 sm.On("main").Return(func(ctx context.Context) error { 290 executorOrder = append(executorOrder, "step"+stepModel.ID) 291 if tt.hasError { 292 return fmt.Errorf("error") 293 } 294 return nil 295 }) 296 297 sm.On("post").Return(func(ctx context.Context) error { 298 if tt.postSteps[i] { 299 executorOrder = append(executorOrder, "post"+stepModel.ID) 300 } 301 return nil 302 }) 303 304 defer sm.AssertExpectations(t) 305 } 306 307 if len(tt.steps) > 0 { 308 jim.On("matrix").Return(map[string]interface{}{}) 309 310 jim.On("interpolateOutputs").Return(func(ctx context.Context) error { 311 executorOrder = append(executorOrder, "interpolateOutputs") 312 return nil 313 }) 314 315 if contains("stopContainer", tt.executedSteps) { 316 jim.On("stopContainer").Return(func(ctx context.Context) error { 317 executorOrder = append(executorOrder, "stopContainer") 318 return nil 319 }) 320 } 321 322 jim.On("result", tt.result) 323 324 jim.On("closeContainer").Return(func(ctx context.Context) error { 325 executorOrder = append(executorOrder, "closeContainer") 326 return nil 327 }) 328 } 329 330 executor := newJobExecutor(jim, sfm, rc) 331 err := executor(ctx) 332 assert.Nil(t, err) 333 assert.Equal(t, tt.executedSteps, executorOrder) 334 335 jim.AssertExpectations(t) 336 sfm.AssertExpectations(t) 337 338 fmt.Println("::endgroup::") 339 }) 340 } 341 }