From b1061ee1afa3717c5b5628ece55177a6bf83e285 Mon Sep 17 00:00:00 2001 From: ruinivist Date: Fri, 20 Mar 2026 21:23:37 +0000 Subject: [PATCH] refactor: path resolution to separate file --- cmd/root_test.go | 70 ---------------- internal/miro/path_resolution.go | 39 +++++++++ internal/miro/path_resolution_test.go | 116 ++++++++++++++++++++++++++ internal/miro/record.go | 32 ------- internal/miro/record_test.go | 52 ------------ 5 files changed, 155 insertions(+), 154 deletions(-) create mode 100644 internal/miro/path_resolution.go create mode 100644 internal/miro/path_resolution_test.go diff --git a/cmd/root_test.go b/cmd/root_test.go index 62055c4..a7b9ccf 100644 --- a/cmd/root_test.go +++ b/cmd/root_test.go @@ -104,76 +104,6 @@ func TestRunRecord(t *testing.T) { }) } -func TestRunRecordWithExplicitTestDirPath(t *testing.T) { - addFakeRecordDependencies(t, "script", "bwrap", "bash") - - root := t.TempDir() - wantDir := filepath.Join(root, "e2e") - - withWorkingDir(t, root, func() { - initStdout, initStderr := captureOutput(t, func() { - if got := Run([]string{"init"}); got != 0 { - t.Fatalf("Run() code = %d, want %d", got, 0) - } - }) - if initStdout != prefixed("Done initialising...\n") { - t.Fatalf("stdout = %q, want %q", initStdout, prefixed("Done initialising...\n")) - } - if initStderr != "" { - t.Fatalf("stderr = %q, want empty", initStderr) - } - - withStdin(t, "y\n", func() {}) - stdout, stderr := captureOutput(t, func() { - if got := Run([]string{"record", filepath.Join("e2e", "suite", "spec")}); got != 0 { - t.Fatalf("Run() code = %d, want %d", got, 0) - } - }) - - createdPath := filepath.Join(wantDir, "suite", "spec") - if stdout != prefixed(createdPath+"\n") { - t.Fatalf("stdout = %q, want %q", stdout, prefixed(createdPath+"\n")) - } - if !strings.Contains(stderr, "Save recording?") { - t.Fatalf("stderr = %q, want save prompt", stderr) - } - - for _, name := range []string{"in", "out"} { - if _, err := os.Stat(filepath.Join(createdPath, name)); err != nil { - t.Fatalf("Stat(%q) error = %v", filepath.Join(createdPath, name), err) - } - } - }) -} - -func TestRunRecordRejectsAbsolutePathOutsideTestDir(t *testing.T) { - root := t.TempDir() - wantDir := filepath.Join(root, "e2e") - if err := os.MkdirAll(wantDir, 0o755); err != nil { - t.Fatalf("MkdirAll() error = %v", err) - } - writeFile(t, filepath.Join(root, "miro.toml"), validConfigContent("e2e")) - - outside := filepath.Join(root, "outside", "spec") - withWorkingDir(t, root, func() { - stdout, stderr := captureOutput(t, func() { - if got := Run([]string{"record", outside}); got != 1 { - t.Fatalf("Run() code = %d, want %d", got, 1) - } - }) - - if stdout != "" { - t.Fatalf("stdout = %q, want empty", stdout) - } - if !strings.Contains(stderr, "must be inside test directory") { - t.Fatalf("stderr = %q, want test directory error", stderr) - } - if _, err := os.Stat(outside); !os.IsNotExist(err) { - t.Fatalf("Stat(%q) error = %v, want not exists", outside, err) - } - }) -} - func TestRunInitFailsWhenDependenciesMissing(t *testing.T) { root := t.TempDir() t.Setenv("PATH", "") diff --git a/internal/miro/path_resolution.go b/internal/miro/path_resolution.go new file mode 100644 index 0000000..d932973 --- /dev/null +++ b/internal/miro/path_resolution.go @@ -0,0 +1,39 @@ +package miro + +import ( + "fmt" + "os" + "path/filepath" + "strings" +) + +func resolveRecordTarget(testDir, path string) (string, error) { + return resolvePathWithinTestDir(testDir, path, "record") +} + +func resolvePathWithinTestDir(testDir, path, pathType string) (string, error) { + absTestDir, err := filepath.Abs(testDir) + if err != nil { + return "", fmt.Errorf("failed to resolve test directory path: %v", err) + } + + absPath, err := filepath.Abs(path) + if err != nil { + return "", fmt.Errorf("failed to resolve %s path: %v", pathType, err) + } + + relToTestDir, err := filepath.Rel(absTestDir, absPath) + if err == nil && isWithinBase(relToTestDir) { + return filepath.Join(absTestDir, relToTestDir), nil + } + + if filepath.IsAbs(path) { + return "", fmt.Errorf("%s path %q must be inside test directory %q", pathType, path, absTestDir) + } + + return filepath.Join(absTestDir, path), nil +} + +func isWithinBase(rel string) bool { + return rel == "." || (rel != ".." && !strings.HasPrefix(rel, ".."+string(os.PathSeparator))) +} diff --git a/internal/miro/path_resolution_test.go b/internal/miro/path_resolution_test.go new file mode 100644 index 0000000..2382ef1 --- /dev/null +++ b/internal/miro/path_resolution_test.go @@ -0,0 +1,116 @@ +package miro + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +func TestResolvePathWithinTestDirAcceptsRelativePath(t *testing.T) { + root := t.TempDir() + testDir := filepath.Join(root, "e2e") + + got := withWorkingDir(t, root, func() string { + path, err := resolvePathWithinTestDir(testDir, filepath.Join("suite", "spec"), "record") + if err != nil { + t.Fatalf("resolvePathWithinTestDir() error = %v", err) + } + return path + }) + + want := filepath.Join(testDir, "suite", "spec") + if got != want { + t.Fatalf("resolvePathWithinTestDir() = %q, want %q", got, want) + } +} + +func TestResolvePathWithinTestDirAcceptsExplicitTestDirPrefix(t *testing.T) { + root := t.TempDir() + testDir := filepath.Join(root, "e2e") + + got := withWorkingDir(t, root, func() string { + path, err := resolvePathWithinTestDir(testDir, filepath.Join("e2e", "suite", "spec"), "record") + if err != nil { + t.Fatalf("resolvePathWithinTestDir() error = %v", err) + } + return path + }) + + want := filepath.Join(testDir, "suite", "spec") + if got != want { + t.Fatalf("resolvePathWithinTestDir() = %q, want %q", got, want) + } +} + +func TestResolvePathWithinTestDirAcceptsAbsolutePathInsideTestDir(t *testing.T) { + testDir := filepath.Join(t.TempDir(), "e2e") + want := filepath.Join(testDir, "suite", "spec") + + got, err := resolvePathWithinTestDir(testDir, want, "record") + if err != nil { + t.Fatalf("resolvePathWithinTestDir() error = %v", err) + } + + if got != want { + t.Fatalf("resolvePathWithinTestDir() = %q, want %q", got, want) + } +} + +func TestResolvePathWithinTestDirRejectsAbsolutePathOutsideTestDir(t *testing.T) { + root := t.TempDir() + testDir := filepath.Join(root, "e2e") + outside := filepath.Join(root, "outside", "suite", "spec") + + _, err := resolvePathWithinTestDir(testDir, outside, "record") + if err == nil { + t.Fatal("resolvePathWithinTestDir() error = nil, want error") + } + if !strings.Contains(err.Error(), "must be inside test directory") { + t.Fatalf("resolvePathWithinTestDir() error = %q, want inside test directory error", err.Error()) + } +} + +func TestResolvePathWithinTestDirAllowsPathCurrentDirectory(t *testing.T) { + root := t.TempDir() + testDir := filepath.Join(root, "e2e") + mustMkdirAll(t, testDir) + + got := withWorkingDir(t, testDir, func() string { + path, err := resolvePathWithinTestDir(testDir, ".", "record") + if err != nil { + t.Fatalf("resolvePathWithinTestDir() error = %v", err) + } + return path + }) + + want, err := filepath.Abs(testDir) + if err != nil { + t.Fatalf("Abs(%q) error = %v", testDir, err) + } + if got != want { + t.Fatalf("resolvePathWithinTestDir() = %q, want %q", got, want) + } +} + +func TestIsWithinBase(t *testing.T) { + tests := []struct { + name string + rel string + want bool + }{ + {name: "dot", rel: ".", want: true}, + {name: "nested", rel: filepath.Join("a", "b"), want: true}, + {name: "parent", rel: "..", want: false}, + {name: "outside", rel: filepath.Join("..", "a"), want: false}, + {name: "same-prefix", rel: ".." + string(os.PathSeparator) + "a", want: false}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := isWithinBase(tt.rel); got != tt.want { + t.Fatalf("isWithinBase(%q) = %v, want %v", tt.rel, got, tt.want) + } + }) + } +} diff --git a/internal/miro/record.go b/internal/miro/record.go index b8bb564..4a0c4be 100644 --- a/internal/miro/record.go +++ b/internal/miro/record.go @@ -3,8 +3,6 @@ package miro import ( "fmt" "os" - "path/filepath" - "strings" ) // Record creates the requested scenario path under the resolved test directory @@ -49,33 +47,3 @@ func Record(path string) (string, error) { return target, nil } - -func resolveRecordTarget(testDir, path string) (string, error) { - absTestDir, err := filepath.Abs(testDir) - if err != nil { - return "", fmt.Errorf("failed to resolve test directory path: %v", err) - } - - absPath, err := filepath.Abs(path) - if err != nil { - return "", fmt.Errorf("failed to resolve record path: %v", err) - } - - // path rel to testDir - relToTestDir, err := filepath.Rel(absTestDir, absPath) - if err == nil && isWithinBase(relToTestDir) { - return filepath.Join(absTestDir, relToTestDir), nil - } - - // not rel to test dir and absolute path given - if filepath.IsAbs(path) { - return "", fmt.Errorf("record path %q must be inside test directory %q", path, absTestDir) - } - - return filepath.Join(absTestDir, path), nil -} - -// is the rel path going "outside" -func isWithinBase(rel string) bool { - return rel == "." || (rel != ".." && !strings.HasPrefix(rel, ".."+string(os.PathSeparator))) -} diff --git a/internal/miro/record_test.go b/internal/miro/record_test.go index e8882c1..bd54ba1 100644 --- a/internal/miro/record_test.go +++ b/internal/miro/record_test.go @@ -47,58 +47,6 @@ func TestRecordCreatesRelativePath(t *testing.T) { } } -func TestRecordAcceptsExplicitTestDirPrefix(t *testing.T) { - root := t.TempDir() - testDir := filepath.Join(root, "e2e") - mustMkdirAll(t, testDir) - writeFile(t, filepath.Join(root, "miro.toml"), validConfigContent("e2e")) - mustWriteRecordShell(t, testDir) - addFakeRecordDependencies(t, "script") - - got := withWorkingDir(t, root, func() string { - withStdin(t, "y\n", func() {}) - path, err := Record(filepath.Join("e2e", "a", "b", "c")) - if err != nil { - t.Fatalf("Record() error = %v", err) - } - return path - }) - - want := filepath.Join(testDir, "a", "b", "c") - if got != want { - t.Fatalf("Record() = %q, want %q", got, want) - } - - for _, name := range []string{"in", "out"} { - if _, err := os.Stat(filepath.Join(want, name)); err != nil { - t.Fatalf("Stat(%q) error = %v", filepath.Join(want, name), err) - } - } - - if got := readFile(t, filepath.Join(want, "out")); got != "fake recorded output\n" { - t.Fatalf("saved out = %q, want %q", got, "fake recorded output\n") - } -} - -func TestRecordRejectsAbsolutePathOutsideTestDir(t *testing.T) { - root := t.TempDir() - mustMkdirAll(t, filepath.Join(root, "e2e")) - writeFile(t, filepath.Join(root, "miro.toml"), validConfigContent("e2e")) - outside := filepath.Join(root, "outside", "a", "b", "c") - - err := withWorkingDir(t, root, func() error { - _, err := Record(outside) - return err - }) - - if err == nil { - t.Fatal("Record() error = nil, want error") - } - if _, statErr := os.Stat(outside); !os.IsNotExist(statErr) { - t.Fatalf("Stat(%q) error = %v, want not exists", outside, statErr) - } -} - func TestRecordReturnsDiscardedErrorWhenSaveDeclined(t *testing.T) { root := t.TempDir() testDir := filepath.Join(root, "e2e")