2026-03-18 15:07:03 +00:00
|
|
|
package process_test
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
2026-04-24 18:03:24 +01:00
|
|
|
"errors"
|
2026-04-04 03:07:13 +00:00
|
|
|
"os/exec"
|
2026-03-18 15:07:03 +00:00
|
|
|
"path/filepath"
|
|
|
|
|
"testing"
|
|
|
|
|
"time"
|
|
|
|
|
|
2026-04-24 23:43:57 +01:00
|
|
|
process "dappco.re/go/process"
|
2026-03-18 15:07:03 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func testCtx(t *testing.T) context.Context {
|
|
|
|
|
t.Helper()
|
|
|
|
|
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
|
|
|
|
t.Cleanup(cancel)
|
|
|
|
|
return ctx
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestProgram_Find_KnownBinary(t *testing.T) {
|
|
|
|
|
p := &process.Program{Name: "echo"}
|
2026-04-24 18:03:24 +01:00
|
|
|
if err := p.Find(); err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if p.Path == "" {
|
|
|
|
|
t.Fatal("expected non-empty path")
|
|
|
|
|
}
|
2026-03-18 15:07:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestProgram_Find_UnknownBinary(t *testing.T) {
|
|
|
|
|
p := &process.Program{Name: "no-such-binary-xyzzy-42"}
|
|
|
|
|
err := p.Find()
|
2026-04-24 18:03:24 +01:00
|
|
|
if err == nil {
|
|
|
|
|
t.Fatal("expected error, got nil")
|
|
|
|
|
}
|
|
|
|
|
if !errors.Is(err, process.ErrProgramNotFound) {
|
|
|
|
|
t.Fatalf("expected ErrProgramNotFound, got %v", err)
|
|
|
|
|
}
|
2026-03-18 15:07:03 +00:00
|
|
|
}
|
|
|
|
|
|
2026-04-04 03:07:13 +00:00
|
|
|
func TestProgram_Find_UsesExistingPath(t *testing.T) {
|
|
|
|
|
path, err := exec.LookPath("echo")
|
2026-04-24 18:03:24 +01:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
2026-04-04 03:07:13 +00:00
|
|
|
|
|
|
|
|
p := &process.Program{Path: path}
|
2026-04-24 18:03:24 +01:00
|
|
|
if err := p.Find(); err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if p.Path != path {
|
|
|
|
|
t.Fatalf("want %v, got %v", path, p.Path)
|
|
|
|
|
}
|
2026-04-04 03:07:13 +00:00
|
|
|
}
|
|
|
|
|
|
2026-04-04 03:10:39 +00:00
|
|
|
func TestProgram_Find_PrefersExistingPathOverName(t *testing.T) {
|
|
|
|
|
path, err := exec.LookPath("echo")
|
2026-04-24 18:03:24 +01:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
2026-04-04 03:10:39 +00:00
|
|
|
|
|
|
|
|
p := &process.Program{
|
|
|
|
|
Name: "no-such-binary-xyzzy-42",
|
|
|
|
|
Path: path,
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-24 18:03:24 +01:00
|
|
|
if err := p.Find(); err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if p.Path != path {
|
|
|
|
|
t.Fatalf("want %v, got %v", path, p.Path)
|
|
|
|
|
}
|
2026-04-04 03:10:39 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-18 15:07:03 +00:00
|
|
|
func TestProgram_Find_EmptyName(t *testing.T) {
|
|
|
|
|
p := &process.Program{}
|
2026-04-24 18:03:24 +01:00
|
|
|
if err := p.Find(); err == nil {
|
|
|
|
|
t.Fatal("expected error, got nil")
|
|
|
|
|
}
|
2026-03-18 15:07:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestProgram_Run_ReturnsOutput(t *testing.T) {
|
|
|
|
|
p := &process.Program{Name: "echo"}
|
2026-04-24 18:03:24 +01:00
|
|
|
if err := p.Find(); err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
2026-03-18 15:07:03 +00:00
|
|
|
|
|
|
|
|
out, err := p.Run(testCtx(t), "hello")
|
2026-04-24 18:03:24 +01:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if out != "hello" {
|
|
|
|
|
t.Fatalf("want %q, got %q", "hello", out)
|
|
|
|
|
}
|
2026-03-18 15:07:03 +00:00
|
|
|
}
|
|
|
|
|
|
2026-04-04 02:39:59 +00:00
|
|
|
func TestProgram_Run_PreservesLeadingWhitespace(t *testing.T) {
|
|
|
|
|
p := &process.Program{Name: "sh"}
|
2026-04-24 18:03:24 +01:00
|
|
|
if err := p.Find(); err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
2026-04-04 02:39:59 +00:00
|
|
|
|
|
|
|
|
out, err := p.Run(testCtx(t), "-c", "printf ' hello \n'")
|
2026-04-24 18:03:24 +01:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if out != " hello" {
|
|
|
|
|
t.Fatalf("want %q, got %q", " hello", out)
|
|
|
|
|
}
|
2026-04-04 02:39:59 +00:00
|
|
|
}
|
|
|
|
|
|
2026-03-18 15:07:03 +00:00
|
|
|
func TestProgram_Run_WithoutFind_FallsBackToName(t *testing.T) {
|
|
|
|
|
// Path is empty; RunDir should fall back to Name for OS PATH resolution.
|
|
|
|
|
p := &process.Program{Name: "echo"}
|
|
|
|
|
|
|
|
|
|
out, err := p.Run(testCtx(t), "fallback")
|
2026-04-24 18:03:24 +01:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if out != "fallback" {
|
|
|
|
|
t.Fatalf("want %q, got %q", "fallback", out)
|
|
|
|
|
}
|
2026-03-18 15:07:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestProgram_RunDir_UsesDirectory(t *testing.T) {
|
|
|
|
|
p := &process.Program{Name: "pwd"}
|
2026-04-24 18:03:24 +01:00
|
|
|
if err := p.Find(); err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
2026-03-18 15:07:03 +00:00
|
|
|
|
|
|
|
|
dir := t.TempDir()
|
|
|
|
|
|
|
|
|
|
out, err := p.RunDir(testCtx(t), dir)
|
2026-04-24 18:03:24 +01:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
2026-03-18 15:07:03 +00:00
|
|
|
// Resolve symlinks on both sides for portability (macOS uses /private/ prefix).
|
|
|
|
|
canonicalDir, err := filepath.EvalSymlinks(dir)
|
2026-04-24 18:03:24 +01:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
2026-03-18 15:07:03 +00:00
|
|
|
canonicalOut, err := filepath.EvalSymlinks(out)
|
2026-04-24 18:03:24 +01:00
|
|
|
if err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if canonicalDir != canonicalOut {
|
|
|
|
|
t.Fatalf("want %q, got %q", canonicalDir, canonicalOut)
|
|
|
|
|
}
|
2026-03-18 15:07:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestProgram_Run_FailingCommand(t *testing.T) {
|
|
|
|
|
p := &process.Program{Name: "false"}
|
2026-04-24 18:03:24 +01:00
|
|
|
if err := p.Find(); err != nil {
|
|
|
|
|
t.Fatalf("unexpected error: %v", err)
|
|
|
|
|
}
|
2026-03-18 15:07:03 +00:00
|
|
|
|
|
|
|
|
_, err := p.Run(testCtx(t))
|
2026-04-24 18:03:24 +01:00
|
|
|
if err == nil {
|
|
|
|
|
t.Fatal("expected error, got nil")
|
|
|
|
|
}
|
2026-03-18 15:07:03 +00:00
|
|
|
}
|
2026-04-04 00:18:14 +00:00
|
|
|
|
|
|
|
|
func TestProgram_Run_NilContextRejected(t *testing.T) {
|
|
|
|
|
p := &process.Program{Name: "echo"}
|
|
|
|
|
|
|
|
|
|
_, err := p.Run(nil, "test")
|
2026-04-24 18:03:24 +01:00
|
|
|
if err == nil {
|
|
|
|
|
t.Fatal("expected error, got nil")
|
|
|
|
|
}
|
|
|
|
|
if !errors.Is(err, process.ErrProgramContextRequired) {
|
|
|
|
|
t.Fatalf("expected ErrProgramContextRequired, got %v", err)
|
|
|
|
|
}
|
2026-04-04 00:18:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestProgram_RunDir_EmptyNameRejected(t *testing.T) {
|
|
|
|
|
p := &process.Program{}
|
|
|
|
|
|
|
|
|
|
_, err := p.RunDir(testCtx(t), "", "test")
|
2026-04-24 18:03:24 +01:00
|
|
|
if err == nil {
|
|
|
|
|
t.Fatal("expected error, got nil")
|
|
|
|
|
}
|
|
|
|
|
if !errors.Is(err, process.ErrProgramNameRequired) {
|
|
|
|
|
t.Fatalf("expected ErrProgramNameRequired, got %v", err)
|
|
|
|
|
}
|
2026-04-04 00:18:14 +00:00
|
|
|
}
|