fix: strip script headers and footers in saved output
This commit is contained in:
+1
-1
@@ -299,7 +299,7 @@ func addFakeRecordDependencies(t *testing.T, names ...string) {
|
|||||||
path := filepath.Join(binDir, name)
|
path := filepath.Join(binDir, name)
|
||||||
body := "#!/bin/sh\nexit 0\n"
|
body := "#!/bin/sh\nexit 0\n"
|
||||||
if name == "script" {
|
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"
|
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\ncat <<'EOF' > \"$in\"\nfake recorded input\nEOF\ncat <<'EOF' > \"$out\"\nScript started on 2026-03-18 11:13:38+00:00 [TERM=\"xterm-256color\"]\nfake recorded output\nScript done on 2026-03-18 11:13:44+00:00 [COMMAND_EXIT_CODE=\"0\"]\nEOF\nexit 0\n"
|
||||||
}
|
}
|
||||||
if err := os.WriteFile(path, []byte(body), 0o755); err != nil {
|
if err := os.WriteFile(path, []byte(body), 0o755); err != nil {
|
||||||
t.Fatalf("WriteFile(%q) error = %v", path, err)
|
t.Fatalf("WriteFile(%q) error = %v", path, err)
|
||||||
|
|||||||
@@ -0,0 +1,82 @@
|
|||||||
|
package miro
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
scriptStartPrefix = []byte("Script started on ")
|
||||||
|
scriptDonePrefix = []byte("Script done on ")
|
||||||
|
)
|
||||||
|
|
||||||
|
func loadRecordedOutput(path string) ([]byte, error) {
|
||||||
|
data, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return stripScriptWrapper(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func stripScriptWrapper(data []byte) ([]byte, error) {
|
||||||
|
hasHeader := bytes.HasPrefix(data, scriptStartPrefix)
|
||||||
|
|
||||||
|
headerEnd := bytes.IndexByte(data, '\n')
|
||||||
|
if hasHeader && headerEnd == -1 {
|
||||||
|
return nil, fmt.Errorf("could not parse script wrapper from recorded output: incomplete header line")
|
||||||
|
}
|
||||||
|
|
||||||
|
body := data
|
||||||
|
if hasHeader {
|
||||||
|
body = data[headerEnd+1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
footerStart, footerState := findScriptFooter(body)
|
||||||
|
switch {
|
||||||
|
case hasHeader && footerState == footerMissing:
|
||||||
|
return nil, fmt.Errorf("could not parse script wrapper from recorded output: missing footer line")
|
||||||
|
case !hasHeader && footerState == footerFound:
|
||||||
|
return nil, fmt.Errorf("could not parse script wrapper from recorded output: missing header line")
|
||||||
|
case !hasHeader && footerState == footerMissing:
|
||||||
|
return nil, fmt.Errorf("could not parse script wrapper from recorded output: missing header and footer lines")
|
||||||
|
case footerState == footerMalformed:
|
||||||
|
return nil, fmt.Errorf("could not parse script wrapper from recorded output: incomplete footer line")
|
||||||
|
}
|
||||||
|
|
||||||
|
return body[:footerStart], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type footerMatchState int
|
||||||
|
|
||||||
|
const (
|
||||||
|
footerMissing footerMatchState = iota
|
||||||
|
footerMalformed
|
||||||
|
footerFound
|
||||||
|
)
|
||||||
|
|
||||||
|
func findScriptFooter(data []byte) (int, footerMatchState) {
|
||||||
|
candidate := -1
|
||||||
|
switch {
|
||||||
|
case bytes.HasPrefix(data, scriptDonePrefix):
|
||||||
|
candidate = 0
|
||||||
|
default:
|
||||||
|
idx := bytes.LastIndex(data, append([]byte{'\n'}, scriptDonePrefix...))
|
||||||
|
if idx != -1 {
|
||||||
|
candidate = idx + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if candidate == -1 {
|
||||||
|
return 0, footerMissing
|
||||||
|
}
|
||||||
|
|
||||||
|
footer := data[candidate:]
|
||||||
|
newline := bytes.IndexByte(footer, '\n')
|
||||||
|
if newline == -1 || candidate+newline+1 != len(data) {
|
||||||
|
return 0, footerMalformed
|
||||||
|
}
|
||||||
|
|
||||||
|
return candidate, footerFound
|
||||||
|
}
|
||||||
@@ -48,10 +48,15 @@ func recordScenario(target string, rio recordIO) error {
|
|||||||
return ErrRecordingDiscarded
|
return ErrRecordingDiscarded
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := copyRecordFile(rawIn, filepath.Join(target, "in")); err != nil {
|
recordedIn, recordedOut, err := loadRecordedFixtures(rawIn, rawOut)
|
||||||
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := copyRecordFile(rawOut, filepath.Join(target, "out")); err != nil {
|
|
||||||
|
if err := os.WriteFile(filepath.Join(target, "in"), recordedIn, 0o644); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filepath.Join(target, "out"), recordedOut, 0o644); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,11 +136,16 @@ func recordFixturesExist(target string) (bool, error) {
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyRecordFile(src, dst string) error {
|
func loadRecordedFixtures(rawIn, rawOut string) ([]byte, []byte, error) {
|
||||||
data, err := os.ReadFile(src)
|
recordedIn, err := os.ReadFile(rawIn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return os.WriteFile(dst, data, 0o644)
|
recordedOut, err := loadRecordedOutput(rawOut)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return recordedIn, recordedOut, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -33,6 +33,13 @@ func TestRecordCreatesRelativePath(t *testing.T) {
|
|||||||
t.Fatalf("Stat(%q) error = %v", filepath.Join(want, name), err)
|
t.Fatalf("Stat(%q) error = %v", filepath.Join(want, name), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if got := 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" {
|
||||||
|
t.Fatalf("saved out = %q, want %q", got, "fake recorded output\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRecordStripsExplicitTestDirPrefix(t *testing.T) {
|
func TestRecordStripsExplicitTestDirPrefix(t *testing.T) {
|
||||||
@@ -60,6 +67,10 @@ func TestRecordStripsExplicitTestDirPrefix(t *testing.T) {
|
|||||||
t.Fatalf("Stat(%q) error = %v", filepath.Join(want, name), err)
|
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) {
|
func TestRecordRejectsAbsolutePathOutsideTestDir(t *testing.T) {
|
||||||
@@ -193,6 +204,17 @@ func withStdin(t *testing.T, input string, fn func()) {
|
|||||||
fn()
|
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{}
|
type ioDiscard struct{}
|
||||||
|
|
||||||
func (ioDiscard) Write(p []byte) (int, error) {
|
func (ioDiscard) Write(p []byte) (int, error) {
|
||||||
@@ -207,7 +229,7 @@ func addFakeRecordDependencies(t *testing.T, names ...string) {
|
|||||||
path := filepath.Join(binDir, name)
|
path := filepath.Join(binDir, name)
|
||||||
body := "#!/bin/sh\nexit 0\n"
|
body := "#!/bin/sh\nexit 0\n"
|
||||||
if name == "script" {
|
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"
|
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\nif [ -n \"${FAKE_SCRIPT_LOG_IN+x}\" ]; then\n printf '%s' \"$FAKE_SCRIPT_LOG_IN\" > \"$in\"\nelse\n cat <<'EOF' > \"$in\"\nfake recorded input\nEOF\nfi\nif [ -n \"${FAKE_SCRIPT_LOG_OUT+x}\" ]; then\n printf '%s' \"$FAKE_SCRIPT_LOG_OUT\" > \"$out\"\nelse\n cat <<'EOF' > \"$out\"\nScript started on 2026-03-18 11:13:38+00:00 [TERM=\"xterm-256color\"]\nfake recorded output\nScript done on 2026-03-18 11:13:44+00:00 [COMMAND_EXIT_CODE=\"0\"]\nEOF\nfi\nexit 0\n"
|
||||||
}
|
}
|
||||||
if err := os.WriteFile(path, []byte(body), 0o755); err != nil {
|
if err := os.WriteFile(path, []byte(body), 0o755); err != nil {
|
||||||
t.Fatalf("WriteFile(%q) error = %v", path, err)
|
t.Fatalf("WriteFile(%q) error = %v", path, err)
|
||||||
|
|||||||
Reference in New Issue
Block a user