From b9925de9b9bb05bce3b6fc217174d2dce16e7efb Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Fri, 12 Jun 2026 16:19:24 -0400 Subject: [PATCH 1/6] Modern txtar --- main_test.go | 6 +- tools/txtar/visitor.go | 37 +++------ tools/txtar/visitor_test.go | 146 ++++++++++++++++++++++++++++++++++++ 3 files changed, 161 insertions(+), 28 deletions(-) create mode 100644 tools/txtar/visitor_test.go diff --git a/main_test.go b/main_test.go index ada4256cb6a..6e965fb3bbb 100644 --- a/main_test.go +++ b/main_test.go @@ -37,9 +37,9 @@ const ( ) func TestMain(m *testing.M) { - os.Exit(testscript.RunMain(m, map[string]func() int{ - "chainlink": core.Main, - })) + testscript.Main(m, map[string]func(){ + "chainlink": func() { os.Exit(core.Main()) }, + }) } var ( diff --git a/tools/txtar/visitor.go b/tools/txtar/visitor.go index d96cdc6f0a8..da3d206b194 100644 --- a/tools/txtar/visitor.go +++ b/tools/txtar/visitor.go @@ -2,7 +2,6 @@ package txtar import ( "io/fs" - "os" "path/filepath" ) @@ -20,7 +19,8 @@ type DirVisitor struct { } func (d *DirVisitor) Walk() error { - return filepath.WalkDir(d.rootDir, func(path string, de fs.DirEntry, err error) error { + root := filepath.Clean(d.rootDir) + return filepath.WalkDir(root, func(path string, de fs.DirEntry, err error) error { if err != nil { return err } @@ -29,42 +29,29 @@ func (d *DirVisitor) Walk() error { return nil } - isRootDir, err := d.isRootDir(de) - if err != nil { - return err - } - - // If we're not recursing, skip all other directories except the root. - if !bool(d.recurse) && !isRootDir { - return nil + if !bool(d.recurse) && filepath.Clean(path) != root { + return fs.SkipDir } - matches, err := fs.Glob(os.DirFS(path), "*txtar") + matches, err := filepath.Glob(filepath.Join(path, "*txtar")) if err != nil { return err } if len(matches) > 0 { - return d.cb(path) + if err := d.cb(path); err != nil { + return err + } + } + + if !bool(d.recurse) { + return fs.SkipDir } return nil }) } -func (d *DirVisitor) isRootDir(de fs.DirEntry) (bool, error) { - fi, err := os.Stat(d.rootDir) - if err != nil { - return false, err - } - - fi2, err := de.Info() - if err != nil { - return false, err - } - return os.SameFile(fi, fi2), nil -} - func NewDirVisitor(rootDir string, recurse RecurseOpt, cb func(path string) error) *DirVisitor { return &DirVisitor{ rootDir: rootDir, diff --git a/tools/txtar/visitor_test.go b/tools/txtar/visitor_test.go new file mode 100644 index 00000000000..a19ba141fb2 --- /dev/null +++ b/tools/txtar/visitor_test.go @@ -0,0 +1,146 @@ +package txtar + +import ( + "errors" + "os" + "path/filepath" + "slices" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func writeTreeFile(t *testing.T, path string) { + t.Helper() + + require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755)) + require.NoError(t, os.WriteFile(path, []byte("txtar"), 0o644)) +} + +func collectTxtarDirs(t *testing.T, root string, recurse RecurseOpt) []string { + t.Helper() + + var dirs []string + visitor := NewDirVisitor(root, recurse, func(path string) error { + rel, err := filepath.Rel(root, path) + require.NoError(t, err) + dirs = append(dirs, rel) + return nil + }) + require.NoError(t, visitor.Walk()) + + slices.Sort(dirs) + return dirs +} + +func TestDirVisitor_Walk(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + files []string + recurse RecurseOpt + wantDirs []string + }{ + { + name: "recurse finds nested txtar directories", + files: []string{ + "a/test.txtar", + "b/readme.md", + "nested/deep/test.txtar", + }, + recurse: Recurse, + wantDirs: []string{"a", "nested/deep"}, + }, + { + name: "no recurse only visits root directory", + files: []string{ + "root.txtar", + "nested/test.txtar", + }, + recurse: NoRecurse, + wantDirs: []string{"."}, + }, + { + name: "no recurse skips nested txtar directories", + files: []string{ + "nested/test.txtar", + }, + recurse: NoRecurse, + wantDirs: nil, + }, + { + name: "recurse includes root when it contains txtar files", + files: []string{ + "root.txtar", + "child/test.txtar", + }, + recurse: Recurse, + wantDirs: []string{".", "child"}, + }, + { + name: "directories without txtar files are ignored", + files: []string{ + "empty/readme.md", + "scripts/test.txtar", + }, + recurse: Recurse, + wantDirs: []string{"scripts"}, + }, + { + name: "empty root returns no directories", + files: nil, + recurse: Recurse, + wantDirs: nil, + }, + { + name: "*txtar suffix matches non dotted extensions", + files: []string{ + "weird/foo_txtar", + }, + recurse: Recurse, + wantDirs: []string{"weird"}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + root := t.TempDir() + for _, file := range tt.files { + writeTreeFile(t, filepath.Join(root, file)) + } + + got := collectTxtarDirs(t, root, tt.recurse) + assert.Equal(t, tt.wantDirs, got) + }) + } +} + +func TestDirVisitor_Walk_callbackError(t *testing.T) { + t.Parallel() + + root := t.TempDir() + writeTreeFile(t, filepath.Join(root, "test.txtar")) + + wantErr := errors.New("callback failed") + visitor := NewDirVisitor(root, Recurse, func(string) error { + return wantErr + }) + + assert.ErrorIs(t, visitor.Walk(), wantErr) +} + +func TestDirVisitor_Walk_missingRoot(t *testing.T) { + t.Parallel() + + root := filepath.Join(t.TempDir(), "missing") + visitor := NewDirVisitor(root, Recurse, func(string) error { + t.Fatal("callback should not run") + return nil + }) + + assert.Error(t, visitor.Walk()) +} From 6eb63bfbbca566a8bae2eccf715effc130116c5f Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Fri, 12 Jun 2026 16:45:39 -0400 Subject: [PATCH 2/6] modernize TestScripts runner --- main_test.go | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/main_test.go b/main_test.go index 6e965fb3bbb..1f932c51875 100644 --- a/main_test.go +++ b/main_test.go @@ -58,21 +58,39 @@ func TestScripts(t *testing.T) { if testing.Short() { t.Skip("skipping testscript") } - t.Parallel() - require.NoError(t, os.Setenv("TMPDIR", "/tmp")) // osx default is too long for go-plugin sockets + require.NoError(t, os.Setenv("GOTMPDIR", "/tmp")) // keep workspaces in /tmp + t.Parallel() visitor := txtar.NewDirVisitor("testdata/scripts", txtar.Recurse, func(path string) error { t.Run(strings.TrimPrefix(path, "testdata/scripts/"), func(t *testing.T) { t.Parallel() - if message, shouldSkip := skipFlakyTests[t.Name()]; shouldSkip { - t.Skipf("Flaky Test: %s", message) + + // Check each .txtar file against skipFlakyTests + matches, err := filepath.Glob(filepath.Join(path, "*.txtar")) + require.NoError(t, err) + + var filesToRun []string + for _, match := range matches { + scriptName := strings.TrimSuffix(filepath.Base(match), ".txtar") + fullTestName := t.Name() + "/" + scriptName + + if message, shouldSkip := skipFlakyTests[fullTestName]; shouldSkip { + t.Logf("Skipping Flaky Test: %s - %s", fullTestName, message) + continue + } + filesToRun = append(filesToRun, match) + } + + if len(filesToRun) == 0 { + t.Skip("all scripts in directory skipped") } testscript.Run(t, testscript.Params{ - Dir: path, - Setup: commonEnv(t), - ContinueOnError: true, + Files: filesToRun, + Setup: commonEnv(t), + ContinueOnError: false, + RequireExplicitExec: true, // UpdateScripts: true, // uncomment to update golden files }) }) @@ -96,6 +114,7 @@ func commonEnv(t testing.TB) func(*testscript.Env) error { te.Setenv("VERSION", static.Version) te.Setenv("VERSION_TAG", static.VersionTag) te.Setenv("COMMIT_SHA", static.Sha) + te.Setenv("TMPDIR", "/tmp") // osx default is too long for go-plugin sockets b, err := os.ReadFile(filepath.Join(te.WorkDir, testPortName)) if err != nil && !os.IsNotExist(err) { From 75a4042392486048e44ccd0b00e8c8b1e21d220a Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Fri, 12 Jun 2026 18:23:44 -0400 Subject: [PATCH 3/6] Fix flaky TestScripts --- core/services/health.go | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/core/services/health.go b/core/services/health.go index 907187eb143..e9bd4e9853c 100644 --- a/core/services/health.go +++ b/core/services/health.go @@ -49,9 +49,6 @@ func (i *StartUpHealthReport) Stop() { func (i *StartUpHealthReport) Start() { go func() { - i.mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { - w.WriteHeader(http.StatusNoContent) - }) i.lggr.Info("Starting StartUpHealthReport") if err := i.server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) { i.lggr.Errorf("StartUpHealthReport server error: %v", err) @@ -63,6 +60,13 @@ func (i *StartUpHealthReport) Start() { // preventing shutdowns due to health-checks when running long backup tasks or migrations func NewStartUpHealthReport(port uint16, lggr logger.Logger) *StartUpHealthReport { mux := http.NewServeMux() + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + if r.URL.Path == "/health" { + w.WriteHeader(http.StatusNoContent) + return + } + w.WriteHeader(http.StatusServiceUnavailable) + }) return &StartUpHealthReport{ lggr: lggr, mux: mux, From cbc35556783914a2db5b7748c68c65eaa62b4edc Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Fri, 12 Jun 2026 18:39:38 -0400 Subject: [PATCH 4/6] /health test --- core/services/health_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/services/health_test.go b/core/services/health_test.go index e6f3884092a..169a659c88d 100644 --- a/core/services/health_test.go +++ b/core/services/health_test.go @@ -27,6 +27,18 @@ func TestNewStartUpHealthReport(t *testing.T) { res, err := http.DefaultClient.Do(req) require.NoError(t, err) require.Equal(t, http.StatusNoContent, res.StatusCode) + + req, err = http.NewRequestWithContext(t.Context(), "GET", "http://localhost:1234/", nil) + require.NoError(t, err) + res, err = http.DefaultClient.Do(req) + require.NoError(t, err) + require.Equal(t, http.StatusServiceUnavailable, res.StatusCode) + + req, err = http.NewRequestWithContext(t.Context(), "GET", "http://localhost:1234/unknown", nil) + require.NoError(t, err) + res, err = http.DefaultClient.Do(req) + require.NoError(t, err) + require.Equal(t, http.StatusServiceUnavailable, res.StatusCode) }() ibhr.Stop() From d4b93f2c37d9115bb57fc36565ddd54905c0eaeb Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Sun, 14 Jun 2026 11:35:22 -0400 Subject: [PATCH 5/6] Review comments --- main_test.go | 8 ++- .../txtar/testdata/callback_error/test.txtar | 1 + .../testdata/ignore_empty/empty/readme.md | 1 + .../testdata/ignore_empty/scripts/test.txtar | 1 + .../no_recurse_nested/nested/test.txtar | 1 + .../no_recurse_root/nested/test.txtar | 1 + .../txtar/testdata/no_recurse_root/root.txtar | 1 + .../recurse_includes_root/child/test.txtar | 1 + .../testdata/recurse_includes_root/root.txtar | 1 + .../testdata/recurse_nested/a/test.txtar | 1 + .../txtar/testdata/recurse_nested/b/readme.md | 1 + .../recurse_nested/nested/deep/test.txtar | 1 + .../testdata/suffix_matches/weird/foo_txtar | 1 + tools/txtar/visitor_test.go | 64 +++++-------------- 14 files changed, 35 insertions(+), 49 deletions(-) create mode 100644 tools/txtar/testdata/callback_error/test.txtar create mode 100644 tools/txtar/testdata/ignore_empty/empty/readme.md create mode 100644 tools/txtar/testdata/ignore_empty/scripts/test.txtar create mode 100644 tools/txtar/testdata/no_recurse_nested/nested/test.txtar create mode 100644 tools/txtar/testdata/no_recurse_root/nested/test.txtar create mode 100644 tools/txtar/testdata/no_recurse_root/root.txtar create mode 100644 tools/txtar/testdata/recurse_includes_root/child/test.txtar create mode 100644 tools/txtar/testdata/recurse_includes_root/root.txtar create mode 100644 tools/txtar/testdata/recurse_nested/a/test.txtar create mode 100644 tools/txtar/testdata/recurse_nested/b/readme.md create mode 100644 tools/txtar/testdata/recurse_nested/nested/deep/test.txtar create mode 100644 tools/txtar/testdata/suffix_matches/weird/foo_txtar diff --git a/main_test.go b/main_test.go index 1f932c51875..78445157c1a 100644 --- a/main_test.go +++ b/main_test.go @@ -59,7 +59,11 @@ func TestScripts(t *testing.T) { t.Skip("skipping testscript") } - require.NoError(t, os.Setenv("GOTMPDIR", "/tmp")) // keep workspaces in /tmp + tmp := t.TempDir() + require.NoError(t, os.Setenv("GOTMPDIR", tmp)) + t.Cleanup(func() { + require.NoError(t, os.Unsetenv("GOTMPDIR")) + }) t.Parallel() visitor := txtar.NewDirVisitor("testdata/scripts", txtar.Recurse, func(path string) error { @@ -89,7 +93,7 @@ func TestScripts(t *testing.T) { testscript.Run(t, testscript.Params{ Files: filesToRun, Setup: commonEnv(t), - ContinueOnError: false, + ContinueOnError: true, RequireExplicitExec: true, // UpdateScripts: true, // uncomment to update golden files }) diff --git a/tools/txtar/testdata/callback_error/test.txtar b/tools/txtar/testdata/callback_error/test.txtar new file mode 100644 index 00000000000..49aaf8d83a1 --- /dev/null +++ b/tools/txtar/testdata/callback_error/test.txtar @@ -0,0 +1 @@ +txtar diff --git a/tools/txtar/testdata/ignore_empty/empty/readme.md b/tools/txtar/testdata/ignore_empty/empty/readme.md new file mode 100644 index 00000000000..49aaf8d83a1 --- /dev/null +++ b/tools/txtar/testdata/ignore_empty/empty/readme.md @@ -0,0 +1 @@ +txtar diff --git a/tools/txtar/testdata/ignore_empty/scripts/test.txtar b/tools/txtar/testdata/ignore_empty/scripts/test.txtar new file mode 100644 index 00000000000..49aaf8d83a1 --- /dev/null +++ b/tools/txtar/testdata/ignore_empty/scripts/test.txtar @@ -0,0 +1 @@ +txtar diff --git a/tools/txtar/testdata/no_recurse_nested/nested/test.txtar b/tools/txtar/testdata/no_recurse_nested/nested/test.txtar new file mode 100644 index 00000000000..49aaf8d83a1 --- /dev/null +++ b/tools/txtar/testdata/no_recurse_nested/nested/test.txtar @@ -0,0 +1 @@ +txtar diff --git a/tools/txtar/testdata/no_recurse_root/nested/test.txtar b/tools/txtar/testdata/no_recurse_root/nested/test.txtar new file mode 100644 index 00000000000..49aaf8d83a1 --- /dev/null +++ b/tools/txtar/testdata/no_recurse_root/nested/test.txtar @@ -0,0 +1 @@ +txtar diff --git a/tools/txtar/testdata/no_recurse_root/root.txtar b/tools/txtar/testdata/no_recurse_root/root.txtar new file mode 100644 index 00000000000..49aaf8d83a1 --- /dev/null +++ b/tools/txtar/testdata/no_recurse_root/root.txtar @@ -0,0 +1 @@ +txtar diff --git a/tools/txtar/testdata/recurse_includes_root/child/test.txtar b/tools/txtar/testdata/recurse_includes_root/child/test.txtar new file mode 100644 index 00000000000..49aaf8d83a1 --- /dev/null +++ b/tools/txtar/testdata/recurse_includes_root/child/test.txtar @@ -0,0 +1 @@ +txtar diff --git a/tools/txtar/testdata/recurse_includes_root/root.txtar b/tools/txtar/testdata/recurse_includes_root/root.txtar new file mode 100644 index 00000000000..49aaf8d83a1 --- /dev/null +++ b/tools/txtar/testdata/recurse_includes_root/root.txtar @@ -0,0 +1 @@ +txtar diff --git a/tools/txtar/testdata/recurse_nested/a/test.txtar b/tools/txtar/testdata/recurse_nested/a/test.txtar new file mode 100644 index 00000000000..49aaf8d83a1 --- /dev/null +++ b/tools/txtar/testdata/recurse_nested/a/test.txtar @@ -0,0 +1 @@ +txtar diff --git a/tools/txtar/testdata/recurse_nested/b/readme.md b/tools/txtar/testdata/recurse_nested/b/readme.md new file mode 100644 index 00000000000..49aaf8d83a1 --- /dev/null +++ b/tools/txtar/testdata/recurse_nested/b/readme.md @@ -0,0 +1 @@ +txtar diff --git a/tools/txtar/testdata/recurse_nested/nested/deep/test.txtar b/tools/txtar/testdata/recurse_nested/nested/deep/test.txtar new file mode 100644 index 00000000000..49aaf8d83a1 --- /dev/null +++ b/tools/txtar/testdata/recurse_nested/nested/deep/test.txtar @@ -0,0 +1 @@ +txtar diff --git a/tools/txtar/testdata/suffix_matches/weird/foo_txtar b/tools/txtar/testdata/suffix_matches/weird/foo_txtar new file mode 100644 index 00000000000..49aaf8d83a1 --- /dev/null +++ b/tools/txtar/testdata/suffix_matches/weird/foo_txtar @@ -0,0 +1 @@ +txtar diff --git a/tools/txtar/visitor_test.go b/tools/txtar/visitor_test.go index a19ba141fb2..647a14dc648 100644 --- a/tools/txtar/visitor_test.go +++ b/tools/txtar/visitor_test.go @@ -2,7 +2,6 @@ package txtar import ( "errors" - "os" "path/filepath" "slices" "testing" @@ -11,13 +10,6 @@ import ( "github.com/stretchr/testify/require" ) -func writeTreeFile(t *testing.T, path string) { - t.Helper() - - require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755)) - require.NoError(t, os.WriteFile(path, []byte("txtar"), 0o644)) -} - func collectTxtarDirs(t *testing.T, root string, recurse RecurseOpt) []string { t.Helper() @@ -39,66 +31,49 @@ func TestDirVisitor_Walk(t *testing.T) { tests := []struct { name string - files []string + testDir string recurse RecurseOpt wantDirs []string }{ { - name: "recurse finds nested txtar directories", - files: []string{ - "a/test.txtar", - "b/readme.md", - "nested/deep/test.txtar", - }, + name: "recurse finds nested txtar directories", + testDir: "recurse_nested", recurse: Recurse, wantDirs: []string{"a", "nested/deep"}, }, { - name: "no recurse only visits root directory", - files: []string{ - "root.txtar", - "nested/test.txtar", - }, + name: "no recurse only visits root directory", + testDir: "no_recurse_root", recurse: NoRecurse, wantDirs: []string{"."}, }, { - name: "no recurse skips nested txtar directories", - files: []string{ - "nested/test.txtar", - }, + name: "no recurse skips nested txtar directories", + testDir: "no_recurse_nested", recurse: NoRecurse, wantDirs: nil, }, { - name: "recurse includes root when it contains txtar files", - files: []string{ - "root.txtar", - "child/test.txtar", - }, + name: "recurse includes root when it contains txtar files", + testDir: "recurse_includes_root", recurse: Recurse, wantDirs: []string{".", "child"}, }, { - name: "directories without txtar files are ignored", - files: []string{ - "empty/readme.md", - "scripts/test.txtar", - }, + name: "directories without txtar files are ignored", + testDir: "ignore_empty", recurse: Recurse, wantDirs: []string{"scripts"}, }, { name: "empty root returns no directories", - files: nil, + testDir: "empty_root", recurse: Recurse, wantDirs: nil, }, { - name: "*txtar suffix matches non dotted extensions", - files: []string{ - "weird/foo_txtar", - }, + name: "*txtar suffix matches non dotted extensions", + testDir: "suffix_matches", recurse: Recurse, wantDirs: []string{"weird"}, }, @@ -108,11 +83,7 @@ func TestDirVisitor_Walk(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() - root := t.TempDir() - for _, file := range tt.files { - writeTreeFile(t, filepath.Join(root, file)) - } - + root := filepath.Join("testdata", tt.testDir) got := collectTxtarDirs(t, root, tt.recurse) assert.Equal(t, tt.wantDirs, got) }) @@ -122,8 +93,7 @@ func TestDirVisitor_Walk(t *testing.T) { func TestDirVisitor_Walk_callbackError(t *testing.T) { t.Parallel() - root := t.TempDir() - writeTreeFile(t, filepath.Join(root, "test.txtar")) + root := filepath.Join("testdata", "callback_error") wantErr := errors.New("callback failed") visitor := NewDirVisitor(root, Recurse, func(string) error { @@ -136,7 +106,7 @@ func TestDirVisitor_Walk_callbackError(t *testing.T) { func TestDirVisitor_Walk_missingRoot(t *testing.T) { t.Parallel() - root := filepath.Join(t.TempDir(), "missing") + root := filepath.Join("testdata", "missing_root_dir_that_does_not_exist") visitor := NewDirVisitor(root, Recurse, func(string) error { t.Fatal("callback should not run") return nil From 11d296d3405eccad4694e0bbe13405175f72a93c Mon Sep 17 00:00:00 2001 From: Adam Hamrick Date: Mon, 15 Jun 2026 13:27:41 -0400 Subject: [PATCH 6/6] gitkeep to fix empty dir test in CI --- tools/txtar/testdata/empty_root/.gitkeep | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tools/txtar/testdata/empty_root/.gitkeep diff --git a/tools/txtar/testdata/empty_root/.gitkeep b/tools/txtar/testdata/empty_root/.gitkeep new file mode 100644 index 00000000000..e69de29bb2d