Files
fn_registry/functions/infra/process_spawn_test.go
T
egutierrez 741fdcee24 feat: add process manager and execution store types (0007b, 0007c)
Process spawn/wait/kill functions for subprocess management with output
capture, timeout, and process group cleanup. DagRun and DagStepResult
types for SQLite execution persistence.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 13:05:13 +02:00

108 lines
2.7 KiB
Go

package infra
import (
"strings"
"testing"
)
func TestProcessSpawn(t *testing.T) {
t.Run("spawn and wait echo", func(t *testing.T) {
h, err := ProcessSpawn("echo hello", "", nil, "")
if err != nil {
t.Fatalf("spawn: %v", err)
}
res, err := ProcessWait(h, 10)
if err != nil {
t.Fatalf("wait: %v", err)
}
if res.ExitCode != 0 {
t.Errorf("exit code: got %d, want 0", res.ExitCode)
}
if !strings.Contains(res.Stdout, "hello") {
t.Errorf("stdout: got %q, want it to contain 'hello'", res.Stdout)
}
})
t.Run("spawn with timeout kills", func(t *testing.T) {
h, err := ProcessSpawn("sleep 60", "", nil, "")
if err != nil {
t.Fatalf("spawn: %v", err)
}
res, err := ProcessWait(h, 2)
if err != nil {
t.Fatalf("wait: %v", err)
}
if !res.Killed {
t.Errorf("killed: got false, want true")
}
if res.ExitCode == 0 {
t.Errorf("exit code: got 0, want != 0 after kill")
}
})
t.Run("spawn with env", func(t *testing.T) {
h, err := ProcessSpawn("echo $TEST_VAR", "", []string{"PATH=/usr/bin:/bin", "TEST_VAR=hello123"}, "")
if err != nil {
t.Fatalf("spawn: %v", err)
}
res, err := ProcessWait(h, 10)
if err != nil {
t.Fatalf("wait: %v", err)
}
if !strings.Contains(res.Stdout, "hello123") {
t.Errorf("stdout: got %q, want it to contain 'hello123'", res.Stdout)
}
})
t.Run("spawn script", func(t *testing.T) {
script := "#!/bin/sh\necho line1\necho line2"
h, err := ProcessSpawn(script, "", nil, "")
if err != nil {
t.Fatalf("spawn: %v", err)
}
res, err := ProcessWait(h, 10)
if err != nil {
t.Fatalf("wait: %v", err)
}
if !strings.Contains(res.Stdout, "line1") {
t.Errorf("stdout: got %q, want it to contain 'line1'", res.Stdout)
}
if !strings.Contains(res.Stdout, "line2") {
t.Errorf("stdout: got %q, want it to contain 'line2'", res.Stdout)
}
})
t.Run("spawn with working dir", func(t *testing.T) {
h, err := ProcessSpawn("pwd", "/tmp", nil, "")
if err != nil {
t.Fatalf("spawn: %v", err)
}
res, err := ProcessWait(h, 10)
if err != nil {
t.Fatalf("wait: %v", err)
}
if !strings.Contains(res.Stdout, "/tmp") {
t.Errorf("stdout: got %q, want it to contain '/tmp'", res.Stdout)
}
})
t.Run("kill process", func(t *testing.T) {
h, err := ProcessSpawn("sleep 60", "", nil, "")
if err != nil {
t.Fatalf("spawn: %v", err)
}
if err := ProcessKill(h, 1); err != nil {
t.Fatalf("kill: %v", err)
}
// After kill, Wait should unblock quickly.
_ = h.Cmd.Wait()
state := h.Cmd.ProcessState
if state == nil {
t.Fatal("process state is nil after kill+wait")
}
if state.ExitCode() == 0 {
t.Errorf("exit code: got 0 after kill, want non-zero")
}
})
}