github.com/nektos/act@v0.2.63/pkg/runner/step_test.go (about) 1 package runner 2 3 import ( 4 "context" 5 "testing" 6 7 "github.com/nektos/act/pkg/common" 8 "github.com/nektos/act/pkg/model" 9 log "github.com/sirupsen/logrus" 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/mock" 12 yaml "gopkg.in/yaml.v3" 13 ) 14 15 func TestMergeIntoMap(t *testing.T) { 16 table := []struct { 17 name string 18 target map[string]string 19 maps []map[string]string 20 expected map[string]string 21 }{ 22 { 23 name: "testEmptyMap", 24 target: map[string]string{}, 25 maps: []map[string]string{}, 26 expected: map[string]string{}, 27 }, 28 { 29 name: "testMergeIntoEmptyMap", 30 target: map[string]string{}, 31 maps: []map[string]string{ 32 { 33 "key1": "value1", 34 "key2": "value2", 35 }, { 36 "key2": "overridden", 37 "key3": "value3", 38 }, 39 }, 40 expected: map[string]string{ 41 "key1": "value1", 42 "key2": "overridden", 43 "key3": "value3", 44 }, 45 }, 46 { 47 name: "testMergeIntoExistingMap", 48 target: map[string]string{ 49 "key1": "value1", 50 "key2": "value2", 51 }, 52 maps: []map[string]string{ 53 { 54 "key1": "overridden", 55 }, 56 }, 57 expected: map[string]string{ 58 "key1": "overridden", 59 "key2": "value2", 60 }, 61 }, 62 } 63 64 for _, tt := range table { 65 t.Run(tt.name, func(t *testing.T) { 66 mergeIntoMapCaseSensitive(tt.target, tt.maps...) 67 assert.Equal(t, tt.expected, tt.target) 68 mergeIntoMapCaseInsensitive(tt.target, tt.maps...) 69 assert.Equal(t, tt.expected, tt.target) 70 }) 71 } 72 } 73 74 type stepMock struct { 75 mock.Mock 76 step 77 } 78 79 func (sm *stepMock) pre() common.Executor { 80 args := sm.Called() 81 return args.Get(0).(func(context.Context) error) 82 } 83 84 func (sm *stepMock) main() common.Executor { 85 args := sm.Called() 86 return args.Get(0).(func(context.Context) error) 87 } 88 89 func (sm *stepMock) post() common.Executor { 90 args := sm.Called() 91 return args.Get(0).(func(context.Context) error) 92 } 93 94 func (sm *stepMock) getRunContext() *RunContext { 95 args := sm.Called() 96 return args.Get(0).(*RunContext) 97 } 98 99 func (sm *stepMock) getGithubContext(ctx context.Context) *model.GithubContext { 100 args := sm.Called() 101 return args.Get(0).(*RunContext).getGithubContext(ctx) 102 } 103 104 func (sm *stepMock) getStepModel() *model.Step { 105 args := sm.Called() 106 return args.Get(0).(*model.Step) 107 } 108 109 func (sm *stepMock) getEnv() *map[string]string { 110 args := sm.Called() 111 return args.Get(0).(*map[string]string) 112 } 113 114 func TestSetupEnv(t *testing.T) { 115 cm := &containerMock{} 116 sm := &stepMock{} 117 118 rc := &RunContext{ 119 Config: &Config{ 120 Env: map[string]string{ 121 "GITHUB_RUN_ID": "runId", 122 }, 123 }, 124 Run: &model.Run{ 125 JobID: "1", 126 Workflow: &model.Workflow{ 127 Jobs: map[string]*model.Job{ 128 "1": { 129 Env: yaml.Node{ 130 Value: "JOB_KEY: jobvalue", 131 }, 132 }, 133 }, 134 }, 135 }, 136 Env: map[string]string{ 137 "RC_KEY": "rcvalue", 138 }, 139 JobContainer: cm, 140 } 141 step := &model.Step{ 142 With: map[string]string{ 143 "STEP_WITH": "with-value", 144 }, 145 } 146 env := map[string]string{} 147 148 sm.On("getRunContext").Return(rc) 149 sm.On("getGithubContext").Return(rc) 150 sm.On("getStepModel").Return(step) 151 sm.On("getEnv").Return(&env) 152 153 err := setupEnv(context.Background(), sm) 154 assert.Nil(t, err) 155 156 // These are commit or system specific 157 delete((env), "GITHUB_REF") 158 delete((env), "GITHUB_REF_NAME") 159 delete((env), "GITHUB_REF_TYPE") 160 delete((env), "GITHUB_SHA") 161 delete((env), "GITHUB_WORKSPACE") 162 delete((env), "GITHUB_REPOSITORY") 163 delete((env), "GITHUB_REPOSITORY_OWNER") 164 delete((env), "GITHUB_ACTOR") 165 166 assert.Equal(t, map[string]string{ 167 "ACT": "true", 168 "CI": "true", 169 "GITHUB_ACTION": "", 170 "GITHUB_ACTIONS": "true", 171 "GITHUB_ACTION_PATH": "", 172 "GITHUB_ACTION_REF": "", 173 "GITHUB_ACTION_REPOSITORY": "", 174 "GITHUB_API_URL": "https:///api/v3", 175 "GITHUB_BASE_REF": "", 176 "GITHUB_EVENT_NAME": "", 177 "GITHUB_EVENT_PATH": "/var/run/act/workflow/event.json", 178 "GITHUB_GRAPHQL_URL": "https:///api/graphql", 179 "GITHUB_HEAD_REF": "", 180 "GITHUB_JOB": "1", 181 "GITHUB_RETENTION_DAYS": "0", 182 "GITHUB_RUN_ID": "runId", 183 "GITHUB_RUN_NUMBER": "1", 184 "GITHUB_SERVER_URL": "https://", 185 "GITHUB_WORKFLOW": "", 186 "INPUT_STEP_WITH": "with-value", 187 "RC_KEY": "rcvalue", 188 "RUNNER_PERFLOG": "/dev/null", 189 "RUNNER_TRACKING_ID": "", 190 }, env) 191 192 cm.AssertExpectations(t) 193 } 194 195 func TestIsStepEnabled(t *testing.T) { 196 createTestStep := func(t *testing.T, input string) step { 197 var step *model.Step 198 err := yaml.Unmarshal([]byte(input), &step) 199 assert.NoError(t, err) 200 201 return &stepRun{ 202 RunContext: &RunContext{ 203 Config: &Config{ 204 Workdir: ".", 205 Platforms: map[string]string{ 206 "ubuntu-latest": "ubuntu-latest", 207 }, 208 }, 209 StepResults: map[string]*model.StepResult{}, 210 Env: map[string]string{}, 211 Run: &model.Run{ 212 JobID: "job1", 213 Workflow: &model.Workflow{ 214 Name: "workflow1", 215 Jobs: map[string]*model.Job{ 216 "job1": createJob(t, `runs-on: ubuntu-latest`, ""), 217 }, 218 }, 219 }, 220 }, 221 Step: step, 222 } 223 } 224 225 log.SetLevel(log.DebugLevel) 226 assertObject := assert.New(t) 227 228 // success() 229 step := createTestStep(t, "if: success()") 230 assertObject.True(isStepEnabled(context.Background(), step.getIfExpression(context.Background(), stepStageMain), step, stepStageMain)) 231 232 step = createTestStep(t, "if: success()") 233 step.getRunContext().StepResults["a"] = &model.StepResult{ 234 Conclusion: model.StepStatusSuccess, 235 } 236 assertObject.True(isStepEnabled(context.Background(), step.getStepModel().If.Value, step, stepStageMain)) 237 238 step = createTestStep(t, "if: success()") 239 step.getRunContext().StepResults["a"] = &model.StepResult{ 240 Conclusion: model.StepStatusFailure, 241 } 242 assertObject.False(isStepEnabled(context.Background(), step.getStepModel().If.Value, step, stepStageMain)) 243 244 // failure() 245 step = createTestStep(t, "if: failure()") 246 assertObject.False(isStepEnabled(context.Background(), step.getStepModel().If.Value, step, stepStageMain)) 247 248 step = createTestStep(t, "if: failure()") 249 step.getRunContext().StepResults["a"] = &model.StepResult{ 250 Conclusion: model.StepStatusSuccess, 251 } 252 assertObject.False(isStepEnabled(context.Background(), step.getStepModel().If.Value, step, stepStageMain)) 253 254 step = createTestStep(t, "if: failure()") 255 step.getRunContext().StepResults["a"] = &model.StepResult{ 256 Conclusion: model.StepStatusFailure, 257 } 258 assertObject.True(isStepEnabled(context.Background(), step.getStepModel().If.Value, step, stepStageMain)) 259 260 // always() 261 step = createTestStep(t, "if: always()") 262 assertObject.True(isStepEnabled(context.Background(), step.getStepModel().If.Value, step, stepStageMain)) 263 264 step = createTestStep(t, "if: always()") 265 step.getRunContext().StepResults["a"] = &model.StepResult{ 266 Conclusion: model.StepStatusSuccess, 267 } 268 assertObject.True(isStepEnabled(context.Background(), step.getStepModel().If.Value, step, stepStageMain)) 269 270 step = createTestStep(t, "if: always()") 271 step.getRunContext().StepResults["a"] = &model.StepResult{ 272 Conclusion: model.StepStatusFailure, 273 } 274 assertObject.True(isStepEnabled(context.Background(), step.getStepModel().If.Value, step, stepStageMain)) 275 } 276 277 func TestIsContinueOnError(t *testing.T) { 278 createTestStep := func(t *testing.T, input string) step { 279 var step *model.Step 280 err := yaml.Unmarshal([]byte(input), &step) 281 assert.NoError(t, err) 282 283 return &stepRun{ 284 RunContext: &RunContext{ 285 Config: &Config{ 286 Workdir: ".", 287 Platforms: map[string]string{ 288 "ubuntu-latest": "ubuntu-latest", 289 }, 290 }, 291 StepResults: map[string]*model.StepResult{}, 292 Env: map[string]string{}, 293 Run: &model.Run{ 294 JobID: "job1", 295 Workflow: &model.Workflow{ 296 Name: "workflow1", 297 Jobs: map[string]*model.Job{ 298 "job1": createJob(t, `runs-on: ubuntu-latest`, ""), 299 }, 300 }, 301 }, 302 }, 303 Step: step, 304 } 305 } 306 307 log.SetLevel(log.DebugLevel) 308 assertObject := assert.New(t) 309 310 // absent 311 step := createTestStep(t, "name: test") 312 continueOnError, err := isContinueOnError(context.Background(), step.getStepModel().RawContinueOnError, step, stepStageMain) 313 assertObject.False(continueOnError) 314 assertObject.Nil(err) 315 316 // explicit true 317 step = createTestStep(t, "continue-on-error: true") 318 continueOnError, err = isContinueOnError(context.Background(), step.getStepModel().RawContinueOnError, step, stepStageMain) 319 assertObject.True(continueOnError) 320 assertObject.Nil(err) 321 322 // explicit false 323 step = createTestStep(t, "continue-on-error: false") 324 continueOnError, err = isContinueOnError(context.Background(), step.getStepModel().RawContinueOnError, step, stepStageMain) 325 assertObject.False(continueOnError) 326 assertObject.Nil(err) 327 328 // expression true 329 step = createTestStep(t, "continue-on-error: ${{ 'test' == 'test' }}") 330 continueOnError, err = isContinueOnError(context.Background(), step.getStepModel().RawContinueOnError, step, stepStageMain) 331 assertObject.True(continueOnError) 332 assertObject.Nil(err) 333 334 // expression false 335 step = createTestStep(t, "continue-on-error: ${{ 'test' != 'test' }}") 336 continueOnError, err = isContinueOnError(context.Background(), step.getStepModel().RawContinueOnError, step, stepStageMain) 337 assertObject.False(continueOnError) 338 assertObject.Nil(err) 339 340 // expression parse error 341 step = createTestStep(t, "continue-on-error: ${{ 'test' != test }}") 342 continueOnError, err = isContinueOnError(context.Background(), step.getStepModel().RawContinueOnError, step, stepStageMain) 343 assertObject.False(continueOnError) 344 assertObject.NotNil(err) 345 }