mirror of
				https://codeberg.org/forgejo/forgejo.git
				synced 2025-10-31 14:31:02 +00:00 
			
		
		
		
	Fix #27906 According to GitHub's [documentation](https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idneeds), a job should always run when its `if` is `always()` > If you would like a job to run even if a job it is dependent on did not succeed, use the `always()` conditional expression in `jobs.<job_id>.if`. --------- Co-authored-by: Giteabot <teabot@gitea.io> (cherry picked from commit d0fe6ea4e101198911383058a2e121e384934a9c)
		
			
				
	
	
		
			136 lines
		
	
	
	
		
			4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			136 lines
		
	
	
	
		
			4 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| // Copyright 2022 The Gitea Authors. All rights reserved.
 | |
| // SPDX-License-Identifier: MIT
 | |
| 
 | |
| package actions
 | |
| 
 | |
| import (
 | |
| 	"testing"
 | |
| 
 | |
| 	actions_model "code.gitea.io/gitea/models/actions"
 | |
| 
 | |
| 	"github.com/stretchr/testify/assert"
 | |
| )
 | |
| 
 | |
| func Test_jobStatusResolver_Resolve(t *testing.T) {
 | |
| 	tests := []struct {
 | |
| 		name string
 | |
| 		jobs actions_model.ActionJobList
 | |
| 		want map[int64]actions_model.Status
 | |
| 	}{
 | |
| 		{
 | |
| 			name: "no blocked",
 | |
| 			jobs: actions_model.ActionJobList{
 | |
| 				{ID: 1, JobID: "1", Status: actions_model.StatusWaiting, Needs: []string{}},
 | |
| 				{ID: 2, JobID: "2", Status: actions_model.StatusWaiting, Needs: []string{}},
 | |
| 				{ID: 3, JobID: "3", Status: actions_model.StatusWaiting, Needs: []string{}},
 | |
| 			},
 | |
| 			want: map[int64]actions_model.Status{},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "single blocked",
 | |
| 			jobs: actions_model.ActionJobList{
 | |
| 				{ID: 1, JobID: "1", Status: actions_model.StatusSuccess, Needs: []string{}},
 | |
| 				{ID: 2, JobID: "2", Status: actions_model.StatusBlocked, Needs: []string{"1"}},
 | |
| 				{ID: 3, JobID: "3", Status: actions_model.StatusWaiting, Needs: []string{}},
 | |
| 			},
 | |
| 			want: map[int64]actions_model.Status{
 | |
| 				2: actions_model.StatusWaiting,
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "multiple blocked",
 | |
| 			jobs: actions_model.ActionJobList{
 | |
| 				{ID: 1, JobID: "1", Status: actions_model.StatusSuccess, Needs: []string{}},
 | |
| 				{ID: 2, JobID: "2", Status: actions_model.StatusBlocked, Needs: []string{"1"}},
 | |
| 				{ID: 3, JobID: "3", Status: actions_model.StatusBlocked, Needs: []string{"1"}},
 | |
| 			},
 | |
| 			want: map[int64]actions_model.Status{
 | |
| 				2: actions_model.StatusWaiting,
 | |
| 				3: actions_model.StatusWaiting,
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "chain blocked",
 | |
| 			jobs: actions_model.ActionJobList{
 | |
| 				{ID: 1, JobID: "1", Status: actions_model.StatusFailure, Needs: []string{}},
 | |
| 				{ID: 2, JobID: "2", Status: actions_model.StatusBlocked, Needs: []string{"1"}},
 | |
| 				{ID: 3, JobID: "3", Status: actions_model.StatusBlocked, Needs: []string{"2"}},
 | |
| 			},
 | |
| 			want: map[int64]actions_model.Status{
 | |
| 				2: actions_model.StatusSkipped,
 | |
| 				3: actions_model.StatusSkipped,
 | |
| 			},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "loop need",
 | |
| 			jobs: actions_model.ActionJobList{
 | |
| 				{ID: 1, JobID: "1", Status: actions_model.StatusBlocked, Needs: []string{"3"}},
 | |
| 				{ID: 2, JobID: "2", Status: actions_model.StatusBlocked, Needs: []string{"1"}},
 | |
| 				{ID: 3, JobID: "3", Status: actions_model.StatusBlocked, Needs: []string{"2"}},
 | |
| 			},
 | |
| 			want: map[int64]actions_model.Status{},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "with ${{ always() }} condition",
 | |
| 			jobs: actions_model.ActionJobList{
 | |
| 				{ID: 1, JobID: "job1", Status: actions_model.StatusFailure, Needs: []string{}},
 | |
| 				{ID: 2, JobID: "job2", Status: actions_model.StatusBlocked, Needs: []string{"job1"}, WorkflowPayload: []byte(
 | |
| 					`
 | |
| name: test
 | |
| on: push
 | |
| jobs:
 | |
|   job2:
 | |
|     runs-on: ubuntu-latest
 | |
|     needs: job1
 | |
|     if: ${{ always() }}
 | |
|     steps:
 | |
|       - run: echo "always run"
 | |
| `)},
 | |
| 			},
 | |
| 			want: map[int64]actions_model.Status{2: actions_model.StatusWaiting},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "with always() condition",
 | |
| 			jobs: actions_model.ActionJobList{
 | |
| 				{ID: 1, JobID: "job1", Status: actions_model.StatusFailure, Needs: []string{}},
 | |
| 				{ID: 2, JobID: "job2", Status: actions_model.StatusBlocked, Needs: []string{"job1"}, WorkflowPayload: []byte(
 | |
| 					`
 | |
| name: test
 | |
| on: push
 | |
| jobs:
 | |
|   job2:
 | |
|     runs-on: ubuntu-latest
 | |
|     needs: job1
 | |
|     if: always()
 | |
|     steps:
 | |
|       - run: echo "always run"
 | |
| `)},
 | |
| 			},
 | |
| 			want: map[int64]actions_model.Status{2: actions_model.StatusWaiting},
 | |
| 		},
 | |
| 		{
 | |
| 			name: "without always() condition",
 | |
| 			jobs: actions_model.ActionJobList{
 | |
| 				{ID: 1, JobID: "job1", Status: actions_model.StatusFailure, Needs: []string{}},
 | |
| 				{ID: 2, JobID: "job2", Status: actions_model.StatusBlocked, Needs: []string{"job1"}, WorkflowPayload: []byte(
 | |
| 					`
 | |
| name: test
 | |
| on: push
 | |
| jobs:
 | |
|   job2:
 | |
|     runs-on: ubuntu-latest
 | |
|     needs: job1
 | |
|     steps:
 | |
|       - run: echo "not always run"
 | |
| `)},
 | |
| 			},
 | |
| 			want: map[int64]actions_model.Status{2: actions_model.StatusSkipped},
 | |
| 		},
 | |
| 	}
 | |
| 	for _, tt := range tests {
 | |
| 		t.Run(tt.name, func(t *testing.T) {
 | |
| 			r := newJobStatusResolver(tt.jobs)
 | |
| 			assert.Equal(t, tt.want, r.Resolve())
 | |
| 		})
 | |
| 	}
 | |
| }
 |