test: refactor tests for a leaner test setup

This commit is contained in:
2026-03-21 08:53:05 +00:00
parent f4820d8099
commit d56e9f3204
8 changed files with 501 additions and 964 deletions
+47 -579
View File
@@ -1,18 +1,18 @@
package cmd
import (
"bytes"
"io"
"os"
"path/filepath"
"strings"
"testing"
"miro/internal/testutil"
)
func TestRunShowsHelpWhenNoArgs(t *testing.T) {
addFakeRecordDependencies(t, "script", "bwrap", "bash")
testutil.AddFakeRecordDependencies(t, "script", "bwrap", "bash")
stdout, stderr := captureOutput(t, func() {
stdout, stderr := testutil.CaptureOutput(t, func() {
if got := Run(nil); got != 0 {
t.Fatalf("Run() code = %d, want %d", got, 0)
}
@@ -31,10 +31,10 @@ func TestRunShowsHelpWhenNoArgs(t *testing.T) {
func TestRunInit(t *testing.T) {
root := t.TempDir()
addFakeRecordDependencies(t, "script", "bwrap", "bash")
testutil.AddFakeRecordDependencies(t, "script", "bwrap", "bash")
withWorkingDir(t, root, func() {
stdout, stderr := captureOutput(t, func() {
testutil.WithWorkingDir(t, root, func() struct{} {
stdout, stderr := testutil.CaptureOutput(t, func() {
if got := Run([]string{"init"}); got != 0 {
t.Fatalf("Run() code = %d, want %d", got, 0)
}
@@ -46,10 +46,11 @@ func TestRunInit(t *testing.T) {
if stderr != "" {
t.Fatalf("stderr = %q, want empty", stderr)
}
return struct{}{}
})
if got := mustReadFile(t, filepath.Join(root, "miro.toml")); got != defaultWrittenConfig("e2e") {
t.Fatalf("config = %q, want %q", got, defaultWrittenConfig("e2e"))
if got := testutil.ReadFile(t, filepath.Join(root, "miro.toml")); got != testutil.DefaultWrittenConfig("e2e") {
t.Fatalf("config = %q, want %q", got, testutil.DefaultWrittenConfig("e2e"))
}
info, err := os.Stat(filepath.Join(root, "e2e", "shell.sh"))
if err != nil {
@@ -61,13 +62,13 @@ func TestRunInit(t *testing.T) {
}
func TestRunRecord(t *testing.T) {
addFakeRecordDependencies(t, "script", "bwrap", "bash")
testutil.AddFakeRecordDependencies(t, "script", "bwrap", "bash")
root := t.TempDir()
wantDir := filepath.Join(root, "e2e")
withWorkingDir(t, root, func() {
initStdout, initStderr := captureOutput(t, func() {
testutil.WithWorkingDir(t, root, func() struct{} {
initStdout, initStderr := testutil.CaptureOutput(t, func() {
if got := Run([]string{"init"}); got != 0 {
t.Fatalf("Run() code = %d, want %d", got, 0)
}
@@ -79,8 +80,8 @@ func TestRunRecord(t *testing.T) {
t.Fatalf("stderr = %q, want empty", initStderr)
}
withStdin(t, "y\n", func() {})
stdout, stderr := captureOutput(t, func() {
testutil.WithStdin(t, "y\n", func() {})
stdout, stderr := testutil.CaptureOutput(t, func() {
if got := Run([]string{"record", "suite/spec"}); got != 0 {
t.Fatalf("Run() code = %d, want %d", got, 0)
}
@@ -99,6 +100,7 @@ func TestRunRecord(t *testing.T) {
t.Fatalf("Stat(%q) error = %v", filepath.Join(createdPath, name), err)
}
}
return struct{}{}
})
}
@@ -106,8 +108,8 @@ func TestRunInitFailsWhenDependenciesMissing(t *testing.T) {
root := t.TempDir()
t.Setenv("PATH", "")
withWorkingDir(t, root, func() {
stdout, stderr := captureOutput(t, func() {
testutil.WithWorkingDir(t, root, func() struct{} {
stdout, stderr := testutil.CaptureOutput(t, func() {
if got := Run([]string{"init"}); got != 1 {
t.Fatalf("Run() code = %d, want %d", got, 1)
}
@@ -125,82 +127,40 @@ func TestRunInitFailsWhenDependenciesMissing(t *testing.T) {
if _, err := os.Stat(filepath.Join(root, "e2e", "shell.sh")); !os.IsNotExist(err) {
t.Fatalf("Stat(%q) error = %v, want not exists", filepath.Join(root, "e2e", "shell.sh"), err)
}
return struct{}{}
})
}
func TestRunRecordFailsWhenDependencyMissing(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"))
func TestRunRecordMissingPath(t *testing.T) {
testutil.AddFakeRecordDependencies(t, "script", "bwrap", "bash")
for _, tc := range []struct {
name string
deps []string
missing string
}{
{name: "script", deps: []string{"bwrap", "bash"}, missing: "script"},
{name: "bwrap", deps: []string{"script", "bash"}, missing: "bwrap"},
{name: "bash", deps: []string{"script", "bwrap"}, missing: "bash"},
} {
t.Run(tc.name, func(t *testing.T) {
addFakeRecordDependencies(t, tc.deps...)
withWorkingDir(t, root, func() {
stdout, stderr := captureOutput(t, func() {
if got := Run([]string{"record", "suite/spec"}); got != 1 {
t.Fatalf("Run() code = %d, want %d", got, 1)
}
})
if stdout != "" {
t.Fatalf("stdout = %q, want empty", stdout)
}
want := `required command "` + tc.missing + `" not found in PATH`
if !strings.Contains(stderr, want) {
t.Fatalf("stderr = %q, want missing dependency error %q", stderr, want)
}
if _, err := os.Stat(filepath.Join(wantDir, "suite", "spec", "in")); !os.IsNotExist(err) {
t.Fatalf("Stat() error = %v, want not exists", err)
}
})
})
}
}
func TestRunRecordMissingConfigFails(t *testing.T) {
addFakeRecordDependencies(t, "script", "bwrap", "bash")
root := t.TempDir()
withWorkingDir(t, root, func() {
stdout, stderr := captureOutput(t, func() {
if got := Run([]string{"record", "suite/spec"}); got != 1 {
t.Fatalf("Run() code = %d, want %d", got, 1)
}
})
if stdout != "" {
t.Fatalf("stdout = %q, want empty", stdout)
}
if !strings.Contains(stderr, "failed to resolve test directory") || !strings.Contains(stderr, "miro.toml") {
t.Fatalf("stderr = %q, want missing config error", stderr)
stdout, stderr := testutil.CaptureOutput(t, func() {
if got := Run([]string{"record"}); got != 1 {
t.Fatalf("Run() code = %d, want %d", got, 1)
}
})
if stdout != "" {
t.Fatalf("stdout = %q, want empty", stdout)
}
if !strings.Contains(stderr, "accepts 1 arg(s), received 0") {
t.Fatalf("stderr = %q, want argument error", stderr)
}
if !strings.Contains(stderr, "Usage:") || !strings.Contains(stderr, "miro record <path>") {
t.Fatalf("stderr = %q, want record usage", stderr)
}
}
func TestRunTest(t *testing.T) {
addFakeRecordDependencies(t, "script", "bwrap", "bash")
testutil.AddFakeRecordDependencies(t, "script", "bwrap", "bash")
t.Setenv("FAKE_SCRIPT_ECHO_STDIN", "1")
root := t.TempDir()
writeScenarioFixtures(t, filepath.Join(root, "e2e", "a"), "echo one\n", "echo one\n")
writeScenarioFixtures(t, filepath.Join(root, "e2e", "nested", "b"), "echo two\n", "echo two\n")
testutil.WriteScenarioFixtures(t, filepath.Join(root, "e2e", "a"), "echo one\n", "echo one\n")
testutil.WriteScenarioFixtures(t, filepath.Join(root, "e2e", "nested", "b"), "echo two\n", "echo two\n")
withWorkingDir(t, root, func() {
initStdout, initStderr := captureOutput(t, func() {
testutil.WithWorkingDir(t, root, func() struct{} {
initStdout, initStderr := testutil.CaptureOutput(t, func() {
if got := Run([]string{"init"}); got != 0 {
t.Fatalf("Run() code = %d, want %d", got, 0)
}
@@ -212,7 +172,7 @@ func TestRunTest(t *testing.T) {
t.Fatalf("stderr = %q, want empty", initStderr)
}
stdout, stderr := captureOutput(t, func() {
stdout, stderr := testutil.CaptureOutput(t, func() {
if got := Run([]string{"test"}); got != 0 {
t.Fatalf("Run() code = %d, want %d", got, 0)
}
@@ -233,270 +193,14 @@ func TestRunTest(t *testing.T) {
if stderr != "" {
t.Fatalf("stderr = %q, want empty", stderr)
}
return struct{}{}
})
}
func TestRunTestScopedDirectory(t *testing.T) {
addFakeRecordDependencies(t, "script", "bwrap", "bash")
t.Setenv("FAKE_SCRIPT_ECHO_STDIN", "1")
root := t.TempDir()
writeScenarioFixtures(t, filepath.Join(root, "e2e", "a"), "echo one\n", "echo one\n")
writeScenarioFixtures(t, filepath.Join(root, "e2e", "nested", "b"), "echo two\n", "echo two\n")
writeScenarioFixtures(t, filepath.Join(root, "e2e", "nested", "c"), "echo three\n", "echo three\n")
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)
}
stdout, stderr := captureOutput(t, func() {
if got := Run([]string{"test", "nested"}); got != 0 {
t.Fatalf("Run() code = %d, want %d", got, 0)
}
})
for _, want := range []string{
"RUN nested/b",
"PASS nested/b (",
"RUN nested/c",
"PASS nested/c (",
"Summary: total=2 passed=2 failed=0",
} {
if !strings.Contains(stdout, want) {
t.Fatalf("stdout = %q, want substring %q", stdout, want)
}
}
if strings.Contains(stdout, "RUN a") {
t.Fatalf("stdout = %q, want scoped run to exclude scenario outside nested", stdout)
}
if stderr != "" {
t.Fatalf("stderr = %q, want empty", stderr)
}
})
}
func TestRunTestScopedLeafDirectory(t *testing.T) {
addFakeRecordDependencies(t, "script", "bwrap", "bash")
t.Setenv("FAKE_SCRIPT_ECHO_STDIN", "1")
root := t.TempDir()
writeScenarioFixtures(t, filepath.Join(root, "e2e", "a"), "echo one\n", "echo one\n")
writeScenarioFixtures(t, filepath.Join(root, "e2e", "nested", "b"), "echo two\n", "echo two\n")
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)
}
stdout, stderr := captureOutput(t, func() {
if got := Run([]string{"test", filepath.Join("nested", "b")}); got != 0 {
t.Fatalf("Run() code = %d, want %d", got, 0)
}
})
for _, want := range []string{
"RUN nested/b",
"PASS nested/b (",
"Summary: total=1 passed=1 failed=0",
} {
if !strings.Contains(stdout, want) {
t.Fatalf("stdout = %q, want substring %q", stdout, want)
}
}
if strings.Contains(stdout, "RUN a") {
t.Fatalf("stdout = %q, want scoped run to exclude scenario outside nested/b", stdout)
}
if stderr != "" {
t.Fatalf("stderr = %q, want empty", stderr)
}
})
}
func TestRunTestReturnsZeroWhenScenarioMismatches(t *testing.T) {
addFakeRecordDependencies(t, "script", "bwrap", "bash")
t.Setenv("FAKE_SCRIPT_ECHO_STDIN", "1")
root := t.TempDir()
writeScenarioFixtures(t, filepath.Join(root, "e2e", "a"), "echo one\n", "echo one\n")
writeScenarioFixtures(t, filepath.Join(root, "e2e", "b"), "echo two\n", "different output\n")
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)
}
stdout, stderr := captureOutput(t, func() {
if got := Run([]string{"test"}); got != 0 {
t.Fatalf("Run() code = %d, want %d", got, 0)
}
})
for _, want := range []string{
"RUN a",
"PASS a (",
"RUN b",
"FAIL b (",
"output differed",
"Summary: total=2 passed=1 failed=1",
} {
if !strings.Contains(stdout, want) {
t.Fatalf("stdout = %q, want substring %q", stdout, want)
}
}
if stderr != "" {
t.Fatalf("stderr = %q, want empty", stderr)
}
})
}
func TestRunTestMissingConfigFails(t *testing.T) {
addFakeRecordDependencies(t, "script", "bwrap", "bash")
root := t.TempDir()
withWorkingDir(t, root, func() {
stdout, stderr := captureOutput(t, func() {
if got := Run([]string{"test"}); got != 1 {
t.Fatalf("Run() code = %d, want %d", got, 1)
}
})
if stdout != "" {
t.Fatalf("stdout = %q, want empty", stdout)
}
if !strings.Contains(stderr, "failed to resolve test directory") || !strings.Contains(stderr, "miro.toml") {
t.Fatalf("stderr = %q, want missing config error", stderr)
}
})
}
func TestRunTestFailsWhenRecorderShellMissing(t *testing.T) {
addFakeRecordDependencies(t, "script", "bwrap", "bash")
root := t.TempDir()
writeFile(t, filepath.Join(root, "miro.toml"), validConfigContent("e2e"))
withWorkingDir(t, root, func() {
stdout, stderr := captureOutput(t, func() {
if got := Run([]string{"test"}); got != 1 {
t.Fatalf("Run() code = %d, want %d", got, 1)
}
})
if stdout != "" {
t.Fatalf("stdout = %q, want empty", stdout)
}
if !strings.Contains(stderr, "rerun `miro init`") {
t.Fatalf("stderr = %q, want rerun init hint", stderr)
}
})
}
func TestRunTestFailsWhenFixtureMalformed(t *testing.T) {
addFakeRecordDependencies(t, "script", "bwrap", "bash")
root := t.TempDir()
writeFile(t, filepath.Join(root, "miro.toml"), validConfigContent("e2e"))
writeFile(t, filepath.Join(root, "e2e", "shell.sh"), "#!/bin/sh\n")
writeFile(t, filepath.Join(root, "e2e", "broken", "in"), "echo broken\n")
withWorkingDir(t, root, func() {
stdout, stderr := captureOutput(t, func() {
if got := Run([]string{"test"}); got != 1 {
t.Fatalf("Run() code = %d, want %d", got, 1)
}
})
if stdout != "" {
t.Fatalf("stdout = %q, want empty", stdout)
}
if !strings.Contains(stderr, `malformed scenario "broken": missing out fixture`) {
t.Fatalf("stderr = %q, want malformed fixture error", stderr)
}
})
}
func TestRunTestReturnsZeroWhenCompareMarkerMissing(t *testing.T) {
addFakeRecordDependencies(t, "script", "bwrap", "bash")
t.Setenv("FAKE_SCRIPT_ECHO_STDIN", "1")
root := t.TempDir()
writeFile(t, filepath.Join(root, "miro.toml"), validConfigContent("e2e"))
writeFile(t, filepath.Join(root, "e2e", "shell.sh"), "#!/bin/sh\n")
writeScenarioFixtures(t, filepath.Join(root, "e2e", "some"), "echo one\n", "echo one\n")
withWorkingDir(t, root, func() {
stdout, stderr := captureOutput(t, func() {
if got := Run([]string{"test"}); got != 0 {
t.Fatalf("Run() code = %d, want %d", got, 0)
}
})
for _, want := range []string{
"RUN some",
"FAIL some (",
"missing compare marker",
"Summary: total=1 passed=0 failed=1",
} {
if !strings.Contains(stdout, want) {
t.Fatalf("stdout = %q, want substring %q", stdout, want)
}
}
if stderr != "" {
t.Fatalf("stderr = %q, want empty", stderr)
}
})
}
func TestRunRecordMissingPath(t *testing.T) {
addFakeRecordDependencies(t, "script", "bwrap", "bash")
stdout, stderr := captureOutput(t, func() {
if got := Run([]string{"record"}); got != 1 {
t.Fatalf("Run() code = %d, want %d", got, 1)
}
})
if stdout != "" {
t.Fatalf("stdout = %q, want empty", stdout)
}
if !strings.Contains(stderr, "accepts 1 arg(s), received 0") {
t.Fatalf("stderr = %q, want argument error", stderr)
}
if !strings.Contains(stderr, "Usage:") || !strings.Contains(stderr, "miro record <path>") {
t.Fatalf("stderr = %q, want record usage", stderr)
}
}
func TestRunTestExtraArgs(t *testing.T) {
addFakeRecordDependencies(t, "script", "bwrap", "bash")
testutil.AddFakeRecordDependencies(t, "script", "bwrap", "bash")
stdout, stderr := captureOutput(t, func() {
stdout, stderr := testutil.CaptureOutput(t, func() {
if got := Run([]string{"test", "a", "b"}); got != 1 {
t.Fatalf("Run() code = %d, want %d", got, 1)
}
@@ -514,9 +218,9 @@ func TestRunTestExtraArgs(t *testing.T) {
}
func TestRunInitExtraArgs(t *testing.T) {
addFakeRecordDependencies(t, "script", "bwrap", "bash")
testutil.AddFakeRecordDependencies(t, "script", "bwrap", "bash")
stdout, stderr := captureOutput(t, func() {
stdout, stderr := testutil.CaptureOutput(t, func() {
if got := Run([]string{"init", "extra"}); got != 1 {
t.Fatalf("Run() code = %d, want %d", got, 1)
}
@@ -534,9 +238,9 @@ func TestRunInitExtraArgs(t *testing.T) {
}
func TestRunUnknownCommand(t *testing.T) {
addFakeRecordDependencies(t, "script", "bwrap", "bash")
testutil.AddFakeRecordDependencies(t, "script", "bwrap", "bash")
stdout, stderr := captureOutput(t, func() {
stdout, stderr := testutil.CaptureOutput(t, func() {
if got := Run([]string{"wat"}); got != 1 {
t.Fatalf("Run() code = %d, want %d", got, 1)
}
@@ -550,242 +254,6 @@ func TestRunUnknownCommand(t *testing.T) {
}
}
func captureOutput(t *testing.T, fn func()) (string, string) {
t.Helper()
t.Setenv("NO_COLOR", "1")
stdoutReader, stdoutWriter, err := os.Pipe()
if err != nil {
t.Fatalf("os.Pipe() stdout error = %v", err)
}
stderrReader, stderrWriter, err := os.Pipe()
if err != nil {
t.Fatalf("os.Pipe() stderr error = %v", err)
}
oldStdout := os.Stdout
oldStderr := os.Stderr
os.Stdout = stdoutWriter
os.Stderr = stderrWriter
t.Cleanup(func() {
os.Stdout = oldStdout
os.Stderr = oldStderr
})
fn()
if err := stdoutWriter.Close(); err != nil {
t.Fatalf("stdout close error = %v", err)
}
if err := stderrWriter.Close(); err != nil {
t.Fatalf("stderr close error = %v", err)
}
var stdoutBuf bytes.Buffer
if _, err := io.Copy(&stdoutBuf, stdoutReader); err != nil {
t.Fatalf("stdout copy error = %v", err)
}
var stderrBuf bytes.Buffer
if _, err := io.Copy(&stderrBuf, stderrReader); err != nil {
t.Fatalf("stderr copy error = %v", err)
}
return stdoutBuf.String(), stderrBuf.String()
}
func prefixed(msg string) string {
return "miro " + msg
}
func writeFile(t *testing.T, path, content string) {
t.Helper()
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
t.Fatalf("MkdirAll(%q) error = %v", filepath.Dir(path), err)
}
if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
t.Fatalf("WriteFile(%q) error = %v", path, err)
}
}
func writeScenarioFixtures(t *testing.T, dir, in, out string) {
t.Helper()
writeFile(t, filepath.Join(dir, "in"), in)
writeFile(t, filepath.Join(dir, "out"), out)
}
func mustReadFile(t *testing.T, path string) string {
t.Helper()
data, err := os.ReadFile(path)
if err != nil {
t.Fatalf("ReadFile(%q) error = %v", path, err)
}
return string(data)
}
func withWorkingDir(t *testing.T, dir string, fn func()) {
t.Helper()
wd, err := os.Getwd()
if err != nil {
t.Fatalf("Getwd() error = %v", err)
}
if err := os.Chdir(dir); err != nil {
t.Fatalf("Chdir(%q) error = %v", dir, err)
}
t.Cleanup(func() {
if err := os.Chdir(wd); err != nil {
t.Fatalf("restore working directory: %v", err)
}
})
fn()
}
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
if [ -n "${FAKE_SCRIPT_ARGS_FILE:-}" ]; then
: > "$FAKE_SCRIPT_ARGS_FILE"
for arg in "$@"; do
printf '%s\n' "$arg" >> "$FAKE_SCRIPT_ARGS_FILE"
done
fi
in=''
out=''
cmd=''
while [ "$#" -gt 0 ]; do
case "$1" in
-I)
in="$2"
shift 2
;;
-O)
out="$2"
shift 2
;;
-c)
cmd="$2"
shift 2
;;
*)
shift
;;
esac
done
if [ -n "${FAKE_SCRIPT_COMMAND_BODY_FILE:-}" ] && [ -n "$cmd" ]; then
: > "$FAKE_SCRIPT_COMMAND_BODY_FILE"
while IFS= read -r line || [ -n "$line" ]; do
printf '%s\n' "$line" >> "$FAKE_SCRIPT_COMMAND_BODY_FILE"
done < "$cmd"
fi
command_has_compare_marker=0
if [ -n "$cmd" ] && /bin/grep -q '__MIRO_E2E_BEGIN__' "$cmd" 2>/dev/null; then
command_has_compare_marker=1
fi
stdin_file=''
if [ "${FAKE_SCRIPT_ECHO_STDIN:-}" = "1" ] || [ -n "${FAKE_SCRIPT_CAPTURE_STDIN_FILE:-}" ]; then
stdin_file="${TMPDIR:-/tmp}/miro-fake-script-stdin-$$"
/bin/cat > "$stdin_file"
fi
if [ -n "${FAKE_SCRIPT_CAPTURE_STDIN_FILE:-}" ] && [ -n "$stdin_file" ]; then
/bin/cp "$stdin_file" "$FAKE_SCRIPT_CAPTURE_STDIN_FILE"
fi
if [ -n "$in" ] && [ -n "${FAKE_SCRIPT_LOG_IN+x}" ]; then
printf '%s' "$FAKE_SCRIPT_LOG_IN" > "$in"
elif [ -n "$in" ] && [ -n "$stdin_file" ]; then
/bin/cp "$stdin_file" "$in"
elif [ -n "$in" ]; then
printf '%s' 'fake recorded input
' > "$in"
fi
if [ -n "${FAKE_SCRIPT_STREAM_OUT+x}" ]; then
printf '%s' "$FAKE_SCRIPT_STREAM_OUT"
elif [ "${FAKE_SCRIPT_ECHO_STDIN:-}" = "1" ] && [ -n "$stdin_file" ]; then
/bin/cat "$stdin_file"
if [ "${MIRO_COMPARE_MARKER:-0}" = "1" ] && [ "$command_has_compare_marker" = "1" ]; then
printf '%s\n' '__MIRO_E2E_BEGIN__'
/bin/cat "$stdin_file"
fi
fi
if [ -n "${FAKE_SCRIPT_STREAM_ERR+x}" ]; then
printf '%s' "$FAKE_SCRIPT_STREAM_ERR" >&2
fi
if [ -n "$out" ] && [ -n "${FAKE_SCRIPT_LOG_OUT+x}" ]; then
printf '%s' "$FAKE_SCRIPT_LOG_OUT" > "$out"
elif [ -n "$out" ] && [ "${FAKE_SCRIPT_ECHO_STDIN:-}" = "1" ] && [ -n "$stdin_file" ]; then
{
printf '%s' 'Script started on 2026-03-18 11:13:38+00:00 [TERM="xterm-256color"]
'
/bin/cat "$stdin_file"
if [ "${MIRO_COMPARE_MARKER:-0}" = "1" ] && [ "$command_has_compare_marker" = "1" ]; then
printf '%s\n' '__MIRO_E2E_BEGIN__'
/bin/cat "$stdin_file"
fi
printf '%s' 'Script done on 2026-03-18 11:13:44+00:00 [COMMAND_EXIT_CODE="0"]
'
} > "$out"
elif [ -n "$out" ]; then
printf '%s' 'Script started on 2026-03-18 11:13:38+00:00 [TERM="xterm-256color"]
fake recorded output
Script done on 2026-03-18 11:13:44+00:00 [COMMAND_EXIT_CODE="0"]
' > "$out"
fi
if [ -n "$stdin_file" ]; then
/bin/rm -f "$stdin_file"
fi
if [ -n "${FAKE_SCRIPT_EXIT_CODE:-}" ]; then
exit "$FAKE_SCRIPT_EXIT_CODE"
fi
exit 0
`
}
if err := os.WriteFile(path, []byte(body), 0o755); err != nil {
t.Fatalf("WriteFile(%q) error = %v", path, err)
}
}
t.Setenv("PATH", binDir)
}
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()
}
func defaultWrittenConfig(testDir string) string {
return "[miro]\n test_dir = \"" + testDir + "\"\n\n[sandbox]\n visible_home = \"/home/test\"\n"
}
func validConfigContent(testDir string) string {
return "[miro]\ntest_dir = \"" + testDir + "\"\n\n[sandbox]\nvisible_home = \"/home/test\"\n"
}
+6 -4
View File
@@ -5,13 +5,15 @@ import (
"path/filepath"
"strings"
"testing"
"miro/internal/testutil"
)
func TestResolvePathWithinTestDirAcceptsRelativePath(t *testing.T) {
root := t.TempDir()
testDir := filepath.Join(root, "e2e")
got := withWorkingDir(t, root, func() string {
got := testutil.WithWorkingDir(t, root, func() string {
path, err := resolvePathWithinTestDir(testDir, filepath.Join("suite", "spec"), "record")
if err != nil {
t.Fatalf("resolvePathWithinTestDir() error = %v", err)
@@ -29,7 +31,7 @@ func TestResolvePathWithinTestDirAcceptsExplicitTestDirPrefix(t *testing.T) {
root := t.TempDir()
testDir := filepath.Join(root, "e2e")
got := withWorkingDir(t, root, func() string {
got := testutil.WithWorkingDir(t, root, func() string {
path, err := resolvePathWithinTestDir(testDir, filepath.Join("e2e", "suite", "spec"), "record")
if err != nil {
t.Fatalf("resolvePathWithinTestDir() error = %v", err)
@@ -74,9 +76,9 @@ func TestResolvePathWithinTestDirRejectsAbsolutePathOutsideTestDir(t *testing.T)
func TestResolvePathWithinTestDirAllowsPathCurrentDirectory(t *testing.T) {
root := t.TempDir()
testDir := filepath.Join(root, "e2e")
mustMkdirAll(t, testDir)
testutil.MustMkdirAll(t, testDir)
got := withWorkingDir(t, testDir, func() string {
got := testutil.WithWorkingDir(t, testDir, func() string {
path, err := resolvePathWithinTestDir(testDir, ".", "record")
if err != nil {
t.Fatalf("resolvePathWithinTestDir() error = %v", err)
+5 -3
View File
@@ -3,11 +3,13 @@ package miro
import (
"path/filepath"
"testing"
"miro/internal/testutil"
)
func TestLoadRecordedInputTrimsTrailingNewlineAfterEOF(t *testing.T) {
path := filepath.Join(t.TempDir(), "in")
writeFile(t, path, "echo hi\r"+string([]byte{eofByte})+"\n")
testutil.WriteFile(t, path, "echo hi\r"+string([]byte{eofByte})+"\n")
got, err := loadRecordedInput(path)
if err != nil {
@@ -22,7 +24,7 @@ func TestLoadRecordedInputTrimsTrailingNewlineAfterEOF(t *testing.T) {
func TestLoadRecordedInputTrimsTrailingNewlineAfterCarriageReturn(t *testing.T) {
path := filepath.Join(t.TempDir(), "in")
writeFile(t, path, "echo hi\rexit\r\n")
testutil.WriteFile(t, path, "echo hi\rexit\r\n")
got, err := loadRecordedInput(path)
if err != nil {
@@ -37,7 +39,7 @@ func TestLoadRecordedInputTrimsTrailingNewlineAfterCarriageReturn(t *testing.T)
func TestLoadRecordedInputLeavesNormalTrailingNewline(t *testing.T) {
path := filepath.Join(t.TempDir(), "in")
writeFile(t, path, "echo hi\n")
testutil.WriteFile(t, path, "echo hi\n")
got, err := loadRecordedInput(path)
if err != nil {
+30 -243
View File
@@ -4,23 +4,24 @@ import (
"bytes"
"errors"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"time"
"miro/internal/testutil"
)
func TestRecordCreatesRelativePath(t *testing.T) {
root := t.TempDir()
testDir := filepath.Join(root, "e2e")
mustMkdirAll(t, testDir)
writeFile(t, filepath.Join(root, "miro.toml"), validConfigContent("e2e"))
testutil.MustMkdirAll(t, testDir)
testutil.WriteFile(t, filepath.Join(root, "miro.toml"), testutil.ValidConfigContent("e2e"))
mustWriteRecordShell(t, testDir)
addFakeRecordDependencies(t, "script")
testutil.AddFakeRecordDependencies(t, "script")
got := withWorkingDir(t, root, func() string {
withStdin(t, "y\n", func() {})
got := testutil.WithWorkingDir(t, root, func() string {
testutil.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)
@@ -39,10 +40,10 @@ func TestRecordCreatesRelativePath(t *testing.T) {
}
}
if got := readFile(t, filepath.Join(want, "in")); got != "fake recorded input\n" {
if got := testutil.ReadFile(t, filepath.Join(want, "in")); got != "fake recorded input\n" {
t.Fatalf("saved in = %q, want %q", got, "fake recorded input\n")
}
if got := readFile(t, filepath.Join(want, "out")); got != "fake recorded output\n" {
if got := testutil.ReadFile(t, filepath.Join(want, "out")); got != "fake recorded output\n" {
t.Fatalf("saved out = %q, want %q", got, "fake recorded output\n")
}
}
@@ -50,13 +51,13 @@ func TestRecordCreatesRelativePath(t *testing.T) {
func TestRecordReturnsDiscardedErrorWhenSaveDeclined(t *testing.T) {
root := t.TempDir()
testDir := filepath.Join(root, "e2e")
mustMkdirAll(t, testDir)
testutil.MustMkdirAll(t, testDir)
mustWriteRecordShell(t, testDir)
addFakeRecordDependencies(t, "script")
testutil.AddFakeRecordDependencies(t, "script")
err := withWorkingDir(t, root, func() error {
err := testutil.WithWorkingDir(t, root, func() error {
target := filepath.Join(testDir, "a", "b", "c")
mustMkdirAll(t, target)
testutil.MustMkdirAll(t, target)
return withRecordStreams(t, "n\n", func(rio recordIO) error {
return recordScenario(target, recordShellPath(testDir), rio, defaultSandboxConfig())
})
@@ -78,13 +79,13 @@ func TestRecordReturnsDiscardedErrorWhenOverwriteDeclined(t *testing.T) {
root := t.TempDir()
testDir := filepath.Join(root, "e2e")
target := filepath.Join(testDir, "a", "b", "c")
mustMkdirAll(t, target)
testutil.MustMkdirAll(t, target)
mustWriteRecordShell(t, testDir)
addFakeRecordDependencies(t, "script")
writeFile(t, filepath.Join(target, "in"), "existing in\n")
writeFile(t, filepath.Join(target, "out"), "existing out\n")
testutil.AddFakeRecordDependencies(t, "script")
testutil.WriteFile(t, filepath.Join(target, "in"), "existing in\n")
testutil.WriteFile(t, filepath.Join(target, "out"), "existing out\n")
err := withWorkingDir(t, root, func() error {
err := testutil.WithWorkingDir(t, root, func() error {
return withRecordStreams(t, "n\n", func(rio recordIO) error {
return recordScenario(target, recordShellPath(testDir), rio, defaultSandboxConfig())
})
@@ -187,7 +188,7 @@ func TestRecordSessionEnvWithExtraIncludesAdditionalEntries(t *testing.T) {
func TestRunRecordSessionUsesSandboxedScriptCommand(t *testing.T) {
testDir := filepath.Join(t.TempDir(), "e2e")
mustWriteRecordShell(t, testDir)
addFakeRecordDependencies(t, "script")
testutil.AddFakeRecordDependencies(t, "script")
argsPath := filepath.Join(t.TempDir(), "script.args")
commandBodyPath := filepath.Join(t.TempDir(), "script.command")
@@ -208,7 +209,7 @@ func TestRunRecordSessionUsesSandboxedScriptCommand(t *testing.T) {
t.Fatalf("runRecordSession() error = %v", err)
}
args := strings.Split(strings.TrimSpace(readFile(t, argsPath)), "\n")
args := strings.Split(strings.TrimSpace(testutil.ReadFile(t, argsPath)), "\n")
if len(args) != 9 {
t.Fatalf("script args = %q, want 9 args", args)
}
@@ -225,7 +226,7 @@ func TestRunRecordSessionUsesSandboxedScriptCommand(t *testing.T) {
t.Fatalf("script args[8] = %q, want %q", args[8], shellPath)
}
body := readFile(t, commandBodyPath)
body := testutil.ReadFile(t, commandBodyPath)
for _, want := range []string{
"host_home=${MIRO_HOST_HOME:?}",
"visible_home=${MIRO_VISIBLE_HOME:?}",
@@ -244,12 +245,12 @@ func TestRunRecordSessionUsesSandboxedScriptCommand(t *testing.T) {
}
func TestRecordScenarioUsesDeterministicSandbox(t *testing.T) {
requireCommands(t, "script", "bwrap", "bash")
testutil.RequireCommands(t, "script", "bwrap", "bash")
root := t.TempDir()
testDir := filepath.Join(root, "e2e")
target := filepath.Join(testDir, "suite", "spec")
mustMkdirAll(t, target)
testutil.MustMkdirAll(t, target)
mustWriteRecordShell(t, testDir)
sandboxConfig := map[string]string{
"visible_home": "/home/test",
@@ -287,7 +288,7 @@ func TestRecordScenarioUsesDeterministicSandbox(t *testing.T) {
writeDone <- nil
}()
err = withWorkingDir(t, root, func() error {
err = testutil.WithWorkingDir(t, root, func() error {
return recordScenario(target, recordShellPath(testDir), recordIO{
in: reader,
out: ioDiscard{},
@@ -301,7 +302,7 @@ func TestRecordScenarioUsesDeterministicSandbox(t *testing.T) {
t.Fatalf("write session input: %v", err)
}
recordedIn := readFile(t, filepath.Join(target, "in"))
recordedIn := testutil.ReadFile(t, filepath.Join(target, "in"))
if strings.Contains(recordedIn, "Script started on ") {
t.Fatalf("saved in = %q, want stripped script wrapper", recordedIn)
}
@@ -311,7 +312,7 @@ func TestRecordScenarioUsesDeterministicSandbox(t *testing.T) {
}
}
recordedOut := readFile(t, filepath.Join(target, "out"))
recordedOut := testutil.ReadFile(t, filepath.Join(target, "out"))
if strings.Contains(recordedOut, "Script started on ") {
t.Fatalf("saved out = %q, want stripped script wrapper", recordedOut)
}
@@ -331,12 +332,12 @@ func TestRecordScenarioUsesDeterministicSandbox(t *testing.T) {
func TestRecordFailsWhenRecorderShellMissing(t *testing.T) {
root := t.TempDir()
testDir := filepath.Join(root, "e2e")
writeFile(t, filepath.Join(root, "miro.toml"), validConfigContent("e2e"))
mustMkdirAll(t, testDir)
addFakeRecordDependencies(t, "script")
testutil.WriteFile(t, filepath.Join(root, "miro.toml"), testutil.ValidConfigContent("e2e"))
testutil.MustMkdirAll(t, testDir)
testutil.AddFakeRecordDependencies(t, "script")
target := filepath.Join(testDir, "suite", "spec")
err := withWorkingDir(t, root, func() error {
err := testutil.WithWorkingDir(t, root, func() error {
_, err := Record(filepath.Join("suite", "spec"))
return err
})
@@ -350,217 +351,3 @@ func TestRecordFailsWhenRecorderShellMissing(t *testing.T) {
t.Fatalf("Stat(%q) error = %v, want not exists", target, statErr)
}
}
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()
}
func readFile(t *testing.T, path string) string {
t.Helper()
data, err := os.ReadFile(path)
if err != nil {
t.Fatalf("ReadFile(%q) error = %v", path, err)
}
return string(data)
}
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
if [ -n "${FAKE_SCRIPT_ARGS_FILE:-}" ]; then
: > "$FAKE_SCRIPT_ARGS_FILE"
for arg in "$@"; do
printf '%s\n' "$arg" >> "$FAKE_SCRIPT_ARGS_FILE"
done
fi
in=''
out=''
cmd=''
while [ "$#" -gt 0 ]; do
case "$1" in
-I)
in="$2"
shift 2
;;
-O)
out="$2"
shift 2
;;
-c)
cmd="$2"
shift 2
;;
*)
shift
;;
esac
done
if [ -n "${FAKE_SCRIPT_COMMAND_BODY_FILE:-}" ] && [ -n "$cmd" ]; then
: > "$FAKE_SCRIPT_COMMAND_BODY_FILE"
while IFS= read -r line || [ -n "$line" ]; do
printf '%s\n' "$line" >> "$FAKE_SCRIPT_COMMAND_BODY_FILE"
done < "$cmd"
fi
command_has_compare_marker=0
if [ -n "$cmd" ] && /bin/grep -q '__MIRO_E2E_BEGIN__' "$cmd" 2>/dev/null; then
command_has_compare_marker=1
fi
stdin_file=''
if [ "${FAKE_SCRIPT_ECHO_STDIN:-}" = "1" ] || [ -n "${FAKE_SCRIPT_CAPTURE_STDIN_FILE:-}" ]; then
stdin_file="${TMPDIR:-/tmp}/miro-fake-script-stdin-$$"
/bin/cat > "$stdin_file"
fi
if [ -n "${FAKE_SCRIPT_CAPTURE_STDIN_FILE:-}" ] && [ -n "$stdin_file" ]; then
/bin/cp "$stdin_file" "$FAKE_SCRIPT_CAPTURE_STDIN_FILE"
fi
if [ -n "$in" ] && [ -n "${FAKE_SCRIPT_LOG_IN+x}" ]; then
printf '%s' "$FAKE_SCRIPT_LOG_IN" > "$in"
elif [ -n "$in" ] && [ -n "$stdin_file" ]; then
/bin/cp "$stdin_file" "$in"
elif [ -n "$in" ]; then
printf '%s' 'fake recorded input
' > "$in"
fi
if [ -n "${FAKE_SCRIPT_STREAM_OUT+x}" ]; then
printf '%s' "$FAKE_SCRIPT_STREAM_OUT"
elif [ "${FAKE_SCRIPT_ECHO_STDIN:-}" = "1" ] && [ -n "$stdin_file" ]; then
/bin/cat "$stdin_file"
if [ "${MIRO_COMPARE_MARKER:-0}" = "1" ] && [ "$command_has_compare_marker" = "1" ]; then
printf '%s\n' '__MIRO_E2E_BEGIN__'
/bin/cat "$stdin_file"
fi
fi
if [ -n "${FAKE_SCRIPT_STREAM_ERR+x}" ]; then
printf '%s' "$FAKE_SCRIPT_STREAM_ERR" >&2
fi
if [ -n "$out" ] && [ -n "${FAKE_SCRIPT_LOG_OUT+x}" ]; then
printf '%s' "$FAKE_SCRIPT_LOG_OUT" > "$out"
elif [ -n "$out" ] && [ "${FAKE_SCRIPT_ECHO_STDIN:-}" = "1" ] && [ -n "$stdin_file" ]; then
{
printf '%s' 'Script started on 2026-03-18 11:13:38+00:00 [TERM="xterm-256color"]
'
/bin/cat "$stdin_file"
if [ "${MIRO_COMPARE_MARKER:-0}" = "1" ] && [ "$command_has_compare_marker" = "1" ]; then
printf '%s\n' '__MIRO_E2E_BEGIN__'
/bin/cat "$stdin_file"
fi
printf '%s' 'Script done on 2026-03-18 11:13:44+00:00 [COMMAND_EXIT_CODE="0"]
'
} > "$out"
elif [ -n "$out" ]; then
printf '%s' 'Script started on 2026-03-18 11:13:38+00:00 [TERM="xterm-256color"]
fake recorded output
Script done on 2026-03-18 11:13:44+00:00 [COMMAND_EXIT_CODE="0"]
' > "$out"
fi
if [ -n "$stdin_file" ]; then
/bin/rm -f "$stdin_file"
fi
if [ -n "${FAKE_SCRIPT_EXIT_CODE:-}" ]; then
exit "$FAKE_SCRIPT_EXIT_CODE"
fi
exit 0
`
}
if err := os.WriteFile(path, []byte(body), 0o755); err != nil {
t.Fatalf("WriteFile(%q) error = %v", path, err)
}
}
t.Setenv("PATH", binDir)
}
func requireCommands(t *testing.T, names ...string) {
t.Helper()
for _, name := range names {
if _, err := exec.LookPath(name); err != nil {
t.Skipf("missing command %q: %v", name, err)
}
}
}
func mustWriteRecordShell(t *testing.T, testDir string) {
t.Helper()
if err := writeRecordShell(testDir); err != nil {
t.Fatalf("writeRecordShell(%q) error = %v", testDir, err)
}
}
func defaultSandboxConfig() map[string]string {
return map[string]string{
"visible_home": "/home/test",
}
}
func containsEnvEntry(env []string, want string) bool {
for _, entry := range env {
if entry == want {
return true
}
}
return false
}
+21 -26
View File
@@ -6,6 +6,8 @@ import (
"strings"
"testing"
"time"
"miro/internal/testutil"
)
func TestFormatElapsed(t *testing.T) {
@@ -30,10 +32,10 @@ func TestFormatElapsed(t *testing.T) {
func TestDiscoverTestScenariosFindsNestedFixturesAndSorts(t *testing.T) {
testDir := filepath.Join(t.TempDir(), "e2e")
writeScenarioFixtures(t, filepath.Join(testDir, "nested", "b"), "echo two\n", "echo two\n")
writeScenarioFixtures(t, filepath.Join(testDir, "a"), "echo one\n", "echo one\n")
writeFile(t, filepath.Join(testDir, "shell.sh"), "#!/bin/sh\n")
writeFile(t, filepath.Join(testDir, "notes.txt"), "ignore me\n")
testutil.WriteScenarioFixtures(t, filepath.Join(testDir, "nested", "b"), "echo two\n", "echo two\n")
testutil.WriteScenarioFixtures(t, filepath.Join(testDir, "a"), "echo one\n", "echo one\n")
testutil.WriteFile(t, filepath.Join(testDir, "shell.sh"), "#!/bin/sh\n")
testutil.WriteFile(t, filepath.Join(testDir, "notes.txt"), "ignore me\n")
got, err := discoverTestScenarios(testDir, testDir)
if err != nil {
@@ -63,7 +65,7 @@ func TestDiscoverTestScenariosFindsNestedFixturesAndSorts(t *testing.T) {
func TestDiscoverTestScenariosRejectsMissingOutFixture(t *testing.T) {
testDir := filepath.Join(t.TempDir(), "e2e")
writeFile(t, filepath.Join(testDir, "broken", "in"), "echo broken\n")
testutil.WriteFile(t, filepath.Join(testDir, "broken", "in"), "echo broken\n")
_, err := discoverTestScenarios(testDir, testDir)
if err == nil {
@@ -76,7 +78,7 @@ func TestDiscoverTestScenariosRejectsMissingOutFixture(t *testing.T) {
func TestDiscoverTestScenariosRejectsMissingInFixture(t *testing.T) {
testDir := filepath.Join(t.TempDir(), "e2e")
writeFile(t, filepath.Join(testDir, "broken", "out"), "echo broken\n")
testutil.WriteFile(t, filepath.Join(testDir, "broken", "out"), "echo broken\n")
_, err := discoverTestScenarios(testDir, testDir)
if err == nil {
@@ -88,14 +90,14 @@ func TestDiscoverTestScenariosRejectsMissingInFixture(t *testing.T) {
}
func TestReplayScenarioUsesRecordedInputAndKeepsReplayOutputQuiet(t *testing.T) {
addFakeRecordDependencies(t, "script")
testutil.AddFakeRecordDependencies(t, "script")
t.Setenv("FAKE_SCRIPT_ECHO_STDIN", "1")
testDir := filepath.Join(t.TempDir(), "e2e")
shellPath := filepath.Join(testDir, "shell.sh")
mustWriteRecordShell(t, testDir)
scenarioDir := filepath.Join(testDir, "suite", "spec")
writeScenarioFixtures(t, scenarioDir, "echo replay\nexit\n", "echo replay\nexit\n")
testutil.WriteScenarioFixtures(t, scenarioDir, "echo replay\nexit\n", "echo replay\nexit\n")
capturedInput := filepath.Join(t.TempDir(), "stdin.capture")
t.Setenv("FAKE_SCRIPT_CAPTURE_STDIN_FILE", capturedInput)
@@ -115,7 +117,7 @@ func TestReplayScenarioUsesRecordedInputAndKeepsReplayOutputQuiet(t *testing.T)
t.Fatalf("replayScenario() error = %v", err)
}
if got := readFile(t, capturedInput); got != "echo replay\nexit\n" {
if got := testutil.ReadFile(t, capturedInput); got != "echo replay\nexit\n" {
t.Fatalf("captured stdin = %q, want recorded input", got)
}
if stdout.String() != "" {
@@ -127,14 +129,14 @@ func TestReplayScenarioUsesRecordedInputAndKeepsReplayOutputQuiet(t *testing.T)
}
func TestReplayScenarioFailsWhenCompareMarkerMissing(t *testing.T) {
addFakeRecordDependencies(t, "script")
testutil.AddFakeRecordDependencies(t, "script")
t.Setenv("FAKE_SCRIPT_ECHO_STDIN", "1")
testDir := filepath.Join(t.TempDir(), "e2e")
shellPath := filepath.Join(testDir, "shell.sh")
writeFile(t, shellPath, "#!/bin/sh\n")
testutil.WriteFile(t, shellPath, "#!/bin/sh\n")
scenarioDir := filepath.Join(testDir, "suite", "spec")
writeScenarioFixtures(t, scenarioDir, "echo replay\nexit\n", "echo replay\nexit\n")
testutil.WriteScenarioFixtures(t, scenarioDir, "echo replay\nexit\n", "echo replay\nexit\n")
err := replayScenario(testScenario{
dir: scenarioDir,
@@ -156,7 +158,7 @@ func TestReplayScenarioFailsWhenCompareMarkerMissing(t *testing.T) {
func TestDiscoverTestScenariosUsesDisplayRootForRelativePaths(t *testing.T) {
testDir := filepath.Join(t.TempDir(), "e2e")
scopedDir := filepath.Join(testDir, "nested")
writeScenarioFixtures(t, filepath.Join(scopedDir, "b"), "echo two\n", "echo two\n")
testutil.WriteScenarioFixtures(t, filepath.Join(scopedDir, "b"), "echo two\n", "echo two\n")
got, err := discoverTestScenarios(scopedDir, testDir)
if err != nil {
@@ -172,7 +174,7 @@ func TestDiscoverTestScenariosUsesDisplayRootForRelativePaths(t *testing.T) {
func TestResolveTestDiscoveryRootRejectsMissingPath(t *testing.T) {
testDir := filepath.Join(t.TempDir(), "e2e")
mustMkdirAll(t, testDir)
testutil.MustMkdirAll(t, testDir)
_, err := resolveTestDiscoveryRoot(testDir, "missing")
if err == nil {
@@ -186,9 +188,9 @@ func TestResolveTestDiscoveryRootRejectsMissingPath(t *testing.T) {
func TestResolveTestDiscoveryRootRejectsFile(t *testing.T) {
root := t.TempDir()
testDir := filepath.Join(root, "e2e")
writeFile(t, filepath.Join(testDir, "case.txt"), "hello\n")
testutil.WriteFile(t, filepath.Join(testDir, "case.txt"), "hello\n")
err := withWorkingDir(t, root, func() error {
err := testutil.WithWorkingDir(t, root, func() error {
_, err := resolveTestDiscoveryRoot(testDir, filepath.Join("e2e", "case.txt"))
return err
})
@@ -204,11 +206,11 @@ func TestRunTestsScopedRunEmptyDirectoryFails(t *testing.T) {
root := t.TempDir()
testDir := filepath.Join(root, "e2e")
emptyDir := filepath.Join(testDir, "empty")
writeFile(t, filepath.Join(root, "miro.toml"), validConfigContent("e2e"))
testutil.WriteFile(t, filepath.Join(root, "miro.toml"), testutil.ValidConfigContent("e2e"))
mustWriteRecordShell(t, testDir)
mustMkdirAll(t, emptyDir)
testutil.MustMkdirAll(t, emptyDir)
err := withWorkingDir(t, root, func() error {
err := testutil.WithWorkingDir(t, root, func() error {
return runTests("empty", testIO{
out: &bytes.Buffer{},
err: &bytes.Buffer{},
@@ -221,10 +223,3 @@ func TestRunTestsScopedRunEmptyDirectoryFails(t *testing.T) {
t.Fatalf("runTests() error = %q, want empty-directory error", err.Error())
}
}
func writeScenarioFixtures(t *testing.T, dir, in, out string) {
t.Helper()
writeFile(t, filepath.Join(dir, "in"), in)
writeFile(t, filepath.Join(dir, "out"), out)
}
+39 -109
View File
@@ -2,36 +2,37 @@ package miro
import (
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"miro/internal/testutil"
)
func TestInitCreatesConfigAtProjectRoot(t *testing.T) {
root := t.TempDir()
withWorkingDir(t, root, func() struct{} {
testutil.WithWorkingDir(t, root, func() struct{} {
if err := Init(); err != nil {
t.Fatalf("Init() error = %v", err)
}
return struct{}{}
})
got := readFile(t, filepath.Join(root, "miro.toml"))
if got != defaultWrittenConfig("e2e") {
t.Fatalf("config = %q, want %q", got, defaultWrittenConfig("e2e"))
got := testutil.ReadFile(t, filepath.Join(root, "miro.toml"))
if got != testutil.DefaultWrittenConfig("e2e") {
t.Fatalf("config = %q, want %q", got, testutil.DefaultWrittenConfig("e2e"))
}
assertRecordShell(t, filepath.Join(root, "e2e", recordShellName))
}
func TestInitUsesGitRoot(t *testing.T) {
root := t.TempDir()
mustGitInit(t, root)
testutil.MustGitInit(t, root)
subdir := filepath.Join(root, "nested", "dir")
mustMkdirAll(t, subdir)
testutil.MustMkdirAll(t, subdir)
withWorkingDir(t, subdir, func() struct{} {
testutil.WithWorkingDir(t, subdir, func() struct{} {
if err := Init(); err != nil {
t.Fatalf("Init() error = %v", err)
}
@@ -46,30 +47,30 @@ func TestInitUsesGitRoot(t *testing.T) {
func TestInitLeavesExistingValidConfigUntouchedAndRefreshesShell(t *testing.T) {
root := t.TempDir()
writeFile(t, filepath.Join(root, "miro.toml"), validConfigContent("custom/suite"))
writeFile(t, filepath.Join(root, "custom", "suite", recordShellName), "outdated\n")
testutil.WriteFile(t, filepath.Join(root, "miro.toml"), testutil.ValidConfigContent("custom/suite"))
testutil.WriteFile(t, filepath.Join(root, "custom", "suite", recordShellName), "outdated\n")
withWorkingDir(t, root, func() struct{} {
testutil.WithWorkingDir(t, root, func() struct{} {
if err := Init(); err != nil {
t.Fatalf("Init() error = %v", err)
}
return struct{}{}
})
got := readFile(t, filepath.Join(root, "miro.toml"))
if got != validConfigContent("custom/suite") {
t.Fatalf("config = %q, want %q", got, validConfigContent("custom/suite"))
got := testutil.ReadFile(t, filepath.Join(root, "miro.toml"))
if got != testutil.ValidConfigContent("custom/suite") {
t.Fatalf("config = %q, want %q", got, testutil.ValidConfigContent("custom/suite"))
}
if got := readFile(t, filepath.Join(root, "custom", "suite", recordShellName)); got != buildRecordShellScript() {
if got := testutil.ReadFile(t, filepath.Join(root, "custom", "suite", recordShellName)); got != buildRecordShellScript() {
t.Fatalf("shell = %q, want refreshed recorder shell", got)
}
}
func TestInitCreatesMissingConfiguredTestDir(t *testing.T) {
root := t.TempDir()
writeFile(t, filepath.Join(root, "miro.toml"), validConfigContent("custom/suite"))
testutil.WriteFile(t, filepath.Join(root, "miro.toml"), testutil.ValidConfigContent("custom/suite"))
withWorkingDir(t, root, func() struct{} {
testutil.WithWorkingDir(t, root, func() struct{} {
if err := Init(); err != nil {
t.Fatalf("Init() error = %v", err)
}
@@ -81,9 +82,9 @@ func TestInitCreatesMissingConfiguredTestDir(t *testing.T) {
func TestInitFailsWhenExistingConfigInvalid(t *testing.T) {
root := t.TempDir()
writeFile(t, filepath.Join(root, "miro.toml"), "test_dir = \"e2e\"\n")
testutil.WriteFile(t, filepath.Join(root, "miro.toml"), "test_dir = \"e2e\"\n")
err := withWorkingDir(t, root, func() error {
err := testutil.WithWorkingDir(t, root, func() error {
return Init()
})
if err == nil {
@@ -97,10 +98,10 @@ func TestInitFailsWhenExistingConfigInvalid(t *testing.T) {
func TestResolveTestDirFromConfig(t *testing.T) {
root := t.TempDir()
configured := filepath.Join(root, "custom", "suite")
mustMkdirAll(t, configured)
writeFile(t, filepath.Join(root, "miro.toml"), validConfigContent("custom/suite"))
testutil.MustMkdirAll(t, configured)
testutil.WriteFile(t, filepath.Join(root, "miro.toml"), testutil.ValidConfigContent("custom/suite"))
got := withWorkingDir(t, root, func() string {
got := testutil.WithWorkingDir(t, root, func() string {
path, err := ResolveTestDir()
if err != nil {
t.Fatalf("ResolveTestDir() error = %v", err)
@@ -116,7 +117,7 @@ func TestResolveTestDirFromConfig(t *testing.T) {
func TestResolveTestDirMissingConfigFails(t *testing.T) {
root := t.TempDir()
err := withWorkingDir(t, root, func() error {
err := testutil.WithWorkingDir(t, root, func() error {
_, err := ResolveTestDir()
return err
})
@@ -131,9 +132,9 @@ func TestResolveTestDirMissingConfigFails(t *testing.T) {
func TestResolveTestDirMissingTestDirFails(t *testing.T) {
root := t.TempDir()
writeFile(t, filepath.Join(root, "miro.toml"), "[miro]\n")
testutil.WriteFile(t, filepath.Join(root, "miro.toml"), "[miro]\n")
err := withWorkingDir(t, root, func() error {
err := testutil.WithWorkingDir(t, root, func() error {
_, err := ResolveTestDir()
return err
})
@@ -148,9 +149,9 @@ func TestResolveTestDirMissingTestDirFails(t *testing.T) {
func TestResolveTestDirEmptyTestDirFails(t *testing.T) {
root := t.TempDir()
writeFile(t, filepath.Join(root, "miro.toml"), "[miro]\ntest_dir = \"\"\n")
testutil.WriteFile(t, filepath.Join(root, "miro.toml"), "[miro]\ntest_dir = \"\"\n")
err := withWorkingDir(t, root, func() error {
err := testutil.WithWorkingDir(t, root, func() error {
_, err := ResolveTestDir()
return err
})
@@ -165,9 +166,9 @@ func TestResolveTestDirEmptyTestDirFails(t *testing.T) {
func TestResolveTestDirMalformedConfigFails(t *testing.T) {
root := t.TempDir()
writeFile(t, filepath.Join(root, "miro.toml"), "[miro]\ntest_dir = [\n")
testutil.WriteFile(t, filepath.Join(root, "miro.toml"), "[miro]\ntest_dir = [\n")
err := withWorkingDir(t, root, func() error {
err := testutil.WithWorkingDir(t, root, func() error {
_, err := ResolveTestDir()
return err
})
@@ -183,9 +184,9 @@ func TestResolveTestDirMalformedConfigFails(t *testing.T) {
func TestResolveTestDirConfiguredMissingDirectoryReturnsConfiguredPath(t *testing.T) {
root := t.TempDir()
want := filepath.Join(root, "missing")
writeFile(t, filepath.Join(root, "miro.toml"), validConfigContent("missing"))
testutil.WriteFile(t, filepath.Join(root, "miro.toml"), testutil.ValidConfigContent("missing"))
got := withWorkingDir(t, root, func() string {
got := testutil.WithWorkingDir(t, root, func() string {
path, err := ResolveTestDir()
if err != nil {
t.Fatalf("ResolveTestDir() error = %v", err)
@@ -200,10 +201,10 @@ func TestResolveTestDirConfiguredMissingDirectoryReturnsConfiguredPath(t *testin
func TestResolveTestDirConfiguredFileFails(t *testing.T) {
root := t.TempDir()
writeFile(t, filepath.Join(root, "case.txt"), "hello\n")
writeFile(t, filepath.Join(root, "miro.toml"), validConfigContent("case.txt"))
testutil.WriteFile(t, filepath.Join(root, "case.txt"), "hello\n")
testutil.WriteFile(t, filepath.Join(root, "miro.toml"), testutil.ValidConfigContent("case.txt"))
err := withWorkingDir(t, root, func() error {
err := testutil.WithWorkingDir(t, root, func() error {
_, err := ResolveTestDir()
return err
})
@@ -218,13 +219,13 @@ func TestResolveTestDirConfiguredFileFails(t *testing.T) {
func TestResolveTestDirUsesGitRoot(t *testing.T) {
root := t.TempDir()
mustGitInit(t, root)
testutil.MustGitInit(t, root)
want := filepath.Join(root, "e2e")
writeFile(t, filepath.Join(root, "miro.toml"), validConfigContent("e2e"))
testutil.WriteFile(t, filepath.Join(root, "miro.toml"), testutil.ValidConfigContent("e2e"))
subdir := filepath.Join(root, "nested", "dir")
mustMkdirAll(t, subdir)
testutil.MustMkdirAll(t, subdir)
got := withWorkingDir(t, subdir, func() string {
got := testutil.WithWorkingDir(t, subdir, func() string {
path, err := ResolveTestDir()
if err != nil {
t.Fatalf("ResolveTestDir() error = %v", err)
@@ -236,74 +237,3 @@ func TestResolveTestDirUsesGitRoot(t *testing.T) {
t.Fatalf("ResolveTestDir() = %q, want %q", got, want)
}
}
func withWorkingDir[T any](t *testing.T, dir string, fn func() T) T {
t.Helper()
wd, err := os.Getwd()
if err != nil {
t.Fatalf("Getwd() error = %v", err)
}
if err := os.Chdir(dir); err != nil {
t.Fatalf("Chdir(%q) error = %v", dir, err)
}
t.Cleanup(func() {
if err := os.Chdir(wd); err != nil {
t.Fatalf("restore working directory: %v", err)
}
})
return fn()
}
func writeFile(t *testing.T, path, content string) {
t.Helper()
mustMkdirAll(t, filepath.Dir(path))
if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
t.Fatalf("WriteFile(%q) error = %v", path, err)
}
}
func mustMkdirAll(t *testing.T, path string) {
t.Helper()
if err := os.MkdirAll(path, 0o755); err != nil {
t.Fatalf("MkdirAll(%q) error = %v", path, err)
}
}
func mustGitInit(t *testing.T, dir string) {
t.Helper()
cmd := exec.Command("git", "init")
cmd.Dir = dir
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("git init: %v\n%s", err, out)
}
}
func assertRecordShell(t *testing.T, path string) {
t.Helper()
info, err := os.Stat(path)
if err != nil {
t.Fatalf("Stat(%q) error = %v", path, err)
}
if info.Mode().Perm()&0o111 == 0 {
t.Fatalf("%q mode = %o, want executable", path, info.Mode().Perm())
}
if got := readFile(t, path); got != buildRecordShellScript() {
t.Fatalf("shell = %q, want generated recorder shell", got)
}
}
func defaultWrittenConfig(testDir string) string {
return "[miro]\n test_dir = \"" + testDir + "\"\n\n[sandbox]\n visible_home = \"/home/test\"\n"
}
func validConfigContent(testDir string) string {
return "[miro]\ntest_dir = \"" + testDir + "\"\n\n[sandbox]\nvisible_home = \"/home/test\"\n"
}
+80
View File
@@ -0,0 +1,80 @@
package miro
import (
"bytes"
"os"
"path/filepath"
"testing"
"miro/internal/testutil"
)
func assertRecordShell(t *testing.T, path string) {
t.Helper()
info, err := os.Stat(path)
if err != nil {
t.Fatalf("Stat(%q) error = %v", path, err)
}
if info.Mode().Perm()&0o111 == 0 {
t.Fatalf("%q mode = %o, want executable", path, info.Mode().Perm())
}
if got := testutil.ReadFile(t, path); got != buildRecordShellScript() {
t.Fatalf("shell = %q, want generated recorder shell", got)
}
}
func withRecordStreams[T any](t *testing.T, input string, fn func(recordIO) T) T {
t.Helper()
path := filepath.Join(t.TempDir(), "stdin.txt")
testutil.WriteFile(t, path, input)
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{},
})
}
type ioDiscard struct{}
func (ioDiscard) Write(p []byte) (int, error) {
return len(p), nil
}
func mustWriteRecordShell(t *testing.T, testDir string) {
t.Helper()
if err := writeRecordShell(testDir); err != nil {
t.Fatalf("writeRecordShell(%q) error = %v", testDir, err)
}
}
func defaultSandboxConfig() map[string]string {
return map[string]string{
"visible_home": "/home/test",
}
}
func containsEnvEntry(env []string, want string) bool {
for _, entry := range env {
if entry == want {
return true
}
}
return false
}
+273
View File
@@ -0,0 +1,273 @@
package testutil
import (
"bytes"
"io"
"os"
"os/exec"
"path/filepath"
"testing"
)
func CaptureOutput(t *testing.T, fn func()) (string, string) {
t.Helper()
t.Setenv("NO_COLOR", "1")
stdoutReader, stdoutWriter, err := os.Pipe()
if err != nil {
t.Fatalf("os.Pipe() stdout error = %v", err)
}
stderrReader, stderrWriter, err := os.Pipe()
if err != nil {
t.Fatalf("os.Pipe() stderr error = %v", err)
}
oldStdout := os.Stdout
oldStderr := os.Stderr
os.Stdout = stdoutWriter
os.Stderr = stderrWriter
t.Cleanup(func() {
os.Stdout = oldStdout
os.Stderr = oldStderr
})
fn()
if err := stdoutWriter.Close(); err != nil {
t.Fatalf("stdout close error = %v", err)
}
if err := stderrWriter.Close(); err != nil {
t.Fatalf("stderr close error = %v", err)
}
var stdoutBuf bytes.Buffer
if _, err := io.Copy(&stdoutBuf, stdoutReader); err != nil {
t.Fatalf("stdout copy error = %v", err)
}
var stderrBuf bytes.Buffer
if _, err := io.Copy(&stderrBuf, stderrReader); err != nil {
t.Fatalf("stderr copy error = %v", err)
}
return stdoutBuf.String(), stderrBuf.String()
}
func WriteFile(t *testing.T, path, content string) {
t.Helper()
MustMkdirAll(t, filepath.Dir(path))
if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
t.Fatalf("WriteFile(%q) error = %v", path, err)
}
}
func WriteScenarioFixtures(t *testing.T, dir, in, out string) {
t.Helper()
WriteFile(t, filepath.Join(dir, "in"), in)
WriteFile(t, filepath.Join(dir, "out"), out)
}
func ReadFile(t *testing.T, path string) string {
t.Helper()
data, err := os.ReadFile(path)
if err != nil {
t.Fatalf("ReadFile(%q) error = %v", path, err)
}
return string(data)
}
func MustMkdirAll(t *testing.T, path string) {
t.Helper()
if err := os.MkdirAll(path, 0o755); err != nil {
t.Fatalf("MkdirAll(%q) error = %v", path, err)
}
}
func WithWorkingDir[T any](t *testing.T, dir string, fn func() T) T {
t.Helper()
wd, err := os.Getwd()
if err != nil {
t.Fatalf("Getwd() error = %v", err)
}
if err := os.Chdir(dir); err != nil {
t.Fatalf("Chdir(%q) error = %v", dir, err)
}
t.Cleanup(func() {
if err := os.Chdir(wd); err != nil {
t.Fatalf("restore working directory: %v", err)
}
})
return fn()
}
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()
}
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
if [ -n "${FAKE_SCRIPT_ARGS_FILE:-}" ]; then
: > "$FAKE_SCRIPT_ARGS_FILE"
for arg in "$@"; do
printf '%s\n' "$arg" >> "$FAKE_SCRIPT_ARGS_FILE"
done
fi
in=''
out=''
cmd=''
while [ "$#" -gt 0 ]; do
case "$1" in
-I)
in="$2"
shift 2
;;
-O)
out="$2"
shift 2
;;
-c)
cmd="$2"
shift 2
;;
*)
shift
;;
esac
done
if [ -n "${FAKE_SCRIPT_COMMAND_BODY_FILE:-}" ] && [ -n "$cmd" ]; then
: > "$FAKE_SCRIPT_COMMAND_BODY_FILE"
while IFS= read -r line || [ -n "$line" ]; do
printf '%s\n' "$line" >> "$FAKE_SCRIPT_COMMAND_BODY_FILE"
done < "$cmd"
fi
command_has_compare_marker=0
if [ -n "$cmd" ] && /bin/grep -q '__MIRO_E2E_BEGIN__' "$cmd" 2>/dev/null; then
command_has_compare_marker=1
fi
stdin_file=''
if [ "${FAKE_SCRIPT_ECHO_STDIN:-}" = "1" ] || [ -n "${FAKE_SCRIPT_CAPTURE_STDIN_FILE:-}" ]; then
stdin_file="${TMPDIR:-/tmp}/miro-fake-script-stdin-$$"
/bin/cat > "$stdin_file"
fi
if [ -n "${FAKE_SCRIPT_CAPTURE_STDIN_FILE:-}" ] && [ -n "$stdin_file" ]; then
/bin/cp "$stdin_file" "$FAKE_SCRIPT_CAPTURE_STDIN_FILE"
fi
if [ -n "$in" ] && [ -n "${FAKE_SCRIPT_LOG_IN+x}" ]; then
printf '%s' "$FAKE_SCRIPT_LOG_IN" > "$in"
elif [ -n "$in" ] && [ -n "$stdin_file" ]; then
/bin/cp "$stdin_file" "$in"
elif [ -n "$in" ]; then
printf '%s' 'fake recorded input
' > "$in"
fi
if [ -n "${FAKE_SCRIPT_STREAM_OUT+x}" ]; then
printf '%s' "$FAKE_SCRIPT_STREAM_OUT"
elif [ "${FAKE_SCRIPT_ECHO_STDIN:-}" = "1" ] && [ -n "$stdin_file" ]; then
/bin/cat "$stdin_file"
if [ "${MIRO_COMPARE_MARKER:-0}" = "1" ] && [ "$command_has_compare_marker" = "1" ]; then
printf '%s\n' '__MIRO_E2E_BEGIN__'
/bin/cat "$stdin_file"
fi
fi
if [ -n "${FAKE_SCRIPT_STREAM_ERR+x}" ]; then
printf '%s' "$FAKE_SCRIPT_STREAM_ERR" >&2
fi
if [ -n "$out" ] && [ -n "${FAKE_SCRIPT_LOG_OUT+x}" ]; then
printf '%s' "$FAKE_SCRIPT_LOG_OUT" > "$out"
elif [ -n "$out" ] && [ "${FAKE_SCRIPT_ECHO_STDIN:-}" = "1" ] && [ -n "$stdin_file" ]; then
{
printf '%s' 'Script started on 2026-03-18 11:13:38+00:00 [TERM="xterm-256color"]
'
/bin/cat "$stdin_file"
if [ "${MIRO_COMPARE_MARKER:-0}" = "1" ] && [ "$command_has_compare_marker" = "1" ]; then
printf '%s\n' '__MIRO_E2E_BEGIN__'
/bin/cat "$stdin_file"
fi
printf '%s' 'Script done on 2026-03-18 11:13:44+00:00 [COMMAND_EXIT_CODE="0"]
'
} > "$out"
elif [ -n "$out" ]; then
printf '%s' 'Script started on 2026-03-18 11:13:38+00:00 [TERM="xterm-256color"]
fake recorded output
Script done on 2026-03-18 11:13:44+00:00 [COMMAND_EXIT_CODE="0"]
' > "$out"
fi
if [ -n "$stdin_file" ]; then
/bin/rm -f "$stdin_file"
fi
if [ -n "${FAKE_SCRIPT_EXIT_CODE:-}" ]; then
exit "$FAKE_SCRIPT_EXIT_CODE"
fi
exit 0
`
}
if err := os.WriteFile(path, []byte(body), 0o755); err != nil {
t.Fatalf("WriteFile(%q) error = %v", path, err)
}
}
t.Setenv("PATH", binDir)
}
func RequireCommands(t *testing.T, names ...string) {
t.Helper()
for _, name := range names {
if _, err := exec.LookPath(name); err != nil {
t.Skipf("missing command %q: %v", name, err)
}
}
}
func MustGitInit(t *testing.T, dir string) {
t.Helper()
cmd := exec.Command("git", "init")
cmd.Dir = dir
out, err := cmd.CombinedOutput()
if err != nil {
t.Fatalf("git init: %v\n%s", err, out)
}
}
func DefaultWrittenConfig(testDir string) string {
return "[miro]\n test_dir = \"" + testDir + "\"\n\n[sandbox]\n visible_home = \"/home/test\"\n"
}
func ValidConfigContent(testDir string) string {
return "[miro]\ntest_dir = \"" + testDir + "\"\n\n[sandbox]\nvisible_home = \"/home/test\"\n"
}