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  }