Files
act_runner/act/runner/action_test.go
Lunny Xiao 13dc9386fe Rename act_runner to runner (#850)
## Consumer-facing breaking changes

- **Go module path**: `gitea.com/gitea/act_runner` → `gitea.com/gitea/runner`. Anything importing `act/...` or `internal/...` packages (notably Gitea itself) must update imports.
- **Binary name**: `act_runner` → `gitea-runner`. Wrapper scripts, systemd units, init scripts, and documentation referencing the binary by `act_runner` will break.
- **Docker image**: `gitea/act_runner` → `gitea/runner` (incl. `*-dind-rootless` variants). Users pulling `gitea/act_runner:nightly` etc. will get stale images. Note: the image name is `gitea/runner`, not `gitea/gitea-runner`.
- **Release artifact paths**: S3 directory `act_runner/{{.Version}}` → `gitea-runner/{{.Version}}`, and artifact filenames change with the new project name. Existing download URLs break.
- **Metrics namespace**: changed from `act_runner` to `gitea_runner` (e.g. `act_runner_jobs_total` → `gitea_runner_jobs_total`); existing monitors/dashboards must be updated.
- **ldflags version path**: `gitea.com/gitea/act_runner/internal/pkg/ver.version` → `gitea.com/gitea/runner/internal/pkg/ver.version`. Affects anyone building with custom ldflags.
- **Kubernetes example resource names**: `act-runner` / `act-runner-vol` → `runner` / `runner-vol`. Users who copied the manifests verbatim will see resource churn on apply.
- **s6 service name**: `scripts/s6/act_runner/` → `scripts/s6/gitea-runner/` (image-internal; only matters for downstream image overrides).

Unchanged: YAML config field names, env vars (`GITEA_*`), CLI flags/subcommands, registration file format.
---------

Co-authored-by: silverwind <me@silverwind.io>
Reviewed-on: https://gitea.com/gitea/runner/pulls/850
Reviewed-by: Zettat123 <39446+zettat123@noreply.gitea.com>
Reviewed-by: silverwind <2021+silverwind@noreply.gitea.com>
Reviewed-by: Nicolas <bircni@icloud.com>
Co-authored-by: Lunny Xiao <xiaolunwen@gmail.com>
Co-committed-by: Lunny Xiao <xiaolunwen@gmail.com>
2026-04-30 20:12:51 +00:00

255 lines
5.4 KiB
Go

// Copyright 2022 The Gitea Authors. All rights reserved.
// Copyright 2022 The nektos/act Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package runner
import (
"context"
"io"
"io/fs"
"strings"
"testing"
"gitea.com/gitea/runner/act/model"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
type closerMock struct {
mock.Mock
}
func (m *closerMock) Close() error {
m.Called()
return nil
}
func TestActionReader(t *testing.T) {
yaml := strings.ReplaceAll(`
name: 'name'
runs:
using: 'node16'
main: 'main.js'
`, "\t", " ")
table := []struct {
name string
step *model.Step
filename string
fileContent string
expected *model.Action
}{
{
name: "readActionYml",
step: &model.Step{},
filename: "action.yml",
fileContent: yaml,
expected: &model.Action{
Name: "name",
Runs: model.ActionRuns{
Using: "node16",
Main: "main.js",
PreIf: "always()",
PostIf: "always()",
},
},
},
{
name: "readActionYaml",
step: &model.Step{},
filename: "action.yaml",
fileContent: yaml,
expected: &model.Action{
Name: "name",
Runs: model.ActionRuns{
Using: "node16",
Main: "main.js",
PreIf: "always()",
PostIf: "always()",
},
},
},
{
name: "readDockerfile",
step: &model.Step{},
filename: "Dockerfile",
fileContent: "FROM ubuntu:20.04",
expected: &model.Action{
Name: "(Synthetic)",
Runs: model.ActionRuns{
Using: "docker",
Image: "Dockerfile",
},
},
},
{
name: "readWithArgs",
step: &model.Step{
With: map[string]string{
"args": "cmd",
},
},
expected: &model.Action{
Name: "(Synthetic)",
Inputs: map[string]model.Input{
"cwd": {
Description: "(Actual working directory)",
Required: false,
Default: "actionDir/actionPath",
},
"command": {
Description: "(Actual program)",
Required: false,
Default: "cmd",
},
},
Runs: model.ActionRuns{
Using: "node12",
Main: "trampoline.js",
},
},
},
}
for _, tt := range table {
t.Run(tt.name, func(t *testing.T) {
closerMock := &closerMock{}
readFile := func(filename string) (io.Reader, io.Closer, error) {
if tt.filename != filename {
return nil, nil, fs.ErrNotExist
}
return strings.NewReader(tt.fileContent), closerMock, nil
}
writeFile := func(filename string, data []byte, perm fs.FileMode) error {
assert.Equal(t, "actionDir/actionPath/trampoline.js", filename)
assert.Equal(t, fs.FileMode(0o400), perm)
return nil
}
if tt.filename != "" {
closerMock.On("Close")
}
action, err := readActionImpl(context.Background(), tt.step, "actionDir", "actionPath", readFile, writeFile)
assert.NoError(t, err) //nolint:testifylint // pre-existing issue from nektos/act
assert.Equal(t, tt.expected, action)
closerMock.AssertExpectations(t)
})
}
}
func TestActionRunner(t *testing.T) {
table := []struct {
name string
step actionStep
expectedEnv map[string]string
}{
{
name: "with-input",
step: &stepActionRemote{
Step: &model.Step{
Uses: "org/repo/path@ref",
},
RunContext: &RunContext{
Config: &Config{},
Run: &model.Run{
JobID: "job",
Workflow: &model.Workflow{
Jobs: map[string]*model.Job{
"job": {
Name: "job",
},
},
},
},
},
action: &model.Action{
Inputs: map[string]model.Input{
"key": {
Default: "default value",
},
},
Runs: model.ActionRuns{
Using: "node16",
},
},
env: map[string]string{},
},
expectedEnv: map[string]string{"INPUT_KEY": "default value"},
},
{
name: "restore-saved-state",
step: &stepActionRemote{
Step: &model.Step{
ID: "step",
Uses: "org/repo/path@ref",
},
RunContext: &RunContext{
ActionPath: "path",
Config: &Config{},
Run: &model.Run{
JobID: "job",
Workflow: &model.Workflow{
Jobs: map[string]*model.Job{
"job": {
Name: "job",
},
},
},
},
CurrentStep: "post-step",
StepResults: map[string]*model.StepResult{
"step": {},
},
IntraActionState: map[string]map[string]string{
"step": {
"name": "state value",
},
},
},
action: &model.Action{
Runs: model.ActionRuns{
Using: "node16",
},
},
env: map[string]string{},
},
expectedEnv: map[string]string{"STATE_name": "state value"},
},
}
for _, tt := range table {
t.Run(tt.name, func(t *testing.T) {
ctx := context.Background()
cm := &containerMock{}
cm.On("CopyDir", "/var/run/act/actions/dir/", "dir/", false).Return(func(ctx context.Context) error { return nil })
envMatcher := mock.MatchedBy(func(env map[string]string) bool {
for k, v := range tt.expectedEnv {
if env[k] != v {
return false
}
}
return true
})
cm.On("Exec", []string{"node", "/var/run/act/actions/dir/path"}, envMatcher, "", "").Return(func(ctx context.Context) error { return nil })
tt.step.getRunContext().JobContainer = cm
err := runActionImpl(tt.step, "dir", newRemoteAction("org/repo/path@ref"))(ctx)
assert.NoError(t, err) //nolint:testifylint // pre-existing issue from nektos/act
cm.AssertExpectations(t)
})
}
}