// Copyright 2026 The Gitea Authors. All rights reserved. // Copyright 2026 The nektos/act Authors. All rights reserved. // SPDX-License-Identifier: MIT package runner import ( "bytes" "io" "strings" "testing" "gitea.com/gitea/runner/act/common" "gitea.com/gitea/runner/act/common/logging" "gitea.dev/actionslib/pkg/model" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" yaml "go.yaml.in/yaml/v4" ) func TestRunScriptGroupTitle(t *testing.T) { sr := &stepRun{Step: &model.Step{Name: "Build"}} assert.Equal(t, "make build", sr.runScriptGroupTitle("make build")) assert.Equal(t, "echo one", sr.runScriptGroupTitle(" \techo one\necho two")) assert.Equal(t, "Build", sr.runScriptGroupTitle("")) sr = &stepRun{Step: &model.Step{ID: "s1"}} assert.Equal(t, "s1", sr.runScriptGroupTitle("\n \n")) } func TestStepDeclaredEnvOrderPreservesYAML(t *testing.T) { raw := `id: s1 run: "echo 1" env: GITHUB_TOKEN: tok PATH: /custom/bin MY_VAR: hello ` var step model.Step require.NoError(t, yaml.Unmarshal([]byte(raw), &step)) assert.Equal(t, []string{"GITHUB_TOKEN", "PATH", "MY_VAR"}, stepDeclaredEnvKeysInOrder(&step)) } func TestStepDeclaredEnvKeysInOrderEmpty(t *testing.T) { assert.Nil(t, stepDeclaredEnvKeysInOrder(nil)) assert.Empty(t, stepDeclaredEnvKeysInOrder(&model.Step{})) } func TestStepDeclaredEnvKeysIgnoreYAMLMergeKey(t *testing.T) { doc := ` common: &common COMMON_A: a COMMON_B: b step: env: LOCAL_BEFORE: before <<: *common COMMON_B: overridden LOCAL_AFTER: after ` var root struct { Step model.Step `yaml:"step"` } require.NoError(t, yaml.Unmarshal([]byte(doc), &root)) keys := stepDeclaredEnvKeysInOrder(&root.Step) assert.Equal(t, []string{"LOCAL_BEFORE", "COMMON_B", "LOCAL_AFTER"}, keys) } func TestPrintRunScriptActionDetailsGolden(t *testing.T) { raw := `id: s1 name: Build run: | echo one echo two shell: pwsh env: PATH_PREFIX: /custom/bin GITHUB_TOKEN: tok GREETING: hello ` var step model.Step require.NoError(t, yaml.Unmarshal([]byte(raw), &step)) buf := &bytes.Buffer{} logger := logging.New(logging.NewLevelHandler(newJobLogHandler(buf, cyan), logging.LevelInfo)) entry := logger.With("job", "j1") ctx := common.WithLogger(t.Context(), entry) sr := &stepRun{ Step: &step, RunContext: &RunContext{}, shellCommand: `pwsh -command ". '{0}'"`, interpolatedScript: "echo one\necho two\n", env: map[string]string{ "PATH_PREFIX": "/custom/bin", "GITHUB_TOKEN": "tok", "GREETING": "hello", }, } sr.printRunScriptActionDetails(ctx) want := strings.Join([]string{ "[j1] | ::group::Run echo one", "[j1] | echo one", "[j1] | echo two", `[j1] | shell: pwsh -command ". '{0}'"`, "[j1] | env:", "[j1] | PATH_PREFIX: /custom/bin", "[j1] | GREETING: hello", "[j1] | ::endgroup::", "", }, "\n") assert.Equal(t, want, buf.String()) } func TestPrintRunActionHeaderGolden(t *testing.T) { raw := `id: s1 name: Checkout uses: actions/checkout@v4 with: fetch-depth: "0" Token: secret clean: "" env: CUSTOM: value GITHUB_TOKEN: tok ` var step model.Step require.NoError(t, yaml.Unmarshal([]byte(raw), &step)) buf := &bytes.Buffer{} logger := logging.New(logging.NewLevelHandler(newJobLogHandler(buf, cyan), logging.LevelInfo)) entry := logger.With("job", "j1") ctx := common.WithLogger(t.Context(), entry) action := &model.Action{Inputs: map[string]model.Input{"token": {}, "clean": {}, "path": {}, "lfs": {}}} env := map[string]string{"CUSTOM": "value", "GITHUB_TOKEN": "tok", "INPUT_TOKEN": "secret", "INPUT_CLEAN": "", "INPUT_PATH": "sub"} printRunActionHeader(ctx, &step, action, env, &RunContext{}) want := strings.Join([]string{ "[j1] | ::group::Run actions/checkout@v4", "[j1] | with:", "[j1] | Token: secret", "[j1] | fetch-depth: 0", "[j1] | path: sub", "[j1] | env:", "[j1] | CUSTOM: value", "[j1] | ::endgroup::", "", }, "\n") assert.Equal(t, want, buf.String()) } func TestIsInternalEnvKey(t *testing.T) { for _, k := range []string{"PATH", "HOME", "CI", "GITHUB_TOKEN", "GITEA_ACTIONS", "RUNNER_OS", "INPUT_FOO"} { assert.True(t, isInternalEnvKey(k, false), k) } for _, k := range []string{"PATH_PREFIX", "MY_VAR", "GREETING", "HOMEPAGE"} { assert.False(t, isInternalEnvKey(k, false), k) } assert.True(t, isInternalEnvKey("path", true)) assert.False(t, isInternalEnvKey("path", false)) } func TestPrintColoredScriptLineCyan(t *testing.T) { f := textFormatter{w: io.Discard, color: cyan} record := logging.Record{ Level: logging.LevelInfo, Message: "echo one", Attrs: []logging.Attr{ {Key: "job", Value: "j1"}, {Key: rawOutputField, Value: true}, {Key: scriptLineCyanField, Value: true}, }, } buf := &bytes.Buffer{} f.printColored(buf, record) assert.Equal(t, "\x1b[36m|\x1b[0m \x1b[36;1mecho one\x1b[0m", buf.String()) }