github.com/nektos/act@v0.2.63/pkg/model/workflow_test.go (about) 1 package model 2 3 import ( 4 "strings" 5 "testing" 6 7 "github.com/stretchr/testify/assert" 8 ) 9 10 func TestReadWorkflow_StringEvent(t *testing.T) { 11 yaml := ` 12 name: local-action-docker-url 13 on: push 14 15 jobs: 16 test: 17 runs-on: ubuntu-latest 18 steps: 19 - uses: ./actions/docker-url 20 ` 21 22 workflow, err := ReadWorkflow(strings.NewReader(yaml)) 23 assert.NoError(t, err, "read workflow should succeed") 24 25 assert.Len(t, workflow.On(), 1) 26 assert.Contains(t, workflow.On(), "push") 27 } 28 29 func TestReadWorkflow_ListEvent(t *testing.T) { 30 yaml := ` 31 name: local-action-docker-url 32 on: [push, pull_request] 33 34 jobs: 35 test: 36 runs-on: ubuntu-latest 37 steps: 38 - uses: ./actions/docker-url 39 ` 40 41 workflow, err := ReadWorkflow(strings.NewReader(yaml)) 42 assert.NoError(t, err, "read workflow should succeed") 43 44 assert.Len(t, workflow.On(), 2) 45 assert.Contains(t, workflow.On(), "push") 46 assert.Contains(t, workflow.On(), "pull_request") 47 } 48 49 func TestReadWorkflow_MapEvent(t *testing.T) { 50 yaml := ` 51 name: local-action-docker-url 52 on: 53 push: 54 branches: 55 - master 56 pull_request: 57 branches: 58 - master 59 60 jobs: 61 test: 62 runs-on: ubuntu-latest 63 steps: 64 - uses: ./actions/docker-url 65 ` 66 67 workflow, err := ReadWorkflow(strings.NewReader(yaml)) 68 assert.NoError(t, err, "read workflow should succeed") 69 assert.Len(t, workflow.On(), 2) 70 assert.Contains(t, workflow.On(), "push") 71 assert.Contains(t, workflow.On(), "pull_request") 72 } 73 74 func TestReadWorkflow_RunsOnLabels(t *testing.T) { 75 yaml := ` 76 name: local-action-docker-url 77 78 jobs: 79 test: 80 container: nginx:latest 81 runs-on: 82 labels: ubuntu-latest 83 steps: 84 - uses: ./actions/docker-url` 85 86 workflow, err := ReadWorkflow(strings.NewReader(yaml)) 87 assert.NoError(t, err, "read workflow should succeed") 88 assert.Equal(t, workflow.Jobs["test"].RunsOn(), []string{"ubuntu-latest"}) 89 } 90 91 func TestReadWorkflow_RunsOnLabelsWithGroup(t *testing.T) { 92 yaml := ` 93 name: local-action-docker-url 94 95 jobs: 96 test: 97 container: nginx:latest 98 runs-on: 99 labels: [ubuntu-latest] 100 group: linux 101 steps: 102 - uses: ./actions/docker-url` 103 104 workflow, err := ReadWorkflow(strings.NewReader(yaml)) 105 assert.NoError(t, err, "read workflow should succeed") 106 assert.Equal(t, workflow.Jobs["test"].RunsOn(), []string{"ubuntu-latest", "linux"}) 107 } 108 109 func TestReadWorkflow_StringContainer(t *testing.T) { 110 yaml := ` 111 name: local-action-docker-url 112 113 jobs: 114 test: 115 container: nginx:latest 116 runs-on: ubuntu-latest 117 steps: 118 - uses: ./actions/docker-url 119 test2: 120 container: 121 image: nginx:latest 122 env: 123 foo: bar 124 runs-on: ubuntu-latest 125 steps: 126 - uses: ./actions/docker-url 127 ` 128 129 workflow, err := ReadWorkflow(strings.NewReader(yaml)) 130 assert.NoError(t, err, "read workflow should succeed") 131 assert.Len(t, workflow.Jobs, 2) 132 assert.Contains(t, workflow.Jobs["test"].Container().Image, "nginx:latest") 133 assert.Contains(t, workflow.Jobs["test2"].Container().Image, "nginx:latest") 134 assert.Contains(t, workflow.Jobs["test2"].Container().Env["foo"], "bar") 135 } 136 137 func TestReadWorkflow_ObjectContainer(t *testing.T) { 138 yaml := ` 139 name: local-action-docker-url 140 141 jobs: 142 test: 143 container: 144 image: r.example.org/something:latest 145 credentials: 146 username: registry-username 147 password: registry-password 148 env: 149 HOME: /home/user 150 volumes: 151 - my_docker_volume:/volume_mount 152 - /data/my_data 153 - /source/directory:/destination/directory 154 runs-on: ubuntu-latest 155 steps: 156 - uses: ./actions/docker-url 157 ` 158 159 workflow, err := ReadWorkflow(strings.NewReader(yaml)) 160 assert.NoError(t, err, "read workflow should succeed") 161 assert.Len(t, workflow.Jobs, 1) 162 163 container := workflow.GetJob("test").Container() 164 165 assert.Contains(t, container.Image, "r.example.org/something:latest") 166 assert.Contains(t, container.Env["HOME"], "/home/user") 167 assert.Contains(t, container.Credentials["username"], "registry-username") 168 assert.Contains(t, container.Credentials["password"], "registry-password") 169 assert.ElementsMatch(t, container.Volumes, []string{ 170 "my_docker_volume:/volume_mount", 171 "/data/my_data", 172 "/source/directory:/destination/directory", 173 }) 174 } 175 176 func TestReadWorkflow_JobTypes(t *testing.T) { 177 yaml := ` 178 name: invalid job definition 179 180 jobs: 181 default-job: 182 runs-on: ubuntu-latest 183 steps: 184 - run: echo 185 remote-reusable-workflow-yml: 186 uses: remote/repo/some/path/to/workflow.yml@main 187 remote-reusable-workflow-yaml: 188 uses: remote/repo/some/path/to/workflow.yaml@main 189 remote-reusable-workflow-custom-path: 190 uses: remote/repo/path/to/workflow.yml@main 191 local-reusable-workflow-yml: 192 uses: ./some/path/to/workflow.yml 193 local-reusable-workflow-yaml: 194 uses: ./some/path/to/workflow.yaml 195 ` 196 197 workflow, err := ReadWorkflow(strings.NewReader(yaml)) 198 assert.NoError(t, err, "read workflow should succeed") 199 assert.Len(t, workflow.Jobs, 6) 200 201 jobType, err := workflow.Jobs["default-job"].Type() 202 assert.Equal(t, nil, err) 203 assert.Equal(t, JobTypeDefault, jobType) 204 205 jobType, err = workflow.Jobs["remote-reusable-workflow-yml"].Type() 206 assert.Equal(t, nil, err) 207 assert.Equal(t, JobTypeReusableWorkflowRemote, jobType) 208 209 jobType, err = workflow.Jobs["remote-reusable-workflow-yaml"].Type() 210 assert.Equal(t, nil, err) 211 assert.Equal(t, JobTypeReusableWorkflowRemote, jobType) 212 213 jobType, err = workflow.Jobs["remote-reusable-workflow-custom-path"].Type() 214 assert.Equal(t, nil, err) 215 assert.Equal(t, JobTypeReusableWorkflowRemote, jobType) 216 217 jobType, err = workflow.Jobs["local-reusable-workflow-yml"].Type() 218 assert.Equal(t, nil, err) 219 assert.Equal(t, JobTypeReusableWorkflowLocal, jobType) 220 221 jobType, err = workflow.Jobs["local-reusable-workflow-yaml"].Type() 222 assert.Equal(t, nil, err) 223 assert.Equal(t, JobTypeReusableWorkflowLocal, jobType) 224 } 225 226 func TestReadWorkflow_JobTypes_InvalidPath(t *testing.T) { 227 yaml := ` 228 name: invalid job definition 229 230 jobs: 231 remote-reusable-workflow-missing-version: 232 uses: remote/repo/some/path/to/workflow.yml 233 remote-reusable-workflow-bad-extension: 234 uses: remote/repo/some/path/to/workflow.json 235 local-reusable-workflow-bad-extension: 236 uses: ./some/path/to/workflow.json 237 local-reusable-workflow-bad-path: 238 uses: some/path/to/workflow.yaml 239 ` 240 241 workflow, err := ReadWorkflow(strings.NewReader(yaml)) 242 assert.NoError(t, err, "read workflow should succeed") 243 assert.Len(t, workflow.Jobs, 4) 244 245 jobType, err := workflow.Jobs["remote-reusable-workflow-missing-version"].Type() 246 assert.Equal(t, JobTypeInvalid, jobType) 247 assert.NotEqual(t, nil, err) 248 249 jobType, err = workflow.Jobs["remote-reusable-workflow-bad-extension"].Type() 250 assert.Equal(t, JobTypeInvalid, jobType) 251 assert.NotEqual(t, nil, err) 252 253 jobType, err = workflow.Jobs["local-reusable-workflow-bad-extension"].Type() 254 assert.Equal(t, JobTypeInvalid, jobType) 255 assert.NotEqual(t, nil, err) 256 257 jobType, err = workflow.Jobs["local-reusable-workflow-bad-path"].Type() 258 assert.Equal(t, JobTypeInvalid, jobType) 259 assert.NotEqual(t, nil, err) 260 } 261 262 func TestReadWorkflow_StepsTypes(t *testing.T) { 263 yaml := ` 264 name: invalid step definition 265 266 jobs: 267 test: 268 runs-on: ubuntu-latest 269 steps: 270 - name: test1 271 uses: actions/checkout@v2 272 run: echo 273 - name: test2 274 run: echo 275 - name: test3 276 uses: actions/checkout@v2 277 - name: test4 278 uses: docker://nginx:latest 279 - name: test5 280 uses: ./local-action 281 ` 282 283 workflow, err := ReadWorkflow(strings.NewReader(yaml)) 284 assert.NoError(t, err, "read workflow should succeed") 285 assert.Len(t, workflow.Jobs, 1) 286 assert.Len(t, workflow.Jobs["test"].Steps, 5) 287 assert.Equal(t, workflow.Jobs["test"].Steps[0].Type(), StepTypeInvalid) 288 assert.Equal(t, workflow.Jobs["test"].Steps[1].Type(), StepTypeRun) 289 assert.Equal(t, workflow.Jobs["test"].Steps[2].Type(), StepTypeUsesActionRemote) 290 assert.Equal(t, workflow.Jobs["test"].Steps[3].Type(), StepTypeUsesDockerURL) 291 assert.Equal(t, workflow.Jobs["test"].Steps[4].Type(), StepTypeUsesActionLocal) 292 } 293 294 // See: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idoutputs 295 func TestReadWorkflow_JobOutputs(t *testing.T) { 296 yaml := ` 297 name: job outputs definition 298 299 jobs: 300 test1: 301 runs-on: ubuntu-latest 302 steps: 303 - id: test1_1 304 run: | 305 echo "::set-output name=a_key::some-a_value" 306 echo "::set-output name=b-key::some-b-value" 307 outputs: 308 some_a_key: ${{ steps.test1_1.outputs.a_key }} 309 some-b-key: ${{ steps.test1_1.outputs.b-key }} 310 311 test2: 312 runs-on: ubuntu-latest 313 needs: 314 - test1 315 steps: 316 - name: test2_1 317 run: | 318 echo "${{ needs.test1.outputs.some_a_key }}" 319 echo "${{ needs.test1.outputs.some-b-key }}" 320 ` 321 322 workflow, err := ReadWorkflow(strings.NewReader(yaml)) 323 assert.NoError(t, err, "read workflow should succeed") 324 assert.Len(t, workflow.Jobs, 2) 325 326 assert.Len(t, workflow.Jobs["test1"].Steps, 1) 327 assert.Equal(t, StepTypeRun, workflow.Jobs["test1"].Steps[0].Type()) 328 assert.Equal(t, "test1_1", workflow.Jobs["test1"].Steps[0].ID) 329 assert.Len(t, workflow.Jobs["test1"].Outputs, 2) 330 assert.Contains(t, workflow.Jobs["test1"].Outputs, "some_a_key") 331 assert.Contains(t, workflow.Jobs["test1"].Outputs, "some-b-key") 332 assert.Equal(t, "${{ steps.test1_1.outputs.a_key }}", workflow.Jobs["test1"].Outputs["some_a_key"]) 333 assert.Equal(t, "${{ steps.test1_1.outputs.b-key }}", workflow.Jobs["test1"].Outputs["some-b-key"]) 334 } 335 336 func TestReadWorkflow_Strategy(t *testing.T) { 337 w, err := NewWorkflowPlanner("testdata/strategy/push.yml", true) 338 assert.NoError(t, err) 339 340 p, err := w.PlanJob("strategy-only-max-parallel") 341 assert.NoError(t, err) 342 343 assert.Equal(t, len(p.Stages), 1) 344 assert.Equal(t, len(p.Stages[0].Runs), 1) 345 346 wf := p.Stages[0].Runs[0].Workflow 347 348 job := wf.Jobs["strategy-only-max-parallel"] 349 matrixes, err := job.GetMatrixes() 350 assert.NoError(t, err) 351 assert.Equal(t, matrixes, []map[string]interface{}{{}}) 352 assert.Equal(t, job.Matrix(), map[string][]interface{}(nil)) 353 assert.Equal(t, job.Strategy.MaxParallel, 2) 354 assert.Equal(t, job.Strategy.FailFast, true) 355 356 job = wf.Jobs["strategy-only-fail-fast"] 357 matrixes, err = job.GetMatrixes() 358 assert.NoError(t, err) 359 assert.Equal(t, matrixes, []map[string]interface{}{{}}) 360 assert.Equal(t, job.Matrix(), map[string][]interface{}(nil)) 361 assert.Equal(t, job.Strategy.MaxParallel, 4) 362 assert.Equal(t, job.Strategy.FailFast, false) 363 364 job = wf.Jobs["strategy-no-matrix"] 365 matrixes, err = job.GetMatrixes() 366 assert.NoError(t, err) 367 assert.Equal(t, matrixes, []map[string]interface{}{{}}) 368 assert.Equal(t, job.Matrix(), map[string][]interface{}(nil)) 369 assert.Equal(t, job.Strategy.MaxParallel, 2) 370 assert.Equal(t, job.Strategy.FailFast, false) 371 372 job = wf.Jobs["strategy-all"] 373 matrixes, err = job.GetMatrixes() 374 assert.NoError(t, err) 375 assert.Equal(t, matrixes, 376 []map[string]interface{}{ 377 {"datacenter": "site-c", "node-version": "14.x", "site": "staging"}, 378 {"datacenter": "site-c", "node-version": "16.x", "site": "staging"}, 379 {"datacenter": "site-d", "node-version": "16.x", "site": "staging"}, 380 {"php-version": 5.4}, 381 {"datacenter": "site-a", "node-version": "10.x", "site": "prod"}, 382 {"datacenter": "site-b", "node-version": "12.x", "site": "dev"}, 383 }, 384 ) 385 assert.Equal(t, job.Matrix(), 386 map[string][]interface{}{ 387 "datacenter": {"site-c", "site-d"}, 388 "exclude": { 389 map[string]interface{}{"datacenter": "site-d", "node-version": "14.x", "site": "staging"}, 390 }, 391 "include": { 392 map[string]interface{}{"php-version": 5.4}, 393 map[string]interface{}{"datacenter": "site-a", "node-version": "10.x", "site": "prod"}, 394 map[string]interface{}{"datacenter": "site-b", "node-version": "12.x", "site": "dev"}, 395 }, 396 "node-version": {"14.x", "16.x"}, 397 "site": {"staging"}, 398 }, 399 ) 400 assert.Equal(t, job.Strategy.MaxParallel, 2) 401 assert.Equal(t, job.Strategy.FailFast, false) 402 } 403 404 func TestStep_ShellCommand(t *testing.T) { 405 tests := []struct { 406 shell string 407 want string 408 }{ 409 {"pwsh -v '. {0}'", "pwsh -v '. {0}'"}, 410 {"pwsh", "pwsh -command . '{0}'"}, 411 {"powershell", "powershell -command . '{0}'"}, 412 } 413 for _, tt := range tests { 414 t.Run(tt.shell, func(t *testing.T) { 415 got := (&Step{Shell: tt.shell}).ShellCommand() 416 assert.Equal(t, got, tt.want) 417 }) 418 } 419 } 420 421 func TestReadWorkflow_WorkflowDispatchConfig(t *testing.T) { 422 yaml := ` 423 name: local-action-docker-url 424 ` 425 workflow, err := ReadWorkflow(strings.NewReader(yaml)) 426 assert.NoError(t, err, "read workflow should succeed") 427 workflowDispatch := workflow.WorkflowDispatchConfig() 428 assert.Nil(t, workflowDispatch) 429 430 yaml = ` 431 name: local-action-docker-url 432 on: push 433 ` 434 workflow, err = ReadWorkflow(strings.NewReader(yaml)) 435 assert.NoError(t, err, "read workflow should succeed") 436 workflowDispatch = workflow.WorkflowDispatchConfig() 437 assert.Nil(t, workflowDispatch) 438 439 yaml = ` 440 name: local-action-docker-url 441 on: workflow_dispatch 442 ` 443 workflow, err = ReadWorkflow(strings.NewReader(yaml)) 444 assert.NoError(t, err, "read workflow should succeed") 445 workflowDispatch = workflow.WorkflowDispatchConfig() 446 assert.NotNil(t, workflowDispatch) 447 assert.Nil(t, workflowDispatch.Inputs) 448 449 yaml = ` 450 name: local-action-docker-url 451 on: [push, pull_request] 452 ` 453 workflow, err = ReadWorkflow(strings.NewReader(yaml)) 454 assert.NoError(t, err, "read workflow should succeed") 455 workflowDispatch = workflow.WorkflowDispatchConfig() 456 assert.Nil(t, workflowDispatch) 457 458 yaml = ` 459 name: local-action-docker-url 460 on: [push, workflow_dispatch] 461 ` 462 workflow, err = ReadWorkflow(strings.NewReader(yaml)) 463 assert.NoError(t, err, "read workflow should succeed") 464 workflowDispatch = workflow.WorkflowDispatchConfig() 465 assert.NotNil(t, workflowDispatch) 466 assert.Nil(t, workflowDispatch.Inputs) 467 468 yaml = ` 469 name: local-action-docker-url 470 on: 471 - push 472 - workflow_dispatch 473 ` 474 workflow, err = ReadWorkflow(strings.NewReader(yaml)) 475 assert.NoError(t, err, "read workflow should succeed") 476 workflowDispatch = workflow.WorkflowDispatchConfig() 477 assert.NotNil(t, workflowDispatch) 478 assert.Nil(t, workflowDispatch.Inputs) 479 480 yaml = ` 481 name: local-action-docker-url 482 on: 483 push: 484 pull_request: 485 ` 486 workflow, err = ReadWorkflow(strings.NewReader(yaml)) 487 assert.NoError(t, err, "read workflow should succeed") 488 workflowDispatch = workflow.WorkflowDispatchConfig() 489 assert.Nil(t, workflowDispatch) 490 491 yaml = ` 492 name: local-action-docker-url 493 on: 494 push: 495 pull_request: 496 workflow_dispatch: 497 inputs: 498 logLevel: 499 description: 'Log level' 500 required: true 501 default: 'warning' 502 type: choice 503 options: 504 - info 505 - warning 506 - debug 507 ` 508 workflow, err = ReadWorkflow(strings.NewReader(yaml)) 509 assert.NoError(t, err, "read workflow should succeed") 510 workflowDispatch = workflow.WorkflowDispatchConfig() 511 assert.NotNil(t, workflowDispatch) 512 assert.Equal(t, WorkflowDispatchInput{ 513 Default: "warning", 514 Description: "Log level", 515 Options: []string{ 516 "info", 517 "warning", 518 "debug", 519 }, 520 Required: true, 521 Type: "choice", 522 }, workflowDispatch.Inputs["logLevel"]) 523 }