github.com/nektos/act@v0.2.83/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 Uses: "./", 143 With: map[string]string{ 144 "STEP_WITH": "with-value", 145 }, 146 } 147 env := map[string]string{} 148 149 sm.On("getRunContext").Return(rc) 150 sm.On("getGithubContext").Return(rc) 151 sm.On("getStepModel").Return(step) 152 sm.On("getEnv").Return(&env) 153 154 err := setupEnv(context.Background(), sm) 155 assert.Nil(t, err) 156 157 // These are commit or system specific 158 delete((env), "GITHUB_REF") 159 delete((env), "GITHUB_REF_NAME") 160 delete((env), "GITHUB_REF_TYPE") 161 delete((env), "GITHUB_SHA") 162 delete((env), "GITHUB_WORKSPACE") 163 delete((env), "GITHUB_REPOSITORY") 164 delete((env), "GITHUB_REPOSITORY_OWNER") 165 delete((env), "GITHUB_ACTOR") 166 167 assert.Equal(t, map[string]string{ 168 "ACT": "true", 169 "CI": "true", 170 "GITHUB_ACTION": "", 171 "GITHUB_ACTIONS": "true", 172 "GITHUB_ACTION_PATH": "", 173 "GITHUB_ACTION_REF": "", 174 "GITHUB_ACTION_REPOSITORY": "", 175 "GITHUB_API_URL": "https:///api/v3", 176 "GITHUB_BASE_REF": "", 177 "GITHUB_EVENT_NAME": "", 178 "GITHUB_EVENT_PATH": "/var/run/act/workflow/event.json", 179 "GITHUB_GRAPHQL_URL": "https:///api/graphql", 180 "GITHUB_HEAD_REF": "", 181 "GITHUB_JOB": "1", 182 "GITHUB_RETENTION_DAYS": "0", 183 "GITHUB_RUN_ID": "runId", 184 "GITHUB_RUN_NUMBER": "1", 185 "GITHUB_RUN_ATTEMPT": "1", 186 "GITHUB_SERVER_URL": "https://", 187 "GITHUB_WORKFLOW": "", 188 "INPUT_STEP_WITH": "with-value", 189 "RC_KEY": "rcvalue", 190 "RUNNER_PERFLOG": "/dev/null", 191 "RUNNER_TRACKING_ID": "", 192 }, env) 193 194 cm.AssertExpectations(t) 195 } 196 197 func TestIsStepEnabled(t *testing.T) { 198 createTestStep := func(t *testing.T, input string) step { 199 var step *model.Step 200 err := yaml.Unmarshal([]byte(input), &step) 201 assert.NoError(t, err) 202 203 return &stepRun{ 204 RunContext: &RunContext{ 205 Config: &Config{ 206 Workdir: ".", 207 Platforms: map[string]string{ 208 "ubuntu-latest": "ubuntu-latest", 209 }, 210 }, 211 StepResults: map[string]*model.StepResult{}, 212 Env: map[string]string{}, 213 Run: &model.Run{ 214 JobID: "job1", 215 Workflow: &model.Workflow{ 216 Name: "workflow1", 217 Jobs: map[string]*model.Job{ 218 "job1": createJob(t, `runs-on: ubuntu-latest`, ""), 219 }, 220 }, 221 }, 222 }, 223 Step: step, 224 } 225 } 226 227 log.SetLevel(log.DebugLevel) 228 assertObject := assert.New(t) 229 230 // success() 231 step := createTestStep(t, "if: success()") 232 assertObject.True(isStepEnabled(context.Background(), step.getIfExpression(context.Background(), stepStageMain), step, stepStageMain)) 233 234 step = createTestStep(t, "if: success()") 235 step.getRunContext().StepResults["a"] = &model.StepResult{ 236 Conclusion: model.StepStatusSuccess, 237 } 238 assertObject.True(isStepEnabled(context.Background(), step.getStepModel().If.Value, step, stepStageMain)) 239 240 step = createTestStep(t, "if: success()") 241 step.getRunContext().StepResults["a"] = &model.StepResult{ 242 Conclusion: model.StepStatusFailure, 243 } 244 assertObject.False(isStepEnabled(context.Background(), step.getStepModel().If.Value, step, stepStageMain)) 245 246 // failure() 247 step = createTestStep(t, "if: failure()") 248 assertObject.False(isStepEnabled(context.Background(), step.getStepModel().If.Value, step, stepStageMain)) 249 250 step = createTestStep(t, "if: failure()") 251 step.getRunContext().StepResults["a"] = &model.StepResult{ 252 Conclusion: model.StepStatusSuccess, 253 } 254 assertObject.False(isStepEnabled(context.Background(), step.getStepModel().If.Value, step, stepStageMain)) 255 256 step = createTestStep(t, "if: failure()") 257 step.getRunContext().StepResults["a"] = &model.StepResult{ 258 Conclusion: model.StepStatusFailure, 259 } 260 assertObject.True(isStepEnabled(context.Background(), step.getStepModel().If.Value, step, stepStageMain)) 261 262 // always() 263 step = createTestStep(t, "if: always()") 264 assertObject.True(isStepEnabled(context.Background(), step.getStepModel().If.Value, step, stepStageMain)) 265 266 step = createTestStep(t, "if: always()") 267 step.getRunContext().StepResults["a"] = &model.StepResult{ 268 Conclusion: model.StepStatusSuccess, 269 } 270 assertObject.True(isStepEnabled(context.Background(), step.getStepModel().If.Value, step, stepStageMain)) 271 272 step = createTestStep(t, "if: always()") 273 step.getRunContext().StepResults["a"] = &model.StepResult{ 274 Conclusion: model.StepStatusFailure, 275 } 276 assertObject.True(isStepEnabled(context.Background(), step.getStepModel().If.Value, step, stepStageMain)) 277 } 278 279 func TestIsContinueOnError(t *testing.T) { 280 createTestStep := func(t *testing.T, input string) step { 281 var step *model.Step 282 err := yaml.Unmarshal([]byte(input), &step) 283 assert.NoError(t, err) 284 285 return &stepRun{ 286 RunContext: &RunContext{ 287 Config: &Config{ 288 Workdir: ".", 289 Platforms: map[string]string{ 290 "ubuntu-latest": "ubuntu-latest", 291 }, 292 }, 293 StepResults: map[string]*model.StepResult{}, 294 Env: map[string]string{}, 295 Run: &model.Run{ 296 JobID: "job1", 297 Workflow: &model.Workflow{ 298 Name: "workflow1", 299 Jobs: map[string]*model.Job{ 300 "job1": createJob(t, `runs-on: ubuntu-latest`, ""), 301 }, 302 }, 303 }, 304 }, 305 Step: step, 306 } 307 } 308 309 log.SetLevel(log.DebugLevel) 310 assertObject := assert.New(t) 311 312 // absent 313 step := createTestStep(t, "name: test") 314 continueOnError, err := isContinueOnError(context.Background(), step.getStepModel().RawContinueOnError, step, stepStageMain) 315 assertObject.False(continueOnError) 316 assertObject.Nil(err) 317 318 // explicit true 319 step = createTestStep(t, "continue-on-error: true") 320 continueOnError, err = isContinueOnError(context.Background(), step.getStepModel().RawContinueOnError, step, stepStageMain) 321 assertObject.True(continueOnError) 322 assertObject.Nil(err) 323 324 // explicit false 325 step = createTestStep(t, "continue-on-error: false") 326 continueOnError, err = isContinueOnError(context.Background(), step.getStepModel().RawContinueOnError, step, stepStageMain) 327 assertObject.False(continueOnError) 328 assertObject.Nil(err) 329 330 // expression true 331 step = createTestStep(t, "continue-on-error: ${{ 'test' == 'test' }}") 332 continueOnError, err = isContinueOnError(context.Background(), step.getStepModel().RawContinueOnError, step, stepStageMain) 333 assertObject.True(continueOnError) 334 assertObject.Nil(err) 335 336 // expression false 337 step = createTestStep(t, "continue-on-error: ${{ 'test' != 'test' }}") 338 continueOnError, err = isContinueOnError(context.Background(), step.getStepModel().RawContinueOnError, step, stepStageMain) 339 assertObject.False(continueOnError) 340 assertObject.Nil(err) 341 342 // expression parse error 343 step = createTestStep(t, "continue-on-error: ${{ 'test' != test }}") 344 continueOnError, err = isContinueOnError(context.Background(), step.getStepModel().RawContinueOnError, step, stepStageMain) 345 assertObject.False(continueOnError) 346 assertObject.NotNil(err) 347 }