diff options
Diffstat (limited to 'pkg/model/workflow_test.go')
-rw-r--r-- | pkg/model/workflow_test.go | 624 |
1 files changed, 624 insertions, 0 deletions
diff --git a/pkg/model/workflow_test.go b/pkg/model/workflow_test.go new file mode 100644 index 0000000..d253906 --- /dev/null +++ b/pkg/model/workflow_test.go @@ -0,0 +1,624 @@ +package model + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestReadWorkflow_ScheduleEvent(t *testing.T) { + yaml := ` +name: local-action-docker-url +on: + schedule: + - cron: '30 5 * * 1,3' + - cron: '30 5 * * 2,4' + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: ./actions/docker-url +` + + workflow, err := ReadWorkflow(strings.NewReader(yaml)) + assert.NoError(t, err, "read workflow should succeed") + + schedules := workflow.OnEvent("schedule") + assert.Len(t, schedules, 2) + + newSchedules := workflow.OnSchedule() + assert.Len(t, newSchedules, 2) + + assert.Equal(t, "30 5 * * 1,3", newSchedules[0]) + assert.Equal(t, "30 5 * * 2,4", newSchedules[1]) + + yaml = ` +name: local-action-docker-url +on: + schedule: + test: '30 5 * * 1,3' + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: ./actions/docker-url +` + + workflow, err = ReadWorkflow(strings.NewReader(yaml)) + assert.NoError(t, err, "read workflow should succeed") + + newSchedules = workflow.OnSchedule() + assert.Len(t, newSchedules, 0) + + yaml = ` +name: local-action-docker-url +on: + schedule: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: ./actions/docker-url +` + + workflow, err = ReadWorkflow(strings.NewReader(yaml)) + assert.NoError(t, err, "read workflow should succeed") + + newSchedules = workflow.OnSchedule() + assert.Len(t, newSchedules, 0) + + yaml = ` +name: local-action-docker-url +on: [push, tag] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: ./actions/docker-url +` + + workflow, err = ReadWorkflow(strings.NewReader(yaml)) + assert.NoError(t, err, "read workflow should succeed") + + newSchedules = workflow.OnSchedule() + assert.Len(t, newSchedules, 0) +} + +func TestReadWorkflow_StringEvent(t *testing.T) { + yaml := ` +name: local-action-docker-url +on: push + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: ./actions/docker-url +` + + workflow, err := ReadWorkflow(strings.NewReader(yaml)) + assert.NoError(t, err, "read workflow should succeed") + + assert.Len(t, workflow.On(), 1) + assert.Contains(t, workflow.On(), "push") +} + +func TestReadWorkflow_ListEvent(t *testing.T) { + yaml := ` +name: local-action-docker-url +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: ./actions/docker-url +` + + workflow, err := ReadWorkflow(strings.NewReader(yaml)) + assert.NoError(t, err, "read workflow should succeed") + + assert.Len(t, workflow.On(), 2) + assert.Contains(t, workflow.On(), "push") + assert.Contains(t, workflow.On(), "pull_request") +} + +func TestReadWorkflow_MapEvent(t *testing.T) { + yaml := ` +name: local-action-docker-url +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: ./actions/docker-url +` + + workflow, err := ReadWorkflow(strings.NewReader(yaml)) + assert.NoError(t, err, "read workflow should succeed") + assert.Len(t, workflow.On(), 2) + assert.Contains(t, workflow.On(), "push") + assert.Contains(t, workflow.On(), "pull_request") +} + +func TestReadWorkflow_DecodeNodeError(t *testing.T) { + yaml := ` +on: + push: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - run: echo + env: + foo: {{ a }} +` + + workflow, err := ReadWorkflow(strings.NewReader(yaml)) + assert.NoError(t, err, "read workflow should succeed") + assert.Nil(t, workflow.GetJob("test").Steps[0].GetEnv()) +} + +func TestReadWorkflow_RunsOnLabels(t *testing.T) { + yaml := ` +name: local-action-docker-url + +jobs: + test: + container: nginx:latest + runs-on: + labels: ubuntu-latest + steps: + - uses: ./actions/docker-url` + + workflow, err := ReadWorkflow(strings.NewReader(yaml)) + assert.NoError(t, err, "read workflow should succeed") + assert.Equal(t, workflow.Jobs["test"].RunsOn(), []string{"ubuntu-latest"}) +} + +func TestReadWorkflow_RunsOnLabelsWithGroup(t *testing.T) { + yaml := ` +name: local-action-docker-url + +jobs: + test: + container: nginx:latest + runs-on: + labels: [ubuntu-latest] + group: linux + steps: + - uses: ./actions/docker-url` + + workflow, err := ReadWorkflow(strings.NewReader(yaml)) + assert.NoError(t, err, "read workflow should succeed") + assert.Equal(t, workflow.Jobs["test"].RunsOn(), []string{"ubuntu-latest", "linux"}) +} + +func TestReadWorkflow_StringContainer(t *testing.T) { + yaml := ` +name: local-action-docker-url + +jobs: + test: + container: nginx:latest + runs-on: ubuntu-latest + steps: + - uses: ./actions/docker-url + test2: + container: + image: nginx:latest + env: + foo: bar + runs-on: ubuntu-latest + steps: + - uses: ./actions/docker-url +` + + workflow, err := ReadWorkflow(strings.NewReader(yaml)) + assert.NoError(t, err, "read workflow should succeed") + assert.Len(t, workflow.Jobs, 2) + assert.Contains(t, workflow.Jobs["test"].Container().Image, "nginx:latest") + assert.Contains(t, workflow.Jobs["test2"].Container().Image, "nginx:latest") + assert.Contains(t, workflow.Jobs["test2"].Container().Env["foo"], "bar") +} + +func TestReadWorkflow_ObjectContainer(t *testing.T) { + yaml := ` +name: local-action-docker-url + +jobs: + test: + container: + image: r.example.org/something:latest + credentials: + username: registry-username + password: registry-password + env: + HOME: /home/user + volumes: + - my_docker_volume:/volume_mount + - /data/my_data + - /source/directory:/destination/directory + runs-on: ubuntu-latest + steps: + - uses: ./actions/docker-url +` + + workflow, err := ReadWorkflow(strings.NewReader(yaml)) + assert.NoError(t, err, "read workflow should succeed") + assert.Len(t, workflow.Jobs, 1) + + container := workflow.GetJob("test").Container() + + assert.Contains(t, container.Image, "r.example.org/something:latest") + assert.Contains(t, container.Env["HOME"], "/home/user") + assert.Contains(t, container.Credentials["username"], "registry-username") + assert.Contains(t, container.Credentials["password"], "registry-password") + assert.ElementsMatch(t, container.Volumes, []string{ + "my_docker_volume:/volume_mount", + "/data/my_data", + "/source/directory:/destination/directory", + }) +} + +func TestReadWorkflow_JobTypes(t *testing.T) { + yaml := ` +name: invalid job definition + +jobs: + default-job: + runs-on: ubuntu-latest + steps: + - run: echo + remote-reusable-workflow-yml: + uses: remote/repo/some/path/to/workflow.yml@main + remote-reusable-workflow-yaml: + uses: remote/repo/some/path/to/workflow.yaml@main + remote-reusable-workflow-custom-path: + uses: remote/repo/path/to/workflow.yml@main + local-reusable-workflow-yml: + uses: ./some/path/to/workflow.yml + local-reusable-workflow-yaml: + uses: ./some/path/to/workflow.yaml +` + + workflow, err := ReadWorkflow(strings.NewReader(yaml)) + assert.NoError(t, err, "read workflow should succeed") + assert.Len(t, workflow.Jobs, 6) + + jobType, err := workflow.Jobs["default-job"].Type() + assert.Equal(t, nil, err) + assert.Equal(t, JobTypeDefault, jobType) + + jobType, err = workflow.Jobs["remote-reusable-workflow-yml"].Type() + assert.Equal(t, nil, err) + assert.Equal(t, JobTypeReusableWorkflowRemote, jobType) + + jobType, err = workflow.Jobs["remote-reusable-workflow-yaml"].Type() + assert.Equal(t, nil, err) + assert.Equal(t, JobTypeReusableWorkflowRemote, jobType) + + jobType, err = workflow.Jobs["remote-reusable-workflow-custom-path"].Type() + assert.Equal(t, nil, err) + assert.Equal(t, JobTypeReusableWorkflowRemote, jobType) + + jobType, err = workflow.Jobs["local-reusable-workflow-yml"].Type() + assert.Equal(t, nil, err) + assert.Equal(t, JobTypeReusableWorkflowLocal, jobType) + + jobType, err = workflow.Jobs["local-reusable-workflow-yaml"].Type() + assert.Equal(t, nil, err) + assert.Equal(t, JobTypeReusableWorkflowLocal, jobType) +} + +func TestReadWorkflow_JobTypes_InvalidPath(t *testing.T) { + yaml := ` +name: invalid job definition + +jobs: + remote-reusable-workflow-missing-version: + uses: remote/repo/some/path/to/workflow.yml + remote-reusable-workflow-bad-extension: + uses: remote/repo/some/path/to/workflow.json + local-reusable-workflow-bad-extension: + uses: ./some/path/to/workflow.json + local-reusable-workflow-bad-path: + uses: some/path/to/workflow.yaml +` + + workflow, err := ReadWorkflow(strings.NewReader(yaml)) + assert.NoError(t, err, "read workflow should succeed") + assert.Len(t, workflow.Jobs, 4) + + jobType, err := workflow.Jobs["remote-reusable-workflow-missing-version"].Type() + assert.Equal(t, JobTypeInvalid, jobType) + assert.NotEqual(t, nil, err) + + jobType, err = workflow.Jobs["remote-reusable-workflow-bad-extension"].Type() + assert.Equal(t, JobTypeInvalid, jobType) + assert.NotEqual(t, nil, err) + + jobType, err = workflow.Jobs["local-reusable-workflow-bad-extension"].Type() + assert.Equal(t, JobTypeInvalid, jobType) + assert.NotEqual(t, nil, err) + + jobType, err = workflow.Jobs["local-reusable-workflow-bad-path"].Type() + assert.Equal(t, JobTypeInvalid, jobType) + assert.NotEqual(t, nil, err) +} + +func TestReadWorkflow_StepsTypes(t *testing.T) { + yaml := ` +name: invalid step definition + +jobs: + test: + runs-on: ubuntu-latest + steps: + - name: test1 + uses: actions/checkout@v2 + run: echo + - name: test2 + run: echo + - name: test3 + uses: actions/checkout@v2 + - name: test4 + uses: docker://nginx:latest + - name: test5 + uses: ./local-action +` + + workflow, err := ReadWorkflow(strings.NewReader(yaml)) + assert.NoError(t, err, "read workflow should succeed") + assert.Len(t, workflow.Jobs, 1) + assert.Len(t, workflow.Jobs["test"].Steps, 5) + assert.Equal(t, workflow.Jobs["test"].Steps[0].Type(), StepTypeInvalid) + assert.Equal(t, workflow.Jobs["test"].Steps[1].Type(), StepTypeRun) + assert.Equal(t, workflow.Jobs["test"].Steps[2].Type(), StepTypeUsesActionRemote) + assert.Equal(t, workflow.Jobs["test"].Steps[3].Type(), StepTypeUsesDockerURL) + assert.Equal(t, workflow.Jobs["test"].Steps[4].Type(), StepTypeUsesActionLocal) +} + +// See: https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idoutputs +func TestReadWorkflow_JobOutputs(t *testing.T) { + yaml := ` +name: job outputs definition + +jobs: + test1: + runs-on: ubuntu-latest + steps: + - id: test1_1 + run: | + echo "::set-output name=a_key::some-a_value" + echo "::set-output name=b-key::some-b-value" + outputs: + some_a_key: ${{ steps.test1_1.outputs.a_key }} + some-b-key: ${{ steps.test1_1.outputs.b-key }} + + test2: + runs-on: ubuntu-latest + needs: + - test1 + steps: + - name: test2_1 + run: | + echo "${{ needs.test1.outputs.some_a_key }}" + echo "${{ needs.test1.outputs.some-b-key }}" +` + + workflow, err := ReadWorkflow(strings.NewReader(yaml)) + assert.NoError(t, err, "read workflow should succeed") + assert.Len(t, workflow.Jobs, 2) + + assert.Len(t, workflow.Jobs["test1"].Steps, 1) + assert.Equal(t, StepTypeRun, workflow.Jobs["test1"].Steps[0].Type()) + assert.Equal(t, "test1_1", workflow.Jobs["test1"].Steps[0].ID) + assert.Len(t, workflow.Jobs["test1"].Outputs, 2) + assert.Contains(t, workflow.Jobs["test1"].Outputs, "some_a_key") + assert.Contains(t, workflow.Jobs["test1"].Outputs, "some-b-key") + assert.Equal(t, "${{ steps.test1_1.outputs.a_key }}", workflow.Jobs["test1"].Outputs["some_a_key"]) + assert.Equal(t, "${{ steps.test1_1.outputs.b-key }}", workflow.Jobs["test1"].Outputs["some-b-key"]) +} + +func TestReadWorkflow_Strategy(t *testing.T) { + w, err := NewWorkflowPlanner("testdata/strategy/push.yml", true) + assert.NoError(t, err) + + p, err := w.PlanJob("strategy-only-max-parallel") + assert.NoError(t, err) + + assert.Equal(t, len(p.Stages), 1) + assert.Equal(t, len(p.Stages[0].Runs), 1) + + wf := p.Stages[0].Runs[0].Workflow + + job := wf.Jobs["strategy-only-max-parallel"] + matrixes, err := job.GetMatrixes() + assert.NoError(t, err) + assert.Equal(t, matrixes, []map[string]interface{}{{}}) + assert.Equal(t, job.Matrix(), map[string][]interface{}(nil)) + assert.Equal(t, job.Strategy.MaxParallel, 2) + assert.Equal(t, job.Strategy.FailFast, true) + + job = wf.Jobs["strategy-only-fail-fast"] + matrixes, err = job.GetMatrixes() + assert.NoError(t, err) + assert.Equal(t, matrixes, []map[string]interface{}{{}}) + assert.Equal(t, job.Matrix(), map[string][]interface{}(nil)) + assert.Equal(t, job.Strategy.MaxParallel, 4) + assert.Equal(t, job.Strategy.FailFast, false) + + job = wf.Jobs["strategy-no-matrix"] + matrixes, err = job.GetMatrixes() + assert.NoError(t, err) + assert.Equal(t, matrixes, []map[string]interface{}{{}}) + assert.Equal(t, job.Matrix(), map[string][]interface{}(nil)) + assert.Equal(t, job.Strategy.MaxParallel, 2) + assert.Equal(t, job.Strategy.FailFast, false) + + job = wf.Jobs["strategy-all"] + matrixes, err = job.GetMatrixes() + assert.NoError(t, err) + assert.Equal(t, matrixes, + []map[string]interface{}{ + {"datacenter": "site-c", "node-version": "14.x", "site": "staging"}, + {"datacenter": "site-c", "node-version": "16.x", "site": "staging"}, + {"datacenter": "site-d", "node-version": "16.x", "site": "staging"}, + {"php-version": 5.4}, + {"datacenter": "site-a", "node-version": "10.x", "site": "prod"}, + {"datacenter": "site-b", "node-version": "12.x", "site": "dev"}, + }, + ) + assert.Equal(t, job.Matrix(), + map[string][]interface{}{ + "datacenter": {"site-c", "site-d"}, + "exclude": { + map[string]interface{}{"datacenter": "site-d", "node-version": "14.x", "site": "staging"}, + }, + "include": { + map[string]interface{}{"php-version": 5.4}, + map[string]interface{}{"datacenter": "site-a", "node-version": "10.x", "site": "prod"}, + map[string]interface{}{"datacenter": "site-b", "node-version": "12.x", "site": "dev"}, + }, + "node-version": {"14.x", "16.x"}, + "site": {"staging"}, + }, + ) + assert.Equal(t, job.Strategy.MaxParallel, 2) + assert.Equal(t, job.Strategy.FailFast, false) +} + +func TestStep_ShellCommand(t *testing.T) { + tests := []struct { + shell string + want string + }{ + {"pwsh -v '. {0}'", "pwsh -v '. {0}'"}, + {"pwsh", "pwsh -command . '{0}'"}, + {"powershell", "powershell -command . '{0}'"}, + } + for _, tt := range tests { + t.Run(tt.shell, func(t *testing.T) { + got := (&Step{Shell: tt.shell}).ShellCommand() + assert.Equal(t, got, tt.want) + }) + } +} + +func TestReadWorkflow_WorkflowDispatchConfig(t *testing.T) { + yaml := ` + name: local-action-docker-url + ` + workflow, err := ReadWorkflow(strings.NewReader(yaml)) + assert.NoError(t, err, "read workflow should succeed") + workflowDispatch := workflow.WorkflowDispatchConfig() + assert.Nil(t, workflowDispatch) + + yaml = ` + name: local-action-docker-url + on: push + ` + workflow, err = ReadWorkflow(strings.NewReader(yaml)) + assert.NoError(t, err, "read workflow should succeed") + workflowDispatch = workflow.WorkflowDispatchConfig() + assert.Nil(t, workflowDispatch) + + yaml = ` + name: local-action-docker-url + on: workflow_dispatch + ` + workflow, err = ReadWorkflow(strings.NewReader(yaml)) + assert.NoError(t, err, "read workflow should succeed") + workflowDispatch = workflow.WorkflowDispatchConfig() + assert.NotNil(t, workflowDispatch) + assert.Nil(t, workflowDispatch.Inputs) + + yaml = ` + name: local-action-docker-url + on: [push, pull_request] + ` + workflow, err = ReadWorkflow(strings.NewReader(yaml)) + assert.NoError(t, err, "read workflow should succeed") + workflowDispatch = workflow.WorkflowDispatchConfig() + assert.Nil(t, workflowDispatch) + + yaml = ` + name: local-action-docker-url + on: [push, workflow_dispatch] + ` + workflow, err = ReadWorkflow(strings.NewReader(yaml)) + assert.NoError(t, err, "read workflow should succeed") + workflowDispatch = workflow.WorkflowDispatchConfig() + assert.NotNil(t, workflowDispatch) + assert.Nil(t, workflowDispatch.Inputs) + + yaml = ` + name: local-action-docker-url + on: + - push + - workflow_dispatch + ` + workflow, err = ReadWorkflow(strings.NewReader(yaml)) + assert.NoError(t, err, "read workflow should succeed") + workflowDispatch = workflow.WorkflowDispatchConfig() + assert.NotNil(t, workflowDispatch) + assert.Nil(t, workflowDispatch.Inputs) + + yaml = ` + name: local-action-docker-url + on: + push: + pull_request: + ` + workflow, err = ReadWorkflow(strings.NewReader(yaml)) + assert.NoError(t, err, "read workflow should succeed") + workflowDispatch = workflow.WorkflowDispatchConfig() + assert.Nil(t, workflowDispatch) + + yaml = ` + name: local-action-docker-url + on: + push: + pull_request: + workflow_dispatch: + inputs: + logLevel: + description: 'Log level' + required: true + default: 'warning' + type: choice + options: + - info + - warning + - debug + ` + workflow, err = ReadWorkflow(strings.NewReader(yaml)) + assert.NoError(t, err, "read workflow should succeed") + workflowDispatch = workflow.WorkflowDispatchConfig() + assert.NotNil(t, workflowDispatch) + assert.Equal(t, WorkflowDispatchInput{ + Default: "warning", + Description: "Log level", + Options: []string{ + "info", + "warning", + "debug", + }, + Required: true, + Type: "choice", + }, workflowDispatch.Inputs["logLevel"]) +} |