execute_test.go 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146
  1. package shell
  2. import (
  3. "os"
  4. "os/exec"
  5. "strings"
  6. "testing"
  7. "time"
  8. )
  9. // 执行正常的命令
  10. func TestExecute_Success(t *testing.T) {
  11. res, err := Execute(ExecuteParams{
  12. Cmd: `echo hello`,
  13. Timeout: 2,
  14. })
  15. if err != nil {
  16. t.Fatalf("unexpected error: %v", err)
  17. }
  18. if res.ExitCode != 0 {
  19. t.Fatalf("expected exit code 0, got %d", res.ExitCode)
  20. }
  21. if strings.TrimSpace(res.Stdout) != "hello" {
  22. t.Fatalf("unexpected stdout: %q", res.Stdout)
  23. }
  24. }
  25. // 执行命令不存在
  26. func TestExecute_CommandNotFound(t *testing.T) {
  27. _, err := Execute(ExecuteParams{
  28. Cmd: `this_command_should_not_exist_123`,
  29. Timeout: 1,
  30. })
  31. if err == nil {
  32. t.Fatalf("expected error, got nil")
  33. }
  34. }
  35. // 命令非零退出码
  36. func TestExecute_NonZeroExit(t *testing.T) {
  37. res, err := Execute(ExecuteParams{
  38. Cmd: `sh -c "exit 42"`,
  39. Timeout: 2,
  40. })
  41. if err != nil {
  42. t.Fatalf("unexpected error: %v", err)
  43. }
  44. if res.ExitCode != 42 {
  45. t.Fatalf("expected exit code 42, got %d", res.ExitCode)
  46. }
  47. }
  48. // 命令超时退出时
  49. func TestExecute_Timeout_KillProcessGroup(t *testing.T) {
  50. start := time.Now()
  51. res, err := Execute(ExecuteParams{
  52. Cmd: `sh -c "sleep 100 & wait"`,
  53. Timeout: 1,
  54. })
  55. elapsed := time.Since(start)
  56. if elapsed > 5*time.Second {
  57. t.Fatalf("Execute hung too long: %v", elapsed)
  58. }
  59. if res.ExitCode != 124 {
  60. t.Fatalf("expected exit code 124, got %d", res.ExitCode)
  61. }
  62. if err != nil && err != ErrExecutorLostControl {
  63. t.Fatalf("unexpected error: %v", err)
  64. }
  65. }
  66. // 命令输出超限时
  67. func TestExecute_OutputLimit(t *testing.T) {
  68. res, err := Execute(ExecuteParams{
  69. Cmd: `sh -c "yes | head -c 2097152"`, // 2MB
  70. Timeout: 2,
  71. })
  72. if err != nil {
  73. t.Fatalf("unexpected error: %v", err)
  74. }
  75. if len(res.Stdout) > 1<<20 {
  76. t.Fatalf("stdout exceeded limit: %d", len(res.Stdout))
  77. }
  78. }
  79. // 测试无孤儿进程
  80. func TestExecute_NoOrphanProcess(t *testing.T) {
  81. _, _ = Execute(ExecuteParams{
  82. Cmd: `sh -c "sleep 100 & wait"`,
  83. Timeout: 1,
  84. })
  85. time.Sleep(300 * time.Millisecond)
  86. out, _ := exec.Command("pgrep", "-f", "sleep 100").Output()
  87. if len(out) > 0 {
  88. t.Fatalf("orphan sleep process detected: %s", out)
  89. }
  90. }
  91. // 执行正常的命令
  92. func TestExecute_Ls(t *testing.T) {
  93. res, err := Execute(ExecuteParams{
  94. Cmd: "ls -l",
  95. Timeout: 2,
  96. })
  97. if err != nil {
  98. t.Fatalf("unexpected error: %v", err)
  99. }
  100. if res.ExitCode != 0 {
  101. t.Fatalf("expected exit code 0, got %d", res.ExitCode)
  102. }
  103. if strings.TrimSpace(res.Stdout) == "" {
  104. t.Fatalf("ls stdout should not be empty")
  105. }
  106. }
  107. // 展开"环境变量"
  108. func TestExecute_WithEnvExpansion(t *testing.T) {
  109. home := os.Getenv("HOME")
  110. if home == "" {
  111. t.Skip("HOME not set")
  112. }
  113. res, err := Execute(ExecuteParams{
  114. Cmd: `echo $HOME`,
  115. Timeout: 2,
  116. })
  117. if err != nil {
  118. t.Fatalf("unexpected error: %v", err)
  119. }
  120. if res.ExitCode != 0 {
  121. t.Fatalf("expected exit code 0, got %d", res.ExitCode)
  122. }
  123. if strings.TrimSpace(res.Stdout) != home {
  124. t.Fatalf("unexpected stdout: %q", res.Stdout)
  125. }
  126. }