From 85209ea1f03d6ea935cd8b517a3cd5c4d25a95d1 Mon Sep 17 00:00:00 2001 From: 7ttp <117663341+7ttp@users.noreply.github.com> Date: Wed, 11 Feb 2026 12:23:26 +0530 Subject: [PATCH 1/6] fix: resolve \ir relative paths in single file pg_tap tests --- internal/db/test/test.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/internal/db/test/test.go b/internal/db/test/test.go index 2852ee9432..97baf6993a 100644 --- a/internal/db/test/test.go +++ b/internal/db/test/test.go @@ -42,14 +42,18 @@ func Run(ctx context.Context, testFiles []string, config pgconn.Config, fsys afe fp = filepath.Join(utils.CurrentDirAbs, fp) } dockerPath := utils.ToDockerPath(fp) - cmd = append(cmd, dockerPath) - binds[i] = fmt.Sprintf("%s:%s:ro", fp, dockerPath) if workingDir == "" { workingDir = dockerPath if path.Ext(dockerPath) != "" { workingDir = path.Dir(dockerPath) } } + relPath := dockerPath + if path.Ext(dockerPath) != "" { + relPath = path.Base(dockerPath) + } + cmd = append(cmd, relPath) + binds[i] = fmt.Sprintf("%s:%s:ro", fp, dockerPath) } if viper.GetBool("DEBUG") { cmd = append(cmd, "--verbose") From 3dcba8a3c5506edc316dbf067e6ffb171d593780 Mon Sep 17 00:00:00 2001 From: 7ttp <117663341+7ttp@users.noreply.github.com> Date: Thu, 12 Feb 2026 17:44:05 +0530 Subject: [PATCH 2/6] fix(db): traverse and mount files imported via \ir in pg_tap tests --- internal/db/test/test.go | 57 ++++++++++++++++++++++++++++++++++------ 1 file changed, 49 insertions(+), 8 deletions(-) diff --git a/internal/db/test/test.go b/internal/db/test/test.go index 97baf6993a..aeb12d2690 100644 --- a/internal/db/test/test.go +++ b/internal/db/test/test.go @@ -7,6 +7,8 @@ import ( "os" "path" "path/filepath" + "regexp" + "strings" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/network" @@ -25,19 +27,21 @@ const ( DISABLE_PGTAP = "drop extension if exists pgtap" ) +var irPattern = regexp.MustCompile(`(?im)^\\ir\s+['"]?([^'"\s]+)['"]?`) + func Run(ctx context.Context, testFiles []string, config pgconn.Config, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error { // Build test command if len(testFiles) == 0 { - absTestsDir, err := filepath.Abs(utils.DbTestsDir) - if err != nil { - return errors.Errorf("failed to resolve tests dir: %w", err) - } - testFiles = append(testFiles, absTestsDir) + testFiles = append(testFiles, utils.DbTestsDir) } - binds := make([]string, len(testFiles)) + allFiles, err := traverseImports(testFiles, fsys) + if err != nil { + return err + } + binds := make([]string, len(allFiles)) cmd := []string{"pg_prove", "--ext", ".pg", "--ext", ".sql", "-r"} var workingDir string - for i, fp := range testFiles { + for i, fp := range allFiles { if !filepath.IsAbs(fp) { fp = filepath.Join(utils.CurrentDirAbs, fp) } @@ -52,7 +56,9 @@ func Run(ctx context.Context, testFiles []string, config pgconn.Config, fsys afe if path.Ext(dockerPath) != "" { relPath = path.Base(dockerPath) } - cmd = append(cmd, relPath) + if i < len(testFiles) { + cmd = append(cmd, relPath) + } binds[i] = fmt.Sprintf("%s:%s:ro", fp, dockerPath) } if viper.GetBool("DEBUG") { @@ -111,3 +117,38 @@ func Run(ctx context.Context, testFiles []string, config pgconn.Config, fsys afe os.Stderr, ) } + +func traverseImports(testFiles []string, fsys afero.Fs) ([]string, error) { + seen := map[string]struct{}{} + q := append([]string{}, testFiles...) + result := []string{} + for len(q) > 0 { + curr := q[len(q)-1] + q = q[:len(q)-1] + if _, ok := seen[curr]; ok { + continue + } + seen[curr] = struct{}{} + result = append(result, curr) + info, err := fsys.Stat(curr) + if err != nil { + return nil, errors.Errorf("failed to stat %s: %w", curr, err) + } + if info.IsDir() { + continue + } + data, err := afero.ReadFile(fsys, curr) + if err != nil { + return nil, errors.Errorf("failed to read %s: %w", curr, err) + } + for _, m := range irPattern.FindAllStringSubmatch(string(data), -1) { + if len(m) < 2 { + continue + } + importPath := strings.TrimSpace(m[1]) + resolved := filepath.Join(filepath.Dir(curr), importPath) + q = append(q, resolved) + } + } + return result, nil +} \ No newline at end of file From f4b7c8cafad207ffe5a1172d81eae56e65e2b6a8 Mon Sep 17 00:00:00 2001 From: 7ttp <117663341+7ttp@users.noreply.github.com> Date: Thu, 12 Feb 2026 17:44:45 +0530 Subject: [PATCH 3/6] test(db): add unit tests for \ir import traversal --- internal/db/test/test_test.go | 40 +++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/internal/db/test/test_test.go b/internal/db/test/test_test.go index 063f9bcd11..f82f844bfe 100644 --- a/internal/db/test/test_test.go +++ b/internal/db/test/test_test.go @@ -30,6 +30,7 @@ func TestRunCommand(t *testing.T) { // Setup in-memory fs fsys := afero.NewMemMapFs() require.NoError(t, utils.WriteConfig(fsys, false)) + require.NoError(t, afero.WriteFile(fsys, "nested", []byte("SELECT 1;"), 0644)) // Setup mock postgres conn := pgtest.NewConn() defer conn.Close(t) @@ -53,6 +54,7 @@ func TestRunCommand(t *testing.T) { // Setup in-memory fs fsys := afero.NewMemMapFs() require.NoError(t, utils.WriteConfig(fsys, false)) + require.NoError(t, fsys.MkdirAll(utils.DbTestsDir, 0755)) // Run test err := Run(context.Background(), nil, dbConfig, fsys) // Check error @@ -63,6 +65,7 @@ func TestRunCommand(t *testing.T) { // Setup in-memory fs fsys := afero.NewMemMapFs() require.NoError(t, utils.WriteConfig(fsys, false)) + require.NoError(t, fsys.MkdirAll(utils.DbTestsDir, 0755)) // Setup mock postgres conn := pgtest.NewConn() defer conn.Close(t) @@ -79,6 +82,7 @@ func TestRunCommand(t *testing.T) { // Setup in-memory fs fsys := afero.NewMemMapFs() require.NoError(t, utils.WriteConfig(fsys, false)) + require.NoError(t, fsys.MkdirAll(utils.DbTestsDir, 0755)) // Setup mock postgres conn := pgtest.NewConn() defer conn.Close(t) @@ -99,3 +103,39 @@ func TestRunCommand(t *testing.T) { assert.Empty(t, apitest.ListUnmatchedRequests()) }) } + +func TestTraverseImports(t *testing.T) { + t.Run("handles file with \\ir import", func(t *testing.T) { + fsys := afero.NewMemMapFs() + require.NoError(t, afero.WriteFile(fsys, "main.sql", []byte("\\ir helper.sql"), 0644)) + require.NoError(t, afero.WriteFile(fsys, "helper.sql", []byte("SELECT 1;"), 0644)) + + result, err := traverseImports([]string{"main.sql"}, fsys) + + assert.NoError(t, err) + assert.Len(t, result, 2) + }) + + t.Run("handles nested \\ir imports", func(t *testing.T) { + fsys := afero.NewMemMapFs() + require.NoError(t, afero.WriteFile(fsys, "main.sql", []byte("\\ir level1.sql"), 0644)) + require.NoError(t, afero.WriteFile(fsys, "level1.sql", []byte("\\ir level2.sql"), 0644)) + require.NoError(t, afero.WriteFile(fsys, "level2.sql", []byte("SELECT 1;"), 0644)) + + result, err := traverseImports([]string{"main.sql"}, fsys) + + assert.NoError(t, err) + assert.Len(t, result, 3) + }) + + t.Run("handles circular imports", func(t *testing.T) { + fsys := afero.NewMemMapFs() + require.NoError(t, afero.WriteFile(fsys, "a.sql", []byte("\\ir b.sql"), 0644)) + require.NoError(t, afero.WriteFile(fsys, "b.sql", []byte("\\ir a.sql"), 0644)) + + result, err := traverseImports([]string{"a.sql"}, fsys) + + assert.NoError(t, err) + assert.Len(t, result, 2) + }) +} From 4eda876701291cf7842136e35ad0c98d65b74568 Mon Sep 17 00:00:00 2001 From: 7ttp <117663341+7ttp@users.noreply.github.com> Date: Thu, 12 Feb 2026 18:07:45 +0530 Subject: [PATCH 4/6] lint --- internal/db/test/test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/db/test/test.go b/internal/db/test/test.go index aeb12d2690..3b13158a7e 100644 --- a/internal/db/test/test.go +++ b/internal/db/test/test.go @@ -151,4 +151,4 @@ func traverseImports(testFiles []string, fsys afero.Fs) ([]string, error) { } } return result, nil -} \ No newline at end of file +} From 3bb4f73beb7f8c2ed723bab16f761cf427be9adc Mon Sep 17 00:00:00 2001 From: 7ttp <117663341+7ttp@users.noreply.github.com> Date: Sat, 14 Feb 2026 20:12:43 +0530 Subject: [PATCH 5/6] fix(db): use set lookup to identify test files vs imports --- internal/db/test/test.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/internal/db/test/test.go b/internal/db/test/test.go index 3b13158a7e..7f25057c86 100644 --- a/internal/db/test/test.go +++ b/internal/db/test/test.go @@ -38,6 +38,10 @@ func Run(ctx context.Context, testFiles []string, config pgconn.Config, fsys afe if err != nil { return err } + testFileSet := make(map[string]struct{}, len(testFiles)) + for _, tf := range testFiles { + testFileSet[tf] = struct{}{} + } binds := make([]string, len(allFiles)) cmd := []string{"pg_prove", "--ext", ".pg", "--ext", ".sql", "-r"} var workingDir string @@ -52,11 +56,11 @@ func Run(ctx context.Context, testFiles []string, config pgconn.Config, fsys afe workingDir = path.Dir(dockerPath) } } - relPath := dockerPath - if path.Ext(dockerPath) != "" { - relPath = path.Base(dockerPath) - } - if i < len(testFiles) { + if _, isTestFile := testFileSet[allFiles[i]]; isTestFile { + relPath := dockerPath + if path.Ext(dockerPath) != "" { + relPath = path.Base(dockerPath) + } cmd = append(cmd, relPath) } binds[i] = fmt.Sprintf("%s:%s:ro", fp, dockerPath) From d71d0dcbeb57dde51cd60a61f2a2bcc4d84c696b Mon Sep 17 00:00:00 2001 From: 7ttp <117663341+7ttp@users.noreply.github.com> Date: Sat, 14 Feb 2026 20:41:23 +0530 Subject: [PATCH 6/6] fix(db): handle indented \ir and multi-directory test files --- internal/db/test/test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/db/test/test.go b/internal/db/test/test.go index 7f25057c86..dc5273dc33 100644 --- a/internal/db/test/test.go +++ b/internal/db/test/test.go @@ -27,7 +27,7 @@ const ( DISABLE_PGTAP = "drop extension if exists pgtap" ) -var irPattern = regexp.MustCompile(`(?im)^\\ir\s+['"]?([^'"\s]+)['"]?`) +var irPattern = regexp.MustCompile(`(?im)^\s*\\ir\s+['"]?([^'"\s]+)['"]?`) func Run(ctx context.Context, testFiles []string, config pgconn.Config, fsys afero.Fs, options ...func(*pgx.ConnConfig)) error { // Build test command @@ -58,7 +58,7 @@ func Run(ctx context.Context, testFiles []string, config pgconn.Config, fsys afe } if _, isTestFile := testFileSet[allFiles[i]]; isTestFile { relPath := dockerPath - if path.Ext(dockerPath) != "" { + if path.Ext(dockerPath) != "" && path.Dir(dockerPath) == workingDir { relPath = path.Base(dockerPath) } cmd = append(cmd, relPath)