feat: allow for setup.sh based user fixtures
This commit is contained in:
@@ -37,11 +37,16 @@ func Record(path string) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupScripts, err := discoverSetupScripts(testDir, target)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
if err := recordScenario(target, shellPath, recordIO{
|
if err := recordScenario(target, shellPath, recordIO{
|
||||||
in: os.Stdin,
|
in: os.Stdin,
|
||||||
out: os.Stdout,
|
out: os.Stdout,
|
||||||
err: os.Stderr,
|
err: os.Stderr,
|
||||||
}, cfg.Sandbox); err != nil {
|
}, cfg.Sandbox, setupScripts); err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ type recordIO struct {
|
|||||||
err io.Writer
|
err io.Writer
|
||||||
}
|
}
|
||||||
|
|
||||||
func recordScenario(target, shellPath string, rio recordIO, sandboxConfig map[string]string) error {
|
func recordScenario(target, shellPath string, rio recordIO, sandboxConfig map[string]string, setupScripts []string) error {
|
||||||
rawIn, rawOut, cleanup, err := newRecordFiles()
|
rawIn, rawOut, cleanup, err := newRecordFiles()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -40,7 +40,7 @@ func recordScenario(target, shellPath string, rio recordIO, sandboxConfig map[st
|
|||||||
|
|
||||||
output.Fprintln(rio.err, "Run commands in the recorder shell, then type exit to finish.")
|
output.Fprintln(rio.err, "Run commands in the recorder shell, then type exit to finish.")
|
||||||
|
|
||||||
if err := runRecordSession(target, rawIn, rawOut, shellPath, sandbox, rio, sandboxConfig); err != nil {
|
if err := runRecordSession(target, rawIn, rawOut, shellPath, sandbox, rio, sandboxConfig, setupScripts); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -119,10 +119,10 @@ func newRecordSandboxForPathEnv(pathEnv string) (recordSandbox, func(), error) {
|
|||||||
return sandbox, cleanup, nil
|
return sandbox, cleanup, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runRecordSession(dir, rawIn, rawOut, shellPath string, sandbox recordSandbox, rio recordIO, sandboxConfig map[string]string) error {
|
func runRecordSession(dir, rawIn, rawOut, shellPath string, sandbox recordSandbox, rio recordIO, sandboxConfig map[string]string, setupScripts []string) error {
|
||||||
cmd := exec.Command("script", "-q", "-E", "always", "-I", rawIn, "-O", rawOut, "-c", shellPath)
|
cmd := exec.Command("script", "-q", "-E", "always", "-I", rawIn, "-O", rawOut, "-c", shellPath)
|
||||||
cmd.Dir = dir
|
cmd.Dir = dir
|
||||||
cmd.Env = recordSessionEnv(sandbox, sandboxConfig)
|
cmd.Env = recordSessionEnv(sandbox, sandboxConfig, setupScripts)
|
||||||
cmd.Stdin = rio.in
|
cmd.Stdin = rio.in
|
||||||
cmd.Stdout = rio.out
|
cmd.Stdout = rio.out
|
||||||
cmd.Stderr = rio.err
|
cmd.Stderr = rio.err
|
||||||
|
|||||||
@@ -79,16 +79,17 @@ func buildRecordShellScript() string {
|
|||||||
return string(body)
|
return string(body)
|
||||||
}
|
}
|
||||||
|
|
||||||
func recordSessionEnv(sandbox recordSandbox, sandboxConfig map[string]string) []string {
|
func recordSessionEnv(sandbox recordSandbox, sandboxConfig map[string]string, setupScripts []string) []string {
|
||||||
return recordSessionEnvWithExtra(sandbox, sandboxConfig, nil)
|
return recordSessionEnvWithExtra(sandbox, sandboxConfig, setupScripts, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func recordSessionEnvWithExtra(sandbox recordSandbox, sandboxConfig, extraEnv map[string]string) []string {
|
func recordSessionEnvWithExtra(sandbox recordSandbox, sandboxConfig map[string]string, setupScripts []string, extraEnv map[string]string) []string {
|
||||||
env := append([]string{}, os.Environ()...)
|
env := append([]string{}, os.Environ()...)
|
||||||
env = append(env,
|
env = append(env,
|
||||||
"MIRO_HOST_HOME="+sandbox.hostHome,
|
"MIRO_HOST_HOME="+sandbox.hostHome,
|
||||||
"MIRO_HOST_TMP="+sandbox.hostTmp,
|
"MIRO_HOST_TMP="+sandbox.hostTmp,
|
||||||
"MIRO_PATH_ENV="+sandbox.pathEnv,
|
"MIRO_PATH_ENV="+sandbox.pathEnv,
|
||||||
|
setupScriptsEnvName+"="+strings.Join(setupScripts, "\n"),
|
||||||
)
|
)
|
||||||
for _, key := range sortedKeys(sandboxConfig) {
|
for _, key := range sortedKeys(sandboxConfig) {
|
||||||
env = append(env, sandboxEnvName(key)+"="+sandboxConfig[key])
|
env = append(env, sandboxEnvName(key)+"="+sandboxConfig[key])
|
||||||
|
|||||||
@@ -6,6 +6,20 @@ host_home=${MIRO_HOST_HOME:?}
|
|||||||
host_tmp=${MIRO_HOST_TMP:?}
|
host_tmp=${MIRO_HOST_TMP:?}
|
||||||
path_env=${MIRO_PATH_ENV:?}
|
path_env=${MIRO_PATH_ENV:?}
|
||||||
visible_home=${MIRO_VISIBLE_HOME:?}
|
visible_home=${MIRO_VISIBLE_HOME:?}
|
||||||
|
bootstrap_rc="$host_home/.miro-shell-rc"
|
||||||
|
visible_bootstrap_rc="$visible_home/.miro-shell-rc"
|
||||||
|
setup_scripts_dir='/tmp/miro-setup-scripts'
|
||||||
|
|
||||||
|
cat >"$bootstrap_rc" <<'EOF'
|
||||||
|
cd "${HOME:?}"
|
||||||
|
|
||||||
|
for path in /tmp/miro-setup-scripts/*.sh; do
|
||||||
|
[ -e "$path" ] || continue
|
||||||
|
cd "${HOME:?}"
|
||||||
|
source "$path"
|
||||||
|
cd "${HOME:?}"
|
||||||
|
done
|
||||||
|
EOF
|
||||||
|
|
||||||
if [ "${MIRO_COMPARE_MARKER:-0}" = "1" ]; then
|
if [ "${MIRO_COMPARE_MARKER:-0}" = "1" ]; then
|
||||||
printf '__MIRO_E2E_BEGIN__\n'
|
printf '__MIRO_E2E_BEGIN__\n'
|
||||||
@@ -32,4 +46,16 @@ set -- \
|
|||||||
--setenv TZ 'UTC' \
|
--setenv TZ 'UTC' \
|
||||||
--chdir "$visible_home"
|
--chdir "$visible_home"
|
||||||
|
|
||||||
exec bwrap "$@" bash --noprofile --norc -i
|
if [ -n "${MIRO_SETUP_SCRIPTS:-}" ]; then
|
||||||
|
i=1
|
||||||
|
while IFS= read -r host_path || [ -n "$host_path" ]; do
|
||||||
|
[ -n "$host_path" ] || continue
|
||||||
|
visible_path=$(printf '%s/%03d.sh' "$setup_scripts_dir" "$i")
|
||||||
|
set -- "$@" --ro-bind "$host_path" "$visible_path"
|
||||||
|
i=$((i + 1))
|
||||||
|
done <<EOF
|
||||||
|
${MIRO_SETUP_SCRIPTS-}
|
||||||
|
EOF
|
||||||
|
fi
|
||||||
|
|
||||||
|
exec bwrap "$@" bash --noprofile --rcfile "$visible_bootstrap_rc" -i
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ func TestRecordReturnsDiscardedErrorWhenSaveDeclined(t *testing.T) {
|
|||||||
target := filepath.Join(testDir, "a", "b", "c")
|
target := filepath.Join(testDir, "a", "b", "c")
|
||||||
testutil.MustMkdirAll(t, target)
|
testutil.MustMkdirAll(t, target)
|
||||||
return withRecordStreams(t, "n\n", func(rio recordIO) error {
|
return withRecordStreams(t, "n\n", func(rio recordIO) error {
|
||||||
return recordScenario(target, recordShellPath(testDir), rio, defaultSandboxConfig())
|
return recordScenario(target, recordShellPath(testDir), rio, defaultSandboxConfig(), nil)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -87,7 +87,7 @@ func TestRecordReturnsDiscardedErrorWhenOverwriteDeclined(t *testing.T) {
|
|||||||
|
|
||||||
err := testutil.WithWorkingDir(t, root, func() error {
|
err := testutil.WithWorkingDir(t, root, func() error {
|
||||||
return withRecordStreams(t, "n\n", func(rio recordIO) error {
|
return withRecordStreams(t, "n\n", func(rio recordIO) error {
|
||||||
return recordScenario(target, recordShellPath(testDir), rio, defaultSandboxConfig())
|
return recordScenario(target, recordShellPath(testDir), rio, defaultSandboxConfig(), nil)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -120,6 +120,17 @@ func TestBuildRecordShellScriptUsesExpectedCommands(t *testing.T) {
|
|||||||
"host_tmp=${MIRO_HOST_TMP:?}",
|
"host_tmp=${MIRO_HOST_TMP:?}",
|
||||||
"path_env=${MIRO_PATH_ENV:?}",
|
"path_env=${MIRO_PATH_ENV:?}",
|
||||||
"visible_home=${MIRO_VISIBLE_HOME:?}",
|
"visible_home=${MIRO_VISIBLE_HOME:?}",
|
||||||
|
"bootstrap_rc=\"$host_home/.miro-shell-rc\"",
|
||||||
|
"setup_scripts_dir='/tmp/miro-setup-scripts'",
|
||||||
|
"visible_bootstrap_rc=\"$visible_home/.miro-shell-rc\"",
|
||||||
|
"for path in /tmp/miro-setup-scripts/*.sh; do",
|
||||||
|
"source \"$path\"",
|
||||||
|
`if [ -n "${MIRO_SETUP_SCRIPTS:-}" ]; then`,
|
||||||
|
"i=1",
|
||||||
|
`while IFS= read -r host_path || [ -n "$host_path" ]; do`,
|
||||||
|
`visible_path=$(printf '%s/%03d.sh' "$setup_scripts_dir" "$i")`,
|
||||||
|
`set -- "$@" --ro-bind "$host_path" "$visible_path"`,
|
||||||
|
"${MIRO_SETUP_SCRIPTS-}",
|
||||||
`if [ "${MIRO_COMPARE_MARKER:-0}" = "1" ]; then`,
|
`if [ "${MIRO_COMPARE_MARKER:-0}" = "1" ]; then`,
|
||||||
"printf '__MIRO_E2E_BEGIN__\\n'",
|
"printf '__MIRO_E2E_BEGIN__\\n'",
|
||||||
"--bind \"$host_home\" \"$visible_home\"",
|
"--bind \"$host_home\" \"$visible_home\"",
|
||||||
@@ -130,7 +141,7 @@ func TestBuildRecordShellScriptUsesExpectedCommands(t *testing.T) {
|
|||||||
"--setenv TERM 'xterm-256color'",
|
"--setenv TERM 'xterm-256color'",
|
||||||
"--setenv TZ 'UTC'",
|
"--setenv TZ 'UTC'",
|
||||||
"--chdir \"$visible_home\"",
|
"--chdir \"$visible_home\"",
|
||||||
"exec bwrap \"$@\" bash --noprofile --norc -i",
|
"exec bwrap \"$@\" bash --noprofile --rcfile \"$visible_bootstrap_rc\" -i",
|
||||||
} {
|
} {
|
||||||
if !strings.Contains(body, want) {
|
if !strings.Contains(body, want) {
|
||||||
t.Fatalf("wrapper = %q, want substring %q", body, want)
|
t.Fatalf("wrapper = %q, want substring %q", body, want)
|
||||||
@@ -146,7 +157,7 @@ func TestRecordSessionEnvIncludesConfiguredSandboxEnv(t *testing.T) {
|
|||||||
}, map[string]string{
|
}, map[string]string{
|
||||||
"visible_home": "/sandbox/home",
|
"visible_home": "/sandbox/home",
|
||||||
"key_word": "value",
|
"key_word": "value",
|
||||||
})
|
}, []string{"/repo/e2e/setup.sh", "/repo/e2e/suite/setup.sh"})
|
||||||
|
|
||||||
for _, want := range []string{
|
for _, want := range []string{
|
||||||
"MIRO_HOST_HOME=/tmp/host-home",
|
"MIRO_HOST_HOME=/tmp/host-home",
|
||||||
@@ -154,11 +165,15 @@ func TestRecordSessionEnvIncludesConfiguredSandboxEnv(t *testing.T) {
|
|||||||
"MIRO_PATH_ENV=/tmp/bin",
|
"MIRO_PATH_ENV=/tmp/bin",
|
||||||
"MIRO_KEY_WORD=value",
|
"MIRO_KEY_WORD=value",
|
||||||
"MIRO_VISIBLE_HOME=/sandbox/home",
|
"MIRO_VISIBLE_HOME=/sandbox/home",
|
||||||
|
"MIRO_SETUP_SCRIPTS=/repo/e2e/setup.sh\n/repo/e2e/suite/setup.sh",
|
||||||
} {
|
} {
|
||||||
if !containsEnvEntry(env, want) {
|
if !containsEnvEntry(env, want) {
|
||||||
t.Fatalf("env = %#v, want entry %q", env, want)
|
t.Fatalf("env = %#v, want entry %q", env, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if containsEnvKey(env, "MIRO_SETUP_SCRIPT_BINDS") {
|
||||||
|
t.Fatalf("env = %#v, want MIRO_SETUP_SCRIPT_BINDS omitted", env)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRecordSessionEnvWithExtraIncludesAdditionalEntries(t *testing.T) {
|
func TestRecordSessionEnvWithExtraIncludesAdditionalEntries(t *testing.T) {
|
||||||
@@ -168,7 +183,7 @@ func TestRecordSessionEnvWithExtraIncludesAdditionalEntries(t *testing.T) {
|
|||||||
pathEnv: "/tmp/bin",
|
pathEnv: "/tmp/bin",
|
||||||
}, map[string]string{
|
}, map[string]string{
|
||||||
"visible_home": "/sandbox/home",
|
"visible_home": "/sandbox/home",
|
||||||
}, map[string]string{
|
}, []string{"/repo/e2e/setup.sh"}, map[string]string{
|
||||||
compareMarkerEnvName: compareMarkerEnabledValue,
|
compareMarkerEnvName: compareMarkerEnabledValue,
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -177,12 +192,16 @@ func TestRecordSessionEnvWithExtraIncludesAdditionalEntries(t *testing.T) {
|
|||||||
"MIRO_HOST_TMP=/tmp/host-tmp",
|
"MIRO_HOST_TMP=/tmp/host-tmp",
|
||||||
"MIRO_PATH_ENV=/tmp/bin",
|
"MIRO_PATH_ENV=/tmp/bin",
|
||||||
"MIRO_VISIBLE_HOME=/sandbox/home",
|
"MIRO_VISIBLE_HOME=/sandbox/home",
|
||||||
|
"MIRO_SETUP_SCRIPTS=/repo/e2e/setup.sh",
|
||||||
"MIRO_COMPARE_MARKER=1",
|
"MIRO_COMPARE_MARKER=1",
|
||||||
} {
|
} {
|
||||||
if !containsEnvEntry(env, want) {
|
if !containsEnvEntry(env, want) {
|
||||||
t.Fatalf("env = %#v, want entry %q", env, want)
|
t.Fatalf("env = %#v, want entry %q", env, want)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if containsEnvKey(env, "MIRO_SETUP_SCRIPT_BINDS") {
|
||||||
|
t.Fatalf("env = %#v, want MIRO_SETUP_SCRIPT_BINDS omitted", env)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRunRecordSessionUsesSandboxedScriptCommand(t *testing.T) {
|
func TestRunRecordSessionUsesSandboxedScriptCommand(t *testing.T) {
|
||||||
@@ -203,40 +222,46 @@ func TestRunRecordSessionUsesSandboxedScriptCommand(t *testing.T) {
|
|||||||
|
|
||||||
shellPath := recordShellPath(testDir)
|
shellPath := recordShellPath(testDir)
|
||||||
err = withRecordStreams(t, "", func(rio recordIO) error {
|
err = withRecordStreams(t, "", func(rio recordIO) error {
|
||||||
return runRecordSession(t.TempDir(), filepath.Join(t.TempDir(), "raw.in"), filepath.Join(t.TempDir(), "raw.out"), shellPath, sandbox, rio, defaultSandboxConfig())
|
return runRecordSession(t.TempDir(), filepath.Join(t.TempDir(), "raw.in"), filepath.Join(t.TempDir(), "raw.out"), shellPath, sandbox, rio, defaultSandboxConfig(), []string{"/repo/e2e/setup.sh"})
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("runRecordSession() error = %v", err)
|
t.Fatalf("runRecordSession() error = %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
args := strings.Split(strings.TrimSpace(testutil.ReadFile(t, argsPath)), "\n")
|
args := strings.Split(strings.TrimSpace(testutil.ReadFile(t, argsPath)), "\n")
|
||||||
if len(args) != 9 {
|
if len(args) != 10 {
|
||||||
t.Fatalf("script args = %q, want 9 args", args)
|
t.Fatalf("script args = %q, want 10 args", args)
|
||||||
}
|
}
|
||||||
if got := args[:4]; strings.Join(got, "\n") != strings.Join([]string{"-q", "-E", "always", "-I"}, "\n") {
|
if got := args[:5]; strings.Join(got, "\n") != strings.Join([]string{"-q", "-e", "-E", "always", "-I"}, "\n") {
|
||||||
t.Fatalf("script args prefix = %q, want %q", got, []string{"-q", "-E", "always", "-I"})
|
t.Fatalf("script args prefix = %q, want %q", got, []string{"-q", "-e", "-E", "always", "-I"})
|
||||||
}
|
}
|
||||||
if args[5] != "-O" {
|
if args[6] != "-O" {
|
||||||
t.Fatalf("script args[5] = %q, want %q", args[5], "-O")
|
t.Fatalf("script args[6] = %q, want %q", args[6], "-O")
|
||||||
}
|
}
|
||||||
if args[7] != "-c" {
|
if args[8] != "-c" {
|
||||||
t.Fatalf("script args[7] = %q, want %q", args[7], "-c")
|
t.Fatalf("script args[8] = %q, want %q", args[8], "-c")
|
||||||
}
|
}
|
||||||
if args[8] != shellPath {
|
if args[9] != shellPath {
|
||||||
t.Fatalf("script args[8] = %q, want %q", args[8], shellPath)
|
t.Fatalf("script args[9] = %q, want %q", args[9], shellPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
body := testutil.ReadFile(t, commandBodyPath)
|
body := testutil.ReadFile(t, commandBodyPath)
|
||||||
for _, want := range []string{
|
for _, want := range []string{
|
||||||
"host_home=${MIRO_HOST_HOME:?}",
|
"host_home=${MIRO_HOST_HOME:?}",
|
||||||
"visible_home=${MIRO_VISIBLE_HOME:?}",
|
"visible_home=${MIRO_VISIBLE_HOME:?}",
|
||||||
|
"bootstrap_rc=\"$host_home/.miro-shell-rc\"",
|
||||||
|
"visible_bootstrap_rc=\"$visible_home/.miro-shell-rc\"",
|
||||||
|
"for path in /tmp/miro-setup-scripts/*.sh; do",
|
||||||
|
"source \"$path\"",
|
||||||
|
`if [ -n "${MIRO_SETUP_SCRIPTS:-}" ]; then`,
|
||||||
|
`set -- "$@" --ro-bind "$host_path" "$visible_path"`,
|
||||||
`if [ "${MIRO_COMPARE_MARKER:-0}" = "1" ]; then`,
|
`if [ "${MIRO_COMPARE_MARKER:-0}" = "1" ]; then`,
|
||||||
"printf '__MIRO_E2E_BEGIN__\\n'",
|
"printf '__MIRO_E2E_BEGIN__\\n'",
|
||||||
"--ro-bind / /",
|
"--ro-bind / /",
|
||||||
"--tmpfs /home",
|
"--tmpfs /home",
|
||||||
"--setenv HOME \"$visible_home\"",
|
"--setenv HOME \"$visible_home\"",
|
||||||
"--setenv TMPDIR '/tmp'",
|
"--setenv TMPDIR '/tmp'",
|
||||||
"exec bwrap \"$@\" bash --noprofile --norc -i",
|
"exec bwrap \"$@\" bash --noprofile --rcfile \"$visible_bootstrap_rc\" -i",
|
||||||
} {
|
} {
|
||||||
if !strings.Contains(body, want) {
|
if !strings.Contains(body, want) {
|
||||||
t.Fatalf("wrapper = %q, want substring %q", body, want)
|
t.Fatalf("wrapper = %q, want substring %q", body, want)
|
||||||
@@ -293,7 +318,7 @@ func TestRecordScenarioUsesDeterministicSandbox(t *testing.T) {
|
|||||||
in: reader,
|
in: reader,
|
||||||
out: ioDiscard{},
|
out: ioDiscard{},
|
||||||
err: &bytes.Buffer{},
|
err: &bytes.Buffer{},
|
||||||
}, sandboxConfig)
|
}, sandboxConfig, nil)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("recordScenario() error = %v", err)
|
t.Fatalf("recordScenario() error = %v", err)
|
||||||
|
|||||||
@@ -0,0 +1,61 @@
|
|||||||
|
package miro
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
setupScriptName = "setup.sh"
|
||||||
|
setupScriptsEnvName = "MIRO_SETUP_SCRIPTS"
|
||||||
|
)
|
||||||
|
|
||||||
|
func discoverSetupScripts(testDir, scenarioDir string) ([]string, error) {
|
||||||
|
absTestDir, err := filepath.Abs(testDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to resolve test directory path: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
absScenarioDir, err := filepath.Abs(scenarioDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to resolve scenario directory path: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
relToTestDir, err := filepath.Rel(absTestDir, absScenarioDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to resolve scenario directory %q: %v", absScenarioDir, err)
|
||||||
|
}
|
||||||
|
if !isWithinBase(relToTestDir) {
|
||||||
|
return nil, fmt.Errorf("scenario directory %q must be inside test directory %q", absScenarioDir, absTestDir)
|
||||||
|
}
|
||||||
|
|
||||||
|
dirs := []string{absTestDir}
|
||||||
|
if relToTestDir != "." {
|
||||||
|
current := absTestDir
|
||||||
|
for _, part := range strings.Split(relToTestDir, string(os.PathSeparator)) {
|
||||||
|
current = filepath.Join(current, part)
|
||||||
|
dirs = append(dirs, current)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scripts := make([]string, 0, len(dirs))
|
||||||
|
for _, dir := range dirs {
|
||||||
|
path := filepath.Join(dir, setupScriptName)
|
||||||
|
info, err := os.Stat(path)
|
||||||
|
if err == nil {
|
||||||
|
if info.IsDir() {
|
||||||
|
return nil, fmt.Errorf("setup fixture %q is a directory", path)
|
||||||
|
}
|
||||||
|
scripts = append(scripts, path)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if !errors.Is(err, os.ErrNotExist) {
|
||||||
|
return nil, fmt.Errorf("failed to check setup fixture %q: %v", path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return scripts, nil
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
package miro
|
||||||
|
|
||||||
|
import (
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"miro/internal/testutil"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDiscoverSetupScriptsFindsRootToLeafSetupFiles(t *testing.T) {
|
||||||
|
testDir := filepath.Join(t.TempDir(), "e2e")
|
||||||
|
scenarioDir := filepath.Join(testDir, "suite", "spec")
|
||||||
|
rootSetup := filepath.Join(testDir, setupScriptName)
|
||||||
|
suiteSetup := filepath.Join(testDir, "suite", setupScriptName)
|
||||||
|
scenarioSetup := filepath.Join(scenarioDir, setupScriptName)
|
||||||
|
|
||||||
|
testutil.WriteFile(t, rootSetup, "export ROOT=1\n")
|
||||||
|
testutil.WriteFile(t, suiteSetup, "export SUITE=1\n")
|
||||||
|
testutil.WriteFile(t, scenarioSetup, "export SPEC=1\n")
|
||||||
|
|
||||||
|
got, err := discoverSetupScripts(testDir, scenarioDir)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("discoverSetupScripts() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
want := []string{rootSetup, suiteSetup, scenarioSetup}
|
||||||
|
if len(got) != len(want) {
|
||||||
|
t.Fatalf("len(discoverSetupScripts()) = %d, want %d", len(got), len(want))
|
||||||
|
}
|
||||||
|
for i := range want {
|
||||||
|
if got[i] != want[i] {
|
||||||
|
t.Fatalf("discoverSetupScripts()[%d] = %q, want %q", i, got[i], want[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ type testScenario struct {
|
|||||||
relPath string
|
relPath string
|
||||||
inPath string
|
inPath string
|
||||||
outPath string
|
outPath string
|
||||||
|
setupScripts []string
|
||||||
}
|
}
|
||||||
|
|
||||||
type testSummary struct {
|
type testSummary struct {
|
||||||
@@ -181,6 +182,10 @@ func discoverTestScenarios(discoveryRoot, displayRoot string) ([]testScenario, e
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to resolve scenario path for %q: %v", dir, err)
|
return nil, fmt.Errorf("failed to resolve scenario path for %q: %v", dir, err)
|
||||||
}
|
}
|
||||||
|
setupScripts, err := discoverSetupScripts(displayRoot, dir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case files.inPath == "":
|
case files.inPath == "":
|
||||||
@@ -194,6 +199,7 @@ func discoverTestScenarios(discoveryRoot, displayRoot string) ([]testScenario, e
|
|||||||
relPath: relPath,
|
relPath: relPath,
|
||||||
inPath: files.inPath,
|
inPath: files.inPath,
|
||||||
outPath: files.outPath,
|
outPath: files.outPath,
|
||||||
|
setupScripts: setupScripts,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,9 +228,9 @@ func replayScenario(scenario testScenario, shellPath string, _ testIO, sandboxCo
|
|||||||
}
|
}
|
||||||
defer cleanupSandbox()
|
defer cleanupSandbox()
|
||||||
|
|
||||||
cmd := exec.Command("script", "-q", "-E", "always", "-O", rawOut, "-c", shellPath)
|
cmd := exec.Command("script", "-q", "-e", "-E", "always", "-O", rawOut, "-c", shellPath)
|
||||||
cmd.Dir = scenario.dir
|
cmd.Dir = scenario.dir
|
||||||
cmd.Env = recordSessionEnvWithExtra(sandbox, sandboxConfig, map[string]string{
|
cmd.Env = recordSessionEnvWithExtra(sandbox, sandboxConfig, scenario.setupScripts, map[string]string{
|
||||||
compareMarkerEnvName: compareMarkerEnabledValue,
|
compareMarkerEnvName: compareMarkerEnabledValue,
|
||||||
})
|
})
|
||||||
cmd.Stdin = bytes.NewReader(input)
|
cmd.Stdin = bytes.NewReader(input)
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ func TestReplayScenarioUsesRecordedInputAndKeepsReplayOutputQuiet(t *testing.T)
|
|||||||
relPath: filepath.Join("suite", "spec"),
|
relPath: filepath.Join("suite", "spec"),
|
||||||
inPath: filepath.Join(scenarioDir, "in"),
|
inPath: filepath.Join(scenarioDir, "in"),
|
||||||
outPath: filepath.Join(scenarioDir, "out"),
|
outPath: filepath.Join(scenarioDir, "out"),
|
||||||
|
setupScripts: nil,
|
||||||
}, shellPath, testIO{
|
}, shellPath, testIO{
|
||||||
out: &stdout,
|
out: &stdout,
|
||||||
err: &stderr,
|
err: &stderr,
|
||||||
@@ -143,6 +144,7 @@ func TestReplayScenarioFailsWhenCompareMarkerMissing(t *testing.T) {
|
|||||||
relPath: filepath.Join("suite", "spec"),
|
relPath: filepath.Join("suite", "spec"),
|
||||||
inPath: filepath.Join(scenarioDir, "in"),
|
inPath: filepath.Join(scenarioDir, "in"),
|
||||||
outPath: filepath.Join(scenarioDir, "out"),
|
outPath: filepath.Join(scenarioDir, "out"),
|
||||||
|
setupScripts: nil,
|
||||||
}, shellPath, testIO{
|
}, shellPath, testIO{
|
||||||
out: &bytes.Buffer{},
|
out: &bytes.Buffer{},
|
||||||
err: &bytes.Buffer{},
|
err: &bytes.Buffer{},
|
||||||
@@ -158,6 +160,7 @@ func TestReplayScenarioFailsWhenCompareMarkerMissing(t *testing.T) {
|
|||||||
func TestDiscoverTestScenariosUsesDisplayRootForRelativePaths(t *testing.T) {
|
func TestDiscoverTestScenariosUsesDisplayRootForRelativePaths(t *testing.T) {
|
||||||
testDir := filepath.Join(t.TempDir(), "e2e")
|
testDir := filepath.Join(t.TempDir(), "e2e")
|
||||||
scopedDir := filepath.Join(testDir, "nested")
|
scopedDir := filepath.Join(testDir, "nested")
|
||||||
|
testutil.WriteFile(t, filepath.Join(testDir, setupScriptName), "export ROOT=1\n")
|
||||||
testutil.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)
|
got, err := discoverTestScenarios(scopedDir, testDir)
|
||||||
@@ -170,6 +173,9 @@ func TestDiscoverTestScenariosUsesDisplayRootForRelativePaths(t *testing.T) {
|
|||||||
if got[0].relPath != filepath.Join("nested", "b") {
|
if got[0].relPath != filepath.Join("nested", "b") {
|
||||||
t.Fatalf("scenario relPath = %q, want %q", got[0].relPath, filepath.Join("nested", "b"))
|
t.Fatalf("scenario relPath = %q, want %q", got[0].relPath, filepath.Join("nested", "b"))
|
||||||
}
|
}
|
||||||
|
if len(got[0].setupScripts) != 1 || got[0].setupScripts[0] != filepath.Join(testDir, setupScriptName) {
|
||||||
|
t.Fatalf("scenario setupScripts = %#v, want root setup", got[0].setupScripts)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResolveTestDiscoveryRootRejectsMissingPath(t *testing.T) {
|
func TestResolveTestDiscoveryRootRejectsMissingPath(t *testing.T) {
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"miro/internal/testutil"
|
"miro/internal/testutil"
|
||||||
@@ -78,3 +79,14 @@ func containsEnvEntry(env []string, want string) bool {
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func containsEnvKey(env []string, key string) bool {
|
||||||
|
prefix := key + "="
|
||||||
|
for _, entry := range env {
|
||||||
|
if strings.HasPrefix(entry, prefix) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user