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  }