From f34061954f44be4107ee64d3690bb3a13f48ed80 Mon Sep 17 00:00:00 2001 From: Leon Mika Date: Sat, 2 May 2026 09:49:05 +1000 Subject: [PATCH] Use bytes.NewReader and add Dir/Env/Stdin coverage Replaces the handwritten bytesBuf/eof workaround with the canonical bytes.NewReader + io.EOF, and adds behavioural tests for the Dir, Env, and Stdin branches of Real.Run that the original plan didn't cover. Co-Authored-By: Claude Opus 4.7 (1M context) --- internal/runner/runner.go | 22 ++-------------- internal/runner/runner_test.go | 46 ++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 20 deletions(-) diff --git a/internal/runner/runner.go b/internal/runner/runner.go index c4610ab..1ab63e6 100644 --- a/internal/runner/runner.go +++ b/internal/runner/runner.go @@ -1,6 +1,7 @@ package runner import ( + "bytes" "context" "fmt" "os/exec" @@ -32,7 +33,7 @@ func (Real) Run(ctx context.Context, s Spec) ([]byte, error) { cmd.Env = s.Env } if len(s.Stdin) > 0 { - cmd.Stdin = bytesReader(s.Stdin) + cmd.Stdin = bytes.NewReader(s.Stdin) } out, err := cmd.CombinedOutput() if err != nil { @@ -41,25 +42,6 @@ func (Real) Run(ctx context.Context, s Spec) ([]byte, error) { return out, nil } -// bytesReader avoids a bytes import in this small file. -func bytesReader(b []byte) *bytesBuf { return &bytesBuf{b: b} } - -type bytesBuf struct { - b []byte - i int -} - -func (r *bytesBuf) Read(p []byte) (int, error) { - if r.i >= len(r.b) { - return 0, eof - } - n := copy(p, r.b[r.i:]) - r.i += n - return n, nil -} - -var eof = fmt.Errorf("EOF") - // Call is a recorded invocation made against a Fake. type Call struct { Name string diff --git a/internal/runner/runner_test.go b/internal/runner/runner_test.go index aadda40..e55eb63 100644 --- a/internal/runner/runner_test.go +++ b/internal/runner/runner_test.go @@ -58,3 +58,49 @@ func TestFakeRunner_UnconfiguredCallFailsLoudly(t *testing.T) { t.Fatal("expected error for unconfigured call") } } + +func TestRealRunner_StdinIsPipedToCommand(t *testing.T) { + r := runner.Real{} + out, err := r.Run(context.Background(), runner.Spec{ + Name: "cat", + Stdin: []byte("piped-payload"), + }) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !strings.Contains(string(out), "piped-payload") { + t.Fatalf("output %q does not contain piped payload", out) + } +} + +func TestRealRunner_DirSetsWorkingDirectory(t *testing.T) { + dir := t.TempDir() + r := runner.Real{} + out, err := r.Run(context.Background(), runner.Spec{ + Name: "pwd", + Dir: dir, + }) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + // macOS resolves /var/folders/... → /private/var/folders/... so just + // match the trailing tempdir name. + if !strings.Contains(string(out), dir[strings.LastIndex(dir, "/"):]) { + t.Fatalf("pwd output %q does not contain %q", out, dir) + } +} + +func TestRealRunner_EnvReplacesProcessEnv(t *testing.T) { + r := runner.Real{} + out, err := r.Run(context.Background(), runner.Spec{ + Name: "sh", + Args: []string{"-c", "echo $WAILS_TEST"}, + Env: []string{"WAILS_TEST=hello-from-spec-env"}, + }) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if !strings.Contains(string(out), "hello-from-spec-env") { + t.Fatalf("output %q does not contain expected env value", out) + } +}