Files
2026-08-05 10:57:24 +01:00

108 lines
2.8 KiB
Go

package process
import (
"bufio"
"context"
"io"
"testing"
"time"
"github.com/kandev/kandev/internal/agentctl/types"
)
func TestProcessRunnerStartPipedOutputRemainsReadableAfterDone(t *testing.T) {
runner := NewProcessRunner(nil, newTestLogger(t), 2*1024*1024)
command, env := fixtureExec("write-both stdout-after-done stderr-after-done")
proc, err := runner.StartPiped(PipedStartRequest{
SessionID: "session-fast-exit",
Kind: types.ProcessKindCustom,
ScriptName: "test-fast-exit",
Command: command[0],
Args: command[1:],
Env: env,
PipeStderr: true,
})
if err != nil {
t.Fatalf("StartPiped() error = %v", err)
}
t.Cleanup(func() {
_ = proc.Stdin.Close()
_ = proc.Stdout.Close()
_ = proc.Stderr.Close()
})
if err := proc.Stdin.Close(); err != nil {
t.Fatalf("close stdin: %v", err)
}
select {
case <-proc.Done:
case <-time.After(5 * time.Second):
t.Fatal("fast-exit process was not reaped before timeout")
}
stdout, err := io.ReadAll(proc.Stdout)
if err != nil {
t.Fatalf("read stdout after Done: %v", err)
}
stderr, err := io.ReadAll(proc.Stderr)
if err != nil {
t.Fatalf("read stderr after Done: %v", err)
}
if string(stdout) != "stdout-after-done" {
t.Fatalf("stdout = %q, want %q", stdout, "stdout-after-done")
}
if string(stderr) != "stderr-after-done" {
t.Fatalf("stderr = %q, want %q", stderr, "stderr-after-done")
}
}
func TestProcessRunnerStartPipedRoundTripAndStop(t *testing.T) {
runner := NewProcessRunner(nil, newTestLogger(t), 2*1024*1024)
command, env := fixtureExec("cat")
proc, err := runner.StartPiped(PipedStartRequest{
SessionID: "session-1",
Kind: types.ProcessKindCustom,
ScriptName: "test-lsp",
Command: command[0],
Args: command[1:],
Env: env,
})
if err != nil {
t.Fatalf("StartPiped() error = %v", err)
}
t.Cleanup(func() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
_ = runner.Stop(ctx, StopProcessRequest{ProcessID: proc.ID})
_ = proc.Stdin.Close()
_ = proc.Stdout.Close()
if proc.Stderr != nil {
_ = proc.Stderr.Close()
}
})
if _, err := proc.Stdin.Write([]byte("hello\n")); err != nil {
t.Fatalf("write stdin: %v", err)
}
line, err := bufio.NewReader(proc.Stdout).ReadString('\n')
if err != nil {
t.Fatalf("read stdout: %v", err)
}
if line != "hello\n" {
t.Fatalf("stdout = %q, want %q", line, "hello\\n")
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := runner.Stop(ctx, StopProcessRequest{ProcessID: proc.ID}); err != nil {
t.Fatalf("Stop() error = %v", err)
}
select {
case <-proc.Done:
case <-ctx.Done():
t.Fatal("piped process was not reaped before timeout")
}
if _, ok := runner.Get(proc.ID, false); ok {
t.Fatal("piped process remains tracked after stop")
}
}