Files
mire/internal/miro/record_test.go

219 lines
5.7 KiB
Go

package miro
import (
"bytes"
"errors"
"os"
"path/filepath"
"testing"
)
func TestRecordCreatesRelativePath(t *testing.T) {
root := t.TempDir()
testDir := filepath.Join(root, "e2e")
mustMkdirAll(t, testDir)
addFakeRecordDependencies(t, "script")
got := withWorkingDir(t, root, func() string {
withStdin(t, "y\n", func() {})
path, err := Record(filepath.Join("a", "b", "c") + string(os.PathSeparator))
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)
}
}
}
func TestRecordStripsExplicitTestDirPrefix(t *testing.T) {
root := t.TempDir()
testDir := filepath.Join(root, "e2e")
mustMkdirAll(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)
}
}
}
func TestRecordRejectsAbsolutePathOutsideTestDir(t *testing.T) {
root := t.TempDir()
mustMkdirAll(t, filepath.Join(root, "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")
mustMkdirAll(t, testDir)
addFakeRecordDependencies(t, "script")
err := withWorkingDir(t, root, func() error {
target := filepath.Join(testDir, "a", "b", "c")
mustMkdirAll(t, target)
return withRecordStreams(t, "n\n", func(rio recordIO) error {
return recordScenario(target, rio)
})
})
if !errors.Is(err, ErrRecordingDiscarded) {
t.Fatalf("Record() error = %v, want ErrRecordingDiscarded", err)
}
target := filepath.Join(testDir, "a", "b", "c")
for _, name := range []string{"in", "out"} {
if _, err := os.Stat(filepath.Join(target, name)); !os.IsNotExist(err) {
t.Fatalf("Stat(%q) error = %v, want not exists", filepath.Join(target, name), err)
}
}
}
func TestRecordReturnsDiscardedErrorWhenOverwriteDeclined(t *testing.T) {
root := t.TempDir()
testDir := filepath.Join(root, "e2e")
target := filepath.Join(testDir, "a", "b", "c")
mustMkdirAll(t, target)
addFakeRecordDependencies(t, "script")
writeFile(t, filepath.Join(target, "in"), "existing in\n")
writeFile(t, filepath.Join(target, "out"), "existing out\n")
err := withWorkingDir(t, root, func() error {
return withRecordStreams(t, "n\n", func(rio recordIO) error {
return recordScenario(target, rio)
})
})
if !errors.Is(err, ErrRecordingDiscarded) {
t.Fatalf("recordScenario() error = %v, want ErrRecordingDiscarded", err)
}
for _, tc := range []struct {
name string
want string
}{
{name: "in", want: "existing in\n"},
{name: "out", want: "existing out\n"},
} {
got, readErr := os.ReadFile(filepath.Join(target, tc.name))
if readErr != nil {
t.Fatalf("ReadFile(%q) error = %v", filepath.Join(target, tc.name), readErr)
}
if string(got) != tc.want {
t.Fatalf("%s = %q, want %q", tc.name, string(got), tc.want)
}
}
}
func withRecordStreams[T any](t *testing.T, input string, fn func(recordIO) T) T {
t.Helper()
path := filepath.Join(t.TempDir(), "stdin.txt")
if err := os.WriteFile(path, []byte(input), 0o644); err != nil {
t.Fatalf("WriteFile(%q) error = %v", path, err)
}
file, err := os.Open(path)
if err != nil {
t.Fatalf("Open(%q) error = %v", path, err)
}
t.Cleanup(func() {
if err := file.Close(); err != nil {
t.Fatalf("close record input: %v", err)
}
})
return fn(recordIO{
in: file,
out: ioDiscard{},
err: &bytes.Buffer{},
})
}
func withStdin(t *testing.T, input string, fn func()) {
t.Helper()
path := filepath.Join(t.TempDir(), "stdin.txt")
if err := os.WriteFile(path, []byte(input), 0o644); err != nil {
t.Fatalf("WriteFile(%q) error = %v", path, err)
}
file, err := os.Open(path)
if err != nil {
t.Fatalf("Open(%q) error = %v", path, err)
}
oldStdin := os.Stdin
os.Stdin = file
t.Cleanup(func() {
os.Stdin = oldStdin
if err := file.Close(); err != nil {
t.Fatalf("close stdin file: %v", err)
}
})
fn()
}
type ioDiscard struct{}
func (ioDiscard) Write(p []byte) (int, error) {
return len(p), nil
}
func addFakeRecordDependencies(t *testing.T, names ...string) {
t.Helper()
binDir := t.TempDir()
for _, name := range names {
path := filepath.Join(binDir, name)
body := "#!/bin/sh\nexit 0\n"
if name == "script" {
body = "#!/bin/sh\nin=''\nout=''\nwhile [ \"$#\" -gt 0 ]; do\n case \"$1\" in\n -I)\n in=\"$2\"\n shift 2\n ;;\n -O)\n out=\"$2\"\n shift 2\n ;;\n *)\n shift\n ;;\n esac\ndone\nprintf 'fake recorded input\\n' > \"$in\"\nprintf 'fake recorded output\\n' > \"$out\"\nexit 0\n"
}
if err := os.WriteFile(path, []byte(body), 0o755); err != nil {
t.Fatalf("WriteFile(%q) error = %v", path, err)
}
}
t.Setenv("PATH", binDir+string(os.PathListSeparator)+os.Getenv("PATH"))
}