From f1ab86022af388677ee6ecb6e841b3c15a58bb06 Mon Sep 17 00:00:00 2001 From: ChristopherHX Date: Sat, 29 Mar 2025 17:41:42 +0100 Subject: [PATCH] chore: add tests to reporting fetch failure as job error It is already implemented in the Forgejo runner, but this adds tests that were missing. --- E.g. if GoGitAction Cache had a fetch failure this error did not trigger report jobResult Failure. Also the error has been not printed until the last message before exit of act. * adds tests for both corner cases (cherry picked from commit 5519c488a2bcfb458bc340fd554536d37138a3db) Conflicts: act/runner/job_executor.go already implemented at e6d1f0000638028a49ef0e573f275f80f5bc7ead but without testing act/runner/runner_test.go trivial context conflicts - s/logrus/log/ - s/t.Context()/context.Background()/ --- act/runner/runner_test.go | 132 ++++++++++++++++++ .../push.yml | 8 ++ 2 files changed, 140 insertions(+) create mode 100644 act/runner/testdata/action-cache-v2-fetch-failure-is-job-error/push.yml diff --git a/act/runner/runner_test.go b/act/runner/runner_test.go index d7a5b876..fd405d42 100644 --- a/act/runner/runner_test.go +++ b/act/runner/runner_test.go @@ -1,8 +1,10 @@ package runner import ( + "bufio" "bytes" "context" + "encoding/json" "fmt" "io" "os" @@ -637,3 +639,133 @@ func TestRunner_RunMatrixWithUserDefinedInclusions(t *testing.T) { tjfi.runTest(context.Background(), t, &Config{Matrix: matrix}) } + +type captureJobLoggerFactory struct { + buffer bytes.Buffer +} + +func (factory *captureJobLoggerFactory) WithJobLogger() *log.Logger { + logger := log.New() + logger.SetOutput(&factory.buffer) + logger.SetLevel(log.TraceLevel) + logger.SetFormatter(&log.JSONFormatter{}) + return logger +} + +func TestPullFailureIsJobFailure(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + tables := []TestJobFileInfo{ + {workdir, "checkout", "push", "pull failure", map[string]string{"ubuntu-latest": "localhost:0000/missing:latest"}, secrets}, + } + + for _, table := range tables { + t.Run(table.workflowPath, func(t *testing.T) { + factory := &captureJobLoggerFactory{} + + config := &Config{ + Secrets: table.secrets, + } + + eventFile := filepath.Join(workdir, table.workflowPath, "event.json") + if _, err := os.Stat(eventFile); err == nil { + config.EventPath = eventFile + } + config.ActionCache = &GoGitActionCache{ + path.Clean(path.Join(workdir, "cache")), + } + + logger := log.New() + logger.SetOutput(&factory.buffer) + logger.SetLevel(log.TraceLevel) + logger.SetFormatter(&log.JSONFormatter{}) + + table.runTest(common.WithLogger(WithJobLoggerFactory(context.Background(), factory), logger), t, config) + scan := bufio.NewScanner(&factory.buffer) + var hasJobResult, hasStepResult bool + for scan.Scan() { + t.Log(scan.Text()) + entry := map[string]interface{}{} + if json.Unmarshal(scan.Bytes(), &entry) == nil { + if val, ok := entry["jobResult"]; ok { + assert.Equal(t, "failure", val) + hasJobResult = true + } + if val, ok := entry["stepResult"]; ok && !hasStepResult { + assert.Equal(t, "failure", val) + hasStepResult = true + } + } + } + assert.True(t, hasStepResult, "stepResult not found") + assert.True(t, hasJobResult, "jobResult not found") + }) + } +} + +type mockCache struct{} + +func (c mockCache) Fetch(ctx context.Context, cacheDir, url, ref, token string) (string, error) { + _ = ctx + _ = cacheDir + _ = url + _ = ref + _ = token + return "", fmt.Errorf("fetch failure") +} + +func (c mockCache) GetTarArchive(ctx context.Context, cacheDir, sha, includePrefix string) (io.ReadCloser, error) { + _ = ctx + _ = cacheDir + _ = sha + _ = includePrefix + return nil, fmt.Errorf("fetch failure") +} + +func TestFetchFailureIsJobFailure(t *testing.T) { + if testing.Short() { + t.Skip("skipping integration test") + } + + tables := []TestJobFileInfo{ + {workdir, "action-cache-v2-fetch-failure-is-job-error", "push", "fetch failure", map[string]string{"ubuntu-latest": "-self-hosted"}, secrets}, + } + + for _, table := range tables { + t.Run(table.workflowPath, func(t *testing.T) { + factory := &captureJobLoggerFactory{} + + config := &Config{ + Secrets: table.secrets, + } + + eventFile := filepath.Join(workdir, table.workflowPath, "event.json") + if _, err := os.Stat(eventFile); err == nil { + config.EventPath = eventFile + } + config.ActionCache = &mockCache{} + + logger := log.New() + logger.SetOutput(&factory.buffer) + logger.SetLevel(log.TraceLevel) + logger.SetFormatter(&log.JSONFormatter{}) + + table.runTest(common.WithLogger(WithJobLoggerFactory(context.Background(), factory), logger), t, config) + scan := bufio.NewScanner(&factory.buffer) + var hasJobResult bool + for scan.Scan() { + t.Log(scan.Text()) + entry := map[string]interface{}{} + if json.Unmarshal(scan.Bytes(), &entry) == nil { + if val, ok := entry["jobResult"]; ok { + assert.Equal(t, "failure", val) + hasJobResult = true + } + } + } + assert.True(t, hasJobResult, "jobResult not found") + }) + } +} diff --git a/act/runner/testdata/action-cache-v2-fetch-failure-is-job-error/push.yml b/act/runner/testdata/action-cache-v2-fetch-failure-is-job-error/push.yml new file mode 100644 index 00000000..a5c93db1 --- /dev/null +++ b/act/runner/testdata/action-cache-v2-fetch-failure-is-job-error/push.yml @@ -0,0 +1,8 @@ +name: basic +on: push + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: nektos/test-override@a