mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-03-27 09:45:53 +00:00
221 lines
4.7 KiB
Go
221 lines
4.7 KiB
Go
package session
|
|
|
|
import (
|
|
"os"
|
|
"os/exec"
|
|
"runtime"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestProcessTerminator_TerminateGracefully(t *testing.T) {
|
|
// Skip on Windows as signal handling is different
|
|
if runtime.GOOS == "windows" {
|
|
t.Skip("Skipping signal tests on Windows")
|
|
}
|
|
|
|
tests := []struct {
|
|
name string
|
|
setupSession func() *Session
|
|
expectGraceful bool
|
|
checkInterval time.Duration
|
|
}{
|
|
{
|
|
name: "already exited session",
|
|
setupSession: func() *Session {
|
|
s := &Session{
|
|
ID: "test-session-1",
|
|
info: &Info{
|
|
Status: string(StatusExited),
|
|
},
|
|
}
|
|
return s
|
|
},
|
|
expectGraceful: true,
|
|
},
|
|
{
|
|
name: "no process to terminate",
|
|
setupSession: func() *Session {
|
|
s := &Session{
|
|
ID: "test-session-2",
|
|
info: &Info{
|
|
Status: string(StatusRunning),
|
|
Pid: 0,
|
|
},
|
|
}
|
|
return s
|
|
},
|
|
expectGraceful: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
session := tt.setupSession()
|
|
terminator := NewProcessTerminator(session)
|
|
|
|
err := terminator.TerminateGracefully()
|
|
|
|
if tt.expectGraceful && err != nil {
|
|
t.Errorf("TerminateGracefully() error = %v, want nil", err)
|
|
}
|
|
if !tt.expectGraceful && err == nil {
|
|
t.Error("TerminateGracefully() error = nil, want error")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestProcessTerminator_RealProcess(t *testing.T) {
|
|
// Skip in CI or on Windows
|
|
if os.Getenv("CI") == "true" || runtime.GOOS == "windows" {
|
|
t.Skip("Skipping real process test in CI/Windows")
|
|
}
|
|
|
|
// Start a sleep process that ignores SIGTERM
|
|
cmd := exec.Command("sh", "-c", "trap '' TERM; sleep 10")
|
|
if err := cmd.Start(); err != nil {
|
|
t.Skipf("Cannot start test process: %v", err)
|
|
}
|
|
|
|
session := &Session{
|
|
ID: "test-real-process",
|
|
info: &Info{
|
|
Status: string(StatusRunning),
|
|
Pid: cmd.Process.Pid,
|
|
},
|
|
}
|
|
|
|
// Skip cleanup tracking as cleanup is a method not a field
|
|
|
|
terminator := NewProcessTerminator(session)
|
|
terminator.gracefulTimeout = 1 * time.Second // Shorter timeout for test
|
|
terminator.checkInterval = 100 * time.Millisecond
|
|
|
|
start := time.Now()
|
|
err := terminator.TerminateGracefully()
|
|
elapsed := time.Since(start)
|
|
|
|
if err != nil {
|
|
t.Errorf("TerminateGracefully() error = %v", err)
|
|
}
|
|
|
|
// Should have waited about 1 second before SIGKILL
|
|
if elapsed < 900*time.Millisecond || elapsed > 1500*time.Millisecond {
|
|
t.Errorf("Expected termination after ~1s, but took %v", elapsed)
|
|
}
|
|
|
|
// Process should be dead now
|
|
if err := cmd.Process.Signal(os.Signal(nil)); err == nil {
|
|
t.Error("Process should be terminated")
|
|
}
|
|
}
|
|
|
|
func TestWaitForProcessExit(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
pid int
|
|
timeout time.Duration
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "non-existent process",
|
|
pid: 999999,
|
|
timeout: 100 * time.Millisecond,
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "current process (should not exit)",
|
|
pid: os.Getpid(),
|
|
timeout: 100 * time.Millisecond,
|
|
expected: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := waitForProcessExit(tt.pid, tt.timeout)
|
|
if result != tt.expected {
|
|
t.Errorf("waitForProcessExit() = %v, want %v", result, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIsProcessRunning(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
pid int
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "invalid pid",
|
|
pid: 0,
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "negative pid",
|
|
pid: -1,
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "current process",
|
|
pid: os.Getpid(),
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "non-existent process",
|
|
pid: 999999,
|
|
expected: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := isProcessRunning(tt.pid)
|
|
if result != tt.expected {
|
|
t.Errorf("isProcessRunning(%d) = %v, want %v", tt.pid, result, tt.expected)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestProcessTerminator_CheckInterval(t *testing.T) {
|
|
session := &Session{
|
|
ID: "test-session",
|
|
info: &Info{
|
|
Status: string(StatusRunning),
|
|
Pid: 999999, // Non-existent
|
|
},
|
|
}
|
|
|
|
terminator := NewProcessTerminator(session)
|
|
|
|
// Verify default values match Node.js
|
|
if terminator.gracefulTimeout != 3*time.Second {
|
|
t.Errorf("gracefulTimeout = %v, want 3s", terminator.gracefulTimeout)
|
|
}
|
|
if terminator.checkInterval != 500*time.Millisecond {
|
|
t.Errorf("checkInterval = %v, want 500ms", terminator.checkInterval)
|
|
}
|
|
}
|
|
|
|
func BenchmarkIsProcessRunning(b *testing.B) {
|
|
pid := os.Getpid()
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
isProcessRunning(pid)
|
|
}
|
|
}
|
|
|
|
func BenchmarkWaitForProcessExit(b *testing.B) {
|
|
// Use non-existent PID for immediate return
|
|
pid := 999999
|
|
timeout := 1 * time.Millisecond
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
waitForProcessExit(pid, timeout)
|
|
}
|
|
}
|