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) <noreply@anthropic.com>
This commit is contained in:
Leon Mika 2026-05-02 09:49:05 +10:00
parent 2350046601
commit f34061954f
2 changed files with 48 additions and 20 deletions

View file

@ -1,6 +1,7 @@
package runner package runner
import ( import (
"bytes"
"context" "context"
"fmt" "fmt"
"os/exec" "os/exec"
@ -32,7 +33,7 @@ func (Real) Run(ctx context.Context, s Spec) ([]byte, error) {
cmd.Env = s.Env cmd.Env = s.Env
} }
if len(s.Stdin) > 0 { if len(s.Stdin) > 0 {
cmd.Stdin = bytesReader(s.Stdin) cmd.Stdin = bytes.NewReader(s.Stdin)
} }
out, err := cmd.CombinedOutput() out, err := cmd.CombinedOutput()
if err != nil { if err != nil {
@ -41,25 +42,6 @@ func (Real) Run(ctx context.Context, s Spec) ([]byte, error) {
return out, nil 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. // Call is a recorded invocation made against a Fake.
type Call struct { type Call struct {
Name string Name string

View file

@ -58,3 +58,49 @@ func TestFakeRunner_UnconfiguredCallFailsLoudly(t *testing.T) {
t.Fatal("expected error for unconfigured call") 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)
}
}