1
0
Fork 0
mirror of https://code.forgejo.org/forgejo/runner.git synced 2025-08-16 18:01:34 +00:00
forgejo-runner/act/runner/job_executor_test.go
Earl Warren 8b7e126c1c
fix: log the URL of the action when it fails schema validation (#810)
```
$ go test -v -run='TestRunner_RunEvent$/local-action-fails-schema-validation' ./act/runner
...
[fails-schema-validation/test]     Failure - Main ./local-action-fails-schema-validation/action
[fails-schema-validation/test] failed to validate action.y*ml from action './local-action-fails-schema-validation/action' with path '': Line: 2 Column 3: Failed to match null: Line: 2 Column 3: Expected a scalar got mapping
Line: 2 Column 3: Failed to match inputs-mapping: Line: 3 Column 5: Failed to match null: Line: 3 Column 5: Expected a scalar got mapping
Line: 3 Column 5: Failed to match input-mapping: Line: 3 Column 14: Unknown Variable Access secrets
...
```

<!--start release-notes-assistant-->
<!--URL:https://code.forgejo.org/forgejo/runner-->
- bug fixes
  - [PR](https://code.forgejo.org/forgejo/runner/pulls/810): <!--number 810 --><!--line 0 --><!--description Zml4OiBsb2cgdGhlIFVSTCBvZiB0aGUgYWN0aW9uIHdoZW4gaXQgZmFpbHMgc2NoZW1hIHZhbGlkYXRpb24=-->fix: log the URL of the action when it fails schema validation<!--description-->
<!--end release-notes-assistant-->

Reviewed-on: https://code.forgejo.org/forgejo/runner/pulls/810
Reviewed-by: Michael Kriese <michael.kriese@gmx.de>
Co-authored-by: Earl Warren <contact@earl-warren.org>
Co-committed-by: Earl Warren <contact@earl-warren.org>
2025-08-04 13:20:22 +00:00

341 lines
7.7 KiB
Go

package runner
import (
"context"
"fmt"
"io"
"testing"
"code.forgejo.org/forgejo/runner/v9/act/common"
"code.forgejo.org/forgejo/runner/v9/act/container"
"code.forgejo.org/forgejo/runner/v9/act/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
func TestJobExecutor(t *testing.T) {
tables := []TestJobFileInfo{
{workdir, "uses-and-run-in-one-step", "push", "Invalid run/uses syntax for job:test step:Test", platforms, secrets},
{workdir, "uses-github-empty", "push", "job:test step:empty", platforms, secrets},
{workdir, "uses-github-noref", "push", "Expected format {org}/{repo}[/path]@ref", platforms, secrets},
{workdir, "uses-github-root", "push", "", platforms, secrets},
{workdir, "uses-github-path", "push", "", platforms, secrets},
{workdir, "uses-docker-url", "push", "", platforms, secrets},
{workdir, "uses-github-full-sha", "push", "", platforms, secrets},
{workdir, "uses-github-short-sha", "push", "Please use the full commit SHA", platforms, secrets},
{workdir, "job-nil-step", "push", "invalid Step 0: missing run or uses key", platforms, secrets},
}
// These tests are sufficient to only check syntax.
ctx := common.WithDryrun(context.Background(), true)
for _, table := range tables {
t.Run(table.workflowPath, func(t *testing.T) {
table.runTest(ctx, t, &Config{})
})
}
}
type jobInfoMock struct {
mock.Mock
}
func (jim *jobInfoMock) matrix() map[string]interface{} {
args := jim.Called()
return args.Get(0).(map[string]interface{})
}
func (jim *jobInfoMock) steps() []*model.Step {
args := jim.Called()
return args.Get(0).([]*model.Step)
}
func (jim *jobInfoMock) startContainer() common.Executor {
args := jim.Called()
return args.Get(0).(func(context.Context) error)
}
func (jim *jobInfoMock) stopContainer() common.Executor {
args := jim.Called()
return args.Get(0).(func(context.Context) error)
}
func (jim *jobInfoMock) closeContainer() common.Executor {
args := jim.Called()
return args.Get(0).(func(context.Context) error)
}
func (jim *jobInfoMock) interpolateOutputs() common.Executor {
args := jim.Called()
return args.Get(0).(func(context.Context) error)
}
func (jim *jobInfoMock) result(result string) {
jim.Called(result)
}
type jobContainerMock struct {
container.Container
container.LinuxContainerEnvironmentExtensions
}
func (jcm *jobContainerMock) ReplaceLogWriter(_, _ io.Writer) (io.Writer, io.Writer) {
return nil, nil
}
type stepFactoryMock struct {
mock.Mock
}
func (sfm *stepFactoryMock) newStep(model *model.Step, rc *RunContext) (step, error) {
args := sfm.Called(model, rc)
return args.Get(0).(step), args.Error(1)
}
func TestJobExecutorNewJobExecutor(t *testing.T) {
table := []struct {
name string
steps []*model.Step
preSteps []bool
postSteps []bool
executedSteps []string
result string
hasError bool
}{
{
name: "zeroSteps",
steps: []*model.Step{},
preSteps: []bool{},
postSteps: []bool{},
executedSteps: []string{},
result: "success",
hasError: false,
},
{
name: "stepWithoutPrePost",
steps: []*model.Step{{
ID: "1",
}},
preSteps: []bool{false},
postSteps: []bool{false},
executedSteps: []string{
"startContainer",
"step1",
"stopContainer",
"interpolateOutputs",
"closeContainer",
},
result: "success",
hasError: false,
},
{
name: "stepWithFailure",
steps: []*model.Step{{
ID: "1",
}},
preSteps: []bool{false},
postSteps: []bool{false},
executedSteps: []string{
"startContainer",
"step1",
"interpolateOutputs",
"closeContainer",
},
result: "failure",
hasError: true,
},
{
name: "stepWithPre",
steps: []*model.Step{{
ID: "1",
}},
preSteps: []bool{true},
postSteps: []bool{false},
executedSteps: []string{
"startContainer",
"pre1",
"step1",
"stopContainer",
"interpolateOutputs",
"closeContainer",
},
result: "success",
hasError: false,
},
{
name: "stepWithPost",
steps: []*model.Step{{
ID: "1",
}},
preSteps: []bool{false},
postSteps: []bool{true},
executedSteps: []string{
"startContainer",
"step1",
"post1",
"stopContainer",
"interpolateOutputs",
"closeContainer",
},
result: "success",
hasError: false,
},
{
name: "stepWithPreAndPost",
steps: []*model.Step{{
ID: "1",
}},
preSteps: []bool{true},
postSteps: []bool{true},
executedSteps: []string{
"startContainer",
"pre1",
"step1",
"post1",
"stopContainer",
"interpolateOutputs",
"closeContainer",
},
result: "success",
hasError: false,
},
{
name: "stepsWithPreAndPost",
steps: []*model.Step{{
ID: "1",
}, {
ID: "2",
}, {
ID: "3",
}},
preSteps: []bool{true, false, true},
postSteps: []bool{false, true, true},
executedSteps: []string{
"startContainer",
"pre1",
"pre3",
"step1",
"step2",
"step3",
"post3",
"post2",
"stopContainer",
"interpolateOutputs",
"closeContainer",
},
result: "success",
hasError: false,
},
}
contains := func(needle string, haystack []string) bool {
for _, item := range haystack {
if item == needle {
return true
}
}
return false
}
for _, tt := range table {
t.Run(tt.name, func(t *testing.T) {
fmt.Printf("::group::%s\n", tt.name)
ctx := common.WithJobErrorContainer(context.Background())
jim := &jobInfoMock{}
sfm := &stepFactoryMock{}
rc := &RunContext{
JobContainer: &jobContainerMock{},
Run: &model.Run{
JobID: "test",
Workflow: &model.Workflow{
Jobs: map[string]*model.Job{
"test": {},
},
},
},
Config: &Config{},
}
rc.ExprEval = rc.NewExpressionEvaluator(ctx)
executorOrder := make([]string, 0)
jim.On("steps").Return(tt.steps)
if len(tt.steps) > 0 {
jim.On("startContainer").Return(func(ctx context.Context) error {
executorOrder = append(executorOrder, "startContainer")
return nil
})
}
for i, stepModel := range tt.steps {
i := i
stepModel := stepModel
sm := &stepMock{}
sfm.On("newStep", stepModel, rc).Return(sm, nil)
sm.On("pre").Return(func(ctx context.Context) error {
if tt.preSteps[i] {
executorOrder = append(executorOrder, "pre"+stepModel.ID)
}
return nil
})
sm.On("main").Return(func(ctx context.Context) error {
executorOrder = append(executorOrder, "step"+stepModel.ID)
if tt.hasError {
return fmt.Errorf("error")
}
return nil
})
sm.On("post").Return(func(ctx context.Context) error {
if tt.postSteps[i] {
executorOrder = append(executorOrder, "post"+stepModel.ID)
}
return nil
})
defer sm.AssertExpectations(t)
}
if len(tt.steps) > 0 {
jim.On("matrix").Return(map[string]interface{}{})
jim.On("interpolateOutputs").Return(func(ctx context.Context) error {
executorOrder = append(executorOrder, "interpolateOutputs")
return nil
})
if contains("stopContainer", tt.executedSteps) {
jim.On("stopContainer").Return(func(ctx context.Context) error {
executorOrder = append(executorOrder, "stopContainer")
return nil
})
}
jim.On("result", tt.result)
jim.On("closeContainer").Return(func(ctx context.Context) error {
executorOrder = append(executorOrder, "closeContainer")
return nil
})
}
executor := newJobExecutor(jim, sfm, rc)
err := executor(ctx)
assert.Nil(t, err)
assert.Equal(t, tt.executedSteps, executorOrder)
jim.AssertExpectations(t)
sfm.AssertExpectations(t)
fmt.Println("::endgroup::")
})
}
}