mirror of
https://gitea.com/gitea/runner.git
synced 2026-11-06 07:10:28 +00:00
test: avoid redundant fixture work
Reuse immutable fixtures, combine Git queries and avoid large data copies to reduce test overhead while retaining behavior checks. Assisted-by: Codex:GPT-6
This commit is contained in:
28 files changed
+261
-291
No files matched your search
@@ -59,7 +59,7 @@ func TestCheckout(t *testing.T) {
|
||||
t.Run("history", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
work := t.TempDir()
|
||||
runGit(t, "init", "--initial-branch=main", work)
|
||||
runGit(t, "init", "--template=", "--initial-branch=main", work)
|
||||
runGit(t, "-C", work, "commit", "--allow-empty", "-m", "c1")
|
||||
sha = runGit(t, "-C", work, "rev-parse", "HEAD")
|
||||
runGit(t, "-C", work, "tag", "v1")
|
||||
@@ -77,7 +77,7 @@ func TestCheckout(t *testing.T) {
|
||||
t.Run("sha256", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
sha256Repo := filepath.Join(server, "org", "sha256")
|
||||
runGit(t, "init", "--object-format=sha256", "--initial-branch=main", sha256Repo)
|
||||
runGit(t, "init", "--template=", "--object-format=sha256", "--initial-branch=main", sha256Repo)
|
||||
runGit(t, "-C", sha256Repo, "commit", "--allow-empty", "-m", "s1")
|
||||
runGit(t, "-C", sha256Repo, "checkout", "--detach")
|
||||
})
|
||||
@@ -267,21 +267,41 @@ exec "$LFS" "$@"
|
||||
assert.Contains(t, string(outputs), "ref="+cmp.Or(tc.eventRef, "refs/heads/main")+"\n")
|
||||
return
|
||||
}
|
||||
if tc.head == "" {
|
||||
assert.Empty(t, gitOutput(dst, "symbolic-ref", "-q", "HEAD"))
|
||||
} else {
|
||||
assert.Equal(t, "refs/heads/"+tc.head, gitOutput(dst, "symbolic-ref", "-q", "HEAD"))
|
||||
assert.Equal(t, "origin/"+tc.head, gitOutput(dst, "rev-parse", "--abbrev-ref", "@{u}"))
|
||||
headArgs := []string{"-C", dst, "rev-parse", "HEAD", "--symbolic-full-name", "HEAD"}
|
||||
if tc.head != "" {
|
||||
headArgs = append(headArgs, "--abbrev-ref", "@{u}")
|
||||
}
|
||||
assert.Contains(t, string(outputs), "commit="+gitOutput(dst, "rev-parse", "HEAD")+"\n")
|
||||
head := strings.Fields(runGit(t, headArgs...))
|
||||
if tc.head == "" {
|
||||
require.Len(t, head, 2)
|
||||
assert.Equal(t, "HEAD", head[1])
|
||||
} else {
|
||||
require.Len(t, head, 3)
|
||||
assert.Equal(t, "refs/heads/"+tc.head, head[1])
|
||||
assert.Equal(t, "origin/"+tc.head, head[2])
|
||||
}
|
||||
assert.Contains(t, string(outputs), "commit="+head[0]+"\n")
|
||||
if inputRef := tc.inputs["ref"]; inputRef != "" && inputRef != sha {
|
||||
assert.Contains(t, string(outputs), "ref="+inputRef+"\n")
|
||||
}
|
||||
assert.Equal(t, tc.inputs["sparse-checkout"] != "", gitOutput(dst, "config", "--local", "extensions.worktreeConfig") == "true")
|
||||
persisted := tc.inputs["persist-credentials"] != "false"
|
||||
for _, repo := range repos {
|
||||
var credentialIncludes []string
|
||||
var worktreeConfig string
|
||||
for entry := range strings.SplitSeq(runGit(t, "-C", repo, "config", "--local", "--no-includes", "--null", "--list"), "\x00") {
|
||||
key, value, _ := strings.Cut(entry, "\n")
|
||||
switch key {
|
||||
case "extensions.worktreeconfig":
|
||||
worktreeConfig = value
|
||||
case strings.ToLower(credentialsInclude):
|
||||
credentialIncludes = append(credentialIncludes, value)
|
||||
}
|
||||
}
|
||||
if repo == dst {
|
||||
assert.Equal(t, tc.inputs["sparse-checkout"] != "", strings.TrimSpace(worktreeConfig) == "true")
|
||||
}
|
||||
assert.Equal(t, tc.inputs["clean"] == "false", fileExists(filepath.Join(repo, "untracked")), repo)
|
||||
assert.Len(t, strings.Fields(gitOutput(repo, "config", "--local", "--get-all", credentialsInclude)), 1, repo)
|
||||
assert.Len(t, strings.Fields(strings.Join(credentialIncludes, "\n")), 1, repo)
|
||||
headers, _ := exec.Command("git", "-C", repo, "config", "--get-all", "http."+serverURL+"/.extraheader").Output()
|
||||
assert.Equal(t, persisted, string(headers) == "\nAuthorization: Basic dG9rZW46dG9r\n", repo)
|
||||
assert.Equal(t, persisted, gitOutput(repo, "config", "http."+serverURL+"/.sslcainfo") != "", repo)
|
||||
@@ -345,15 +365,16 @@ func TestCheckoutTrustsTheRunnerCAAndOffersClientCertOnlyDuringTheStep(t *testin
|
||||
|
||||
func publish(t *testing.T, server, name string, files, submodules map[string]string) {
|
||||
work := t.TempDir()
|
||||
runGit(t, "init", "--initial-branch=main", work)
|
||||
runGit(t, "init", "--template=", "--initial-branch=main", work)
|
||||
for file, content := range files {
|
||||
require.NoError(t, os.MkdirAll(filepath.Dir(filepath.Join(work, file)), 0o755))
|
||||
require.NoError(t, os.WriteFile(filepath.Join(work, file), []byte(content), 0o644))
|
||||
}
|
||||
runGit(t, "-C", work, "add", ".")
|
||||
var moduleConfig []byte
|
||||
for path, url := range submodules {
|
||||
runGit(t, "-C", work, "config", "-f", ".gitmodules", "submodule."+path+".path", path)
|
||||
runGit(t, "-C", work, "config", "-f", ".gitmodules", "submodule."+path+".url", url)
|
||||
moduleConfig = fmt.Appendf(moduleConfig, "[submodule %q]\n\tpath = %q\n\turl = %q\n", path, path, url)
|
||||
require.NoError(t, os.WriteFile(filepath.Join(work, ".gitmodules"), moduleConfig, 0o644))
|
||||
commit := runGit(t, "--git-dir", filepath.Join(server, name, url), "rev-parse", "main")
|
||||
runGit(t, "-C", work, "update-index", "--add", "--cacheinfo", "160000,"+commit+","+path)
|
||||
runGit(t, "-C", work, "add", ".gitmodules")
|
||||
|
||||
@@ -6,7 +6,6 @@ package process
|
||||
import (
|
||||
"os/exec"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
@@ -23,16 +22,13 @@ func TestGroupClosePropagatesToOrphan(t *testing.T) {
|
||||
childPID := ready()
|
||||
|
||||
require.NoError(t, cmd.Wait())
|
||||
require.Eventually(t, func() bool {
|
||||
return !processAlive(cmd.Process.Pid)
|
||||
}, 20*time.Second, 200*time.Millisecond, "the step process should have exited on its own")
|
||||
require.True(t, cmd.ProcessState.Exited(), "the step process should have exited on its own")
|
||||
|
||||
require.True(t, processAlive(childPID), "orphan should outlive its parent")
|
||||
|
||||
waitForExit := newProcessExitWaiter(t, childPID)
|
||||
require.NoError(t, group.Close())
|
||||
require.Eventually(t, func() bool {
|
||||
return !processAlive(childPID)
|
||||
}, 20*time.Second, 200*time.Millisecond, "closing the job must terminate the orphan")
|
||||
waitForExit()
|
||||
}
|
||||
|
||||
// TestGroupNestsWithKiller: assigned to the group first and the step's Killer
|
||||
@@ -53,10 +49,9 @@ func TestGroupNestsWithKiller(t *testing.T) {
|
||||
require.NoError(t, err, "the step's job object must nest inside the group's")
|
||||
defer killer.Close()
|
||||
|
||||
waitForExit := newProcessExitWaiter(t, cmd.Process.Pid)
|
||||
require.NoError(t, killer.Kill())
|
||||
require.Eventually(t, func() bool {
|
||||
return !processAlive(cmd.Process.Pid)
|
||||
}, 20*time.Second, 200*time.Millisecond, "cancelling the step should still kill its tree")
|
||||
waitForExit()
|
||||
}
|
||||
|
||||
// TestGroupNilSafe covers the fallback path: when the job object cannot be
|
||||
|
||||
@@ -9,8 +9,6 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"testing"
|
||||
@@ -46,23 +44,8 @@ func processAlive(pid int) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// TestKillerKillsTree verifies that a process group captured by the killer is
|
||||
// terminated together with a child the process spawns afterwards. This mirrors
|
||||
// a step or post-task script that launches a child which spawns further
|
||||
// processes, where cancelling must take down the whole tree, not just the
|
||||
// direct child.
|
||||
func TestKillerKillsTree(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
pidFile := filepath.Join(dir, "child.pid")
|
||||
|
||||
// Parent shell backgrounds a long-lived child (writing its PID to a file)
|
||||
// and then sleeps. With job control off (non-interactive sh) the backgrounded
|
||||
// child stays in the parent's process group, so the group kill must reach it.
|
||||
script := fmt.Sprintf(`sleep 600 & echo $! > %q; sleep 600`, pidFile)
|
||||
cmd := exec.Command("/bin/sh", "-c", script)
|
||||
// Launch as its own process-group leader, exactly like a real process does
|
||||
// (see SysProcAttr), so the killer's PGID == the process PID.
|
||||
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
|
||||
cmd, ready := newTestProcess(t, "parent", false)
|
||||
require.NoError(t, cmd.Start())
|
||||
t.Cleanup(func() {
|
||||
_ = syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
|
||||
@@ -73,25 +56,10 @@ func TestKillerKillsTree(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
defer killer.Close()
|
||||
|
||||
// Wait for the backgrounded child PID to be reported.
|
||||
var childPID int
|
||||
require.Eventually(t, func() bool {
|
||||
b, e := os.ReadFile(pidFile)
|
||||
if e != nil {
|
||||
return false
|
||||
}
|
||||
s := strings.TrimSpace(string(b))
|
||||
if s == "" {
|
||||
return false
|
||||
}
|
||||
childPID, _ = strconv.Atoi(s)
|
||||
return childPID > 0 && processAlive(childPID)
|
||||
}, 20*time.Second, 100*time.Millisecond, "child process should start")
|
||||
childPID := ready()
|
||||
require.True(t, processAlive(childPID), "child process should start")
|
||||
|
||||
// Killing the group must terminate both the parent and the backgrounded child.
|
||||
require.NoError(t, killer.Kill())
|
||||
// Reap the parent so it does not linger as a zombie (which would still report
|
||||
// as alive); SIGKILL makes Wait return promptly.
|
||||
_ = cmd.Wait()
|
||||
|
||||
require.Eventually(t, func() bool {
|
||||
|
||||
@@ -5,7 +5,6 @@ package process
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"golang.org/x/sys/windows"
|
||||
@@ -25,6 +24,23 @@ func processAlive(pid int) bool {
|
||||
return code == stillActive
|
||||
}
|
||||
|
||||
func newProcessExitWaiter(t *testing.T, pids ...int) func() {
|
||||
t.Helper()
|
||||
handles := make([]windows.Handle, 0, len(pids))
|
||||
for _, pid := range pids {
|
||||
handle, err := windows.OpenProcess(windows.SYNCHRONIZE, false, uint32(pid))
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { require.NoError(t, windows.CloseHandle(handle)) })
|
||||
handles = append(handles, handle)
|
||||
}
|
||||
return func() {
|
||||
t.Helper()
|
||||
result, err := windows.WaitForMultipleObjects(handles, true, 20000)
|
||||
require.NoError(t, err)
|
||||
require.EqualValues(t, windows.WAIT_OBJECT_0, result, "processes should exit: %v", pids)
|
||||
}
|
||||
}
|
||||
|
||||
func TestKillerKillsTree(t *testing.T) {
|
||||
cmd, ready := newTestProcess(t, "parent", false)
|
||||
require.NoError(t, cmd.Start())
|
||||
@@ -39,9 +55,7 @@ func TestKillerKillsTree(t *testing.T) {
|
||||
childPID := ready()
|
||||
require.True(t, processAlive(childPID), "child process should start")
|
||||
|
||||
waitForExit := newProcessExitWaiter(t, cmd.Process.Pid, childPID)
|
||||
require.NoError(t, killer.Kill())
|
||||
|
||||
require.Eventually(t, func() bool {
|
||||
return !processAlive(cmd.Process.Pid) && !processAlive(childPID)
|
||||
}, 20*time.Second, 200*time.Millisecond, "parent and child should both be terminated")
|
||||
waitForExit()
|
||||
}
|
||||
@@ -173,11 +173,9 @@ func TestReporter_RunDaemonClose_Race(t *testing.T) {
|
||||
)
|
||||
|
||||
ctx, cancel := context.WithCancel(t.Context())
|
||||
taskCtx, err := structpb.NewStruct(map[string]any{})
|
||||
require.NoError(t, err)
|
||||
cfg, _ := config.LoadDefault("")
|
||||
reporter := NewReporter(ctx, cancel, client, &runnerv1.Task{
|
||||
Context: taskCtx,
|
||||
Context: &structpb.Struct{},
|
||||
}, cfg)
|
||||
reporter.ResetSteps(newTestSteps(1))
|
||||
|
||||
@@ -474,10 +472,8 @@ func TestReporter_ServerCancelStillFlushesFinal(t *testing.T) {
|
||||
|
||||
ctx, cancel := context.WithCancel(t.Context())
|
||||
defer cancel()
|
||||
taskCtx, err := structpb.NewStruct(map[string]any{})
|
||||
require.NoError(t, err)
|
||||
cfg, _ := config.LoadDefault("")
|
||||
reporter := NewReporter(ctx, cancel, client, &runnerv1.Task{Context: taskCtx}, cfg)
|
||||
reporter := NewReporter(ctx, cancel, client, &runnerv1.Task{Context: &structpb.Struct{}}, cfg)
|
||||
reporter.ResetSteps(newTestSteps(1))
|
||||
|
||||
reporter.stateMu.Lock()
|
||||
@@ -527,10 +523,8 @@ func TestReporter_CloseReportsCancelledOnCanceledCtx(t *testing.T) {
|
||||
)
|
||||
|
||||
ctx, cancel := context.WithCancel(t.Context())
|
||||
taskCtx, err := structpb.NewStruct(map[string]any{})
|
||||
require.NoError(t, err)
|
||||
cfg, _ := config.LoadDefault("")
|
||||
reporter := NewReporter(ctx, cancel, client, &runnerv1.Task{Context: taskCtx}, cfg)
|
||||
reporter := NewReporter(ctx, cancel, client, &runnerv1.Task{Context: &structpb.Struct{}}, cfg)
|
||||
reporter.ResetSteps(newTestSteps(1))
|
||||
|
||||
cancel()
|
||||
@@ -671,19 +665,15 @@ func TestReporter_SetOutputs(t *testing.T) {
|
||||
require.True(t, ok)
|
||||
assert.Equal(t, "caf\xff", got.value)
|
||||
|
||||
maxValue := strings.Repeat("v", maxOutputValueLen)
|
||||
r.SetOutputs(map[string]string{"atlimit": maxValue})
|
||||
r.SetOutputs(map[string]string{"atlimit": longValue[:maxOutputValueLen]})
|
||||
got, ok = r.outputs["atlimit"]
|
||||
require.True(t, ok)
|
||||
assert.Len(t, got.value, maxOutputValueLen)
|
||||
}
|
||||
|
||||
func TestReporter_DebugSettingsAreNotMasked(t *testing.T) {
|
||||
taskCtx, err := structpb.NewStruct(map[string]any{})
|
||||
require.NoError(t, err)
|
||||
|
||||
reporter := NewReporter(t.Context(), nil, nil, &runnerv1.Task{
|
||||
Context: taskCtx,
|
||||
Context: &structpb.Struct{},
|
||||
Secrets: map[string]string{"ACTIONS_STEP_DEBUG": "true", "ACTIONS_RUNNER_DEBUG": "true", "TOKEN": "s3cr3t"},
|
||||
}, &config.Config{})
|
||||
defer deregisterGlobalMasks(reporter)
|
||||
@@ -861,10 +851,8 @@ func TestReporter_ServerFailureResultIsReportedAsFailure(t *testing.T) {
|
||||
|
||||
ctx, cancel := context.WithCancel(t.Context())
|
||||
defer cancel()
|
||||
taskCtx, err := structpb.NewStruct(map[string]any{})
|
||||
require.NoError(t, err)
|
||||
cfg, _ := config.LoadDefault("")
|
||||
reporter := NewReporter(ctx, cancel, client, &runnerv1.Task{Context: taskCtx}, cfg)
|
||||
reporter := NewReporter(ctx, cancel, client, &runnerv1.Task{Context: &structpb.Struct{}}, cfg)
|
||||
close(reporter.daemon)
|
||||
|
||||
require.NoError(t, reporter.ReportState(false))
|
||||
@@ -1031,9 +1019,7 @@ func newTestReporter(t *testing.T, client *mocks.Client, cfg *config.Config) *Re
|
||||
t.Helper()
|
||||
ctx, cancel := context.WithCancel(t.Context())
|
||||
t.Cleanup(cancel)
|
||||
taskCtx, err := structpb.NewStruct(map[string]any{})
|
||||
require.NoError(t, err)
|
||||
return NewReporter(ctx, cancel, client, &runnerv1.Task{Context: taskCtx}, cfg)
|
||||
return NewReporter(ctx, cancel, client, &runnerv1.Task{Context: &structpb.Struct{}}, cfg)
|
||||
}
|
||||
|
||||
func newTestSteps(n int) []*model.Step {
|
||||
@@ -1051,11 +1037,9 @@ func TestReporter_ReportedSteps(t *testing.T) {
|
||||
return &runnerv1.UpdateLogResponse{AckIndex: req.Index + int64(len(req.Rows))}, nil
|
||||
})
|
||||
client.On("UpdateTask", mock.Anything, mock.Anything).Maybe().Return(&runnerv1.UpdateTaskResponse{}, nil)
|
||||
taskCtx, err := structpb.NewStruct(map[string]any{})
|
||||
require.NoError(t, err)
|
||||
cfg, _ := config.LoadDefault("")
|
||||
ctx, cancel := context.WithCancel(t.Context())
|
||||
reporter := NewReporter(ctx, cancel, client, &runnerv1.Task{Context: taskCtx, Secrets: map[string]string{"TOKEN": "s3cr3t"}}, cfg)
|
||||
reporter := NewReporter(ctx, cancel, client, &runnerv1.Task{Context: &structpb.Struct{}, Secrets: map[string]string{"TOKEN": "s3cr3t"}}, cfg)
|
||||
reporter.RunDaemon()
|
||||
reporter.Logf("runner version")
|
||||
reporter.ResetSteps(steps)
|
||||
|
||||
@@ -117,7 +117,8 @@ func TestExportRetriesAndMatchesOTLP(t *testing.T) {
|
||||
certificate := server.TLS.Certificates[0]
|
||||
key, err := x509.MarshalPKCS8PrivateKey(certificate.PrivateKey)
|
||||
require.NoError(t, err)
|
||||
certificatePath, keyPath := filepath.Join(t.TempDir(), "cert.pem"), filepath.Join(t.TempDir(), "key.pem")
|
||||
dir := t.TempDir()
|
||||
certificatePath, keyPath := filepath.Join(dir, "cert.pem"), filepath.Join(dir, "key.pem")
|
||||
require.NoError(t, os.WriteFile(certificatePath, pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certificate.Certificate[0]}), 0o600))
|
||||
require.NoError(t, os.WriteFile(keyPath, pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: key}), 0o600))
|
||||
setEnv(t, map[string]string{
|
||||
|
||||
Reference in new issue
Block a user