background_test.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. package shell
  2. import (
  3. "context"
  4. "strings"
  5. "testing"
  6. "time"
  7. )
  8. func TestBackgroundShellManager_Start(t *testing.T) {
  9. t.Parallel()
  10. ctx := context.Background()
  11. workingDir := t.TempDir()
  12. manager := GetBackgroundShellManager()
  13. bgShell, err := manager.Start(ctx, workingDir, nil, "echo 'hello world'", "")
  14. if err != nil {
  15. t.Fatalf("failed to start background shell: %v", err)
  16. }
  17. if bgShell.ID == "" {
  18. t.Error("expected shell ID to be non-empty")
  19. }
  20. // Wait for the command to complete
  21. bgShell.Wait()
  22. stdout, stderr, done, err := bgShell.GetOutput()
  23. if !done {
  24. t.Error("expected shell to be done")
  25. }
  26. if err != nil {
  27. t.Errorf("expected no error, got: %v", err)
  28. }
  29. if !strings.Contains(stdout, "hello world") {
  30. t.Errorf("expected stdout to contain 'hello world', got: %s", stdout)
  31. }
  32. if stderr != "" {
  33. t.Errorf("expected empty stderr, got: %s", stderr)
  34. }
  35. }
  36. func TestBackgroundShellManager_Get(t *testing.T) {
  37. t.Parallel()
  38. ctx := context.Background()
  39. workingDir := t.TempDir()
  40. manager := GetBackgroundShellManager()
  41. bgShell, err := manager.Start(ctx, workingDir, nil, "echo 'test'", "")
  42. if err != nil {
  43. t.Fatalf("failed to start background shell: %v", err)
  44. }
  45. // Retrieve the shell
  46. retrieved, ok := manager.Get(bgShell.ID)
  47. if !ok {
  48. t.Error("expected to find the background shell")
  49. }
  50. if retrieved.ID != bgShell.ID {
  51. t.Errorf("expected shell ID %s, got %s", bgShell.ID, retrieved.ID)
  52. }
  53. // Clean up
  54. manager.Kill(bgShell.ID)
  55. }
  56. func TestBackgroundShellManager_Kill(t *testing.T) {
  57. t.Parallel()
  58. ctx := context.Background()
  59. workingDir := t.TempDir()
  60. manager := GetBackgroundShellManager()
  61. // Start a long-running command
  62. bgShell, err := manager.Start(ctx, workingDir, nil, "sleep 10", "")
  63. if err != nil {
  64. t.Fatalf("failed to start background shell: %v", err)
  65. }
  66. // Kill it
  67. err = manager.Kill(bgShell.ID)
  68. if err != nil {
  69. t.Errorf("failed to kill background shell: %v", err)
  70. }
  71. // Verify it's no longer in the manager
  72. _, ok := manager.Get(bgShell.ID)
  73. if ok {
  74. t.Error("expected shell to be removed after kill")
  75. }
  76. // Verify the shell is done
  77. if !bgShell.IsDone() {
  78. t.Error("expected shell to be done after kill")
  79. }
  80. }
  81. func TestBackgroundShellManager_KillNonExistent(t *testing.T) {
  82. t.Parallel()
  83. manager := GetBackgroundShellManager()
  84. err := manager.Kill("non-existent-id")
  85. if err == nil {
  86. t.Error("expected error when killing non-existent shell")
  87. }
  88. }
  89. func TestBackgroundShell_IsDone(t *testing.T) {
  90. t.Parallel()
  91. ctx := context.Background()
  92. workingDir := t.TempDir()
  93. manager := GetBackgroundShellManager()
  94. bgShell, err := manager.Start(ctx, workingDir, nil, "echo 'quick'", "")
  95. if err != nil {
  96. t.Fatalf("failed to start background shell: %v", err)
  97. }
  98. // Wait a bit for the command to complete
  99. time.Sleep(100 * time.Millisecond)
  100. if !bgShell.IsDone() {
  101. t.Error("expected shell to be done")
  102. }
  103. // Clean up
  104. manager.Kill(bgShell.ID)
  105. }
  106. func TestBackgroundShell_WithBlockFuncs(t *testing.T) {
  107. t.Parallel()
  108. ctx := context.Background()
  109. workingDir := t.TempDir()
  110. manager := GetBackgroundShellManager()
  111. blockFuncs := []BlockFunc{
  112. CommandsBlocker([]string{"curl", "wget"}),
  113. }
  114. bgShell, err := manager.Start(ctx, workingDir, blockFuncs, "curl example.com", "")
  115. if err != nil {
  116. t.Fatalf("failed to start background shell: %v", err)
  117. }
  118. // Wait for the command to complete
  119. bgShell.Wait()
  120. stdout, stderr, done, execErr := bgShell.GetOutput()
  121. if !done {
  122. t.Error("expected shell to be done")
  123. }
  124. // The command should have been blocked
  125. output := stdout + stderr
  126. if !strings.Contains(output, "not allowed") && execErr == nil {
  127. t.Errorf("expected command to be blocked, got stdout: %s, stderr: %s, err: %v", stdout, stderr, execErr)
  128. }
  129. // Clean up
  130. manager.Kill(bgShell.ID)
  131. }
  132. func TestBackgroundShellManager_List(t *testing.T) {
  133. t.Parallel()
  134. ctx := context.Background()
  135. workingDir := t.TempDir()
  136. manager := GetBackgroundShellManager()
  137. // Start two shells
  138. bgShell1, err := manager.Start(ctx, workingDir, nil, "sleep 1", "")
  139. if err != nil {
  140. t.Fatalf("failed to start first background shell: %v", err)
  141. }
  142. bgShell2, err := manager.Start(ctx, workingDir, nil, "sleep 1", "")
  143. if err != nil {
  144. t.Fatalf("failed to start second background shell: %v", err)
  145. }
  146. ids := manager.List()
  147. // Check that both shells are in the list
  148. found1 := false
  149. found2 := false
  150. for _, id := range ids {
  151. if id == bgShell1.ID {
  152. found1 = true
  153. }
  154. if id == bgShell2.ID {
  155. found2 = true
  156. }
  157. }
  158. if !found1 {
  159. t.Errorf("expected to find shell %s in list", bgShell1.ID)
  160. }
  161. if !found2 {
  162. t.Errorf("expected to find shell %s in list", bgShell2.ID)
  163. }
  164. // Clean up
  165. manager.Kill(bgShell1.ID)
  166. manager.Kill(bgShell2.ID)
  167. }
  168. func TestBackgroundShellManager_KillAll(t *testing.T) {
  169. t.Parallel()
  170. ctx := context.Background()
  171. workingDir := t.TempDir()
  172. manager := GetBackgroundShellManager()
  173. // Start multiple long-running shells
  174. shell1, err := manager.Start(ctx, workingDir, nil, "sleep 10", "")
  175. if err != nil {
  176. t.Fatalf("failed to start shell 1: %v", err)
  177. }
  178. shell2, err := manager.Start(ctx, workingDir, nil, "sleep 10", "")
  179. if err != nil {
  180. t.Fatalf("failed to start shell 2: %v", err)
  181. }
  182. shell3, err := manager.Start(ctx, workingDir, nil, "sleep 10", "")
  183. if err != nil {
  184. t.Fatalf("failed to start shell 3: %v", err)
  185. }
  186. // Verify shells are running
  187. if shell1.IsDone() || shell2.IsDone() || shell3.IsDone() {
  188. t.Error("shells should not be done yet")
  189. }
  190. // Kill all shells
  191. manager.KillAll()
  192. // Verify all shells are done
  193. if !shell1.IsDone() {
  194. t.Error("shell1 should be done after KillAll")
  195. }
  196. if !shell2.IsDone() {
  197. t.Error("shell2 should be done after KillAll")
  198. }
  199. if !shell3.IsDone() {
  200. t.Error("shell3 should be done after KillAll")
  201. }
  202. // Verify they're removed from the manager
  203. if _, ok := manager.Get(shell1.ID); ok {
  204. t.Error("shell1 should be removed from manager")
  205. }
  206. if _, ok := manager.Get(shell2.ID); ok {
  207. t.Error("shell2 should be removed from manager")
  208. }
  209. if _, ok := manager.Get(shell3.ID); ok {
  210. t.Error("shell3 should be removed from manager")
  211. }
  212. // Verify list is empty (or doesn't contain our shells)
  213. ids := manager.List()
  214. for _, id := range ids {
  215. if id == shell1.ID || id == shell2.ID || id == shell3.ID {
  216. t.Errorf("shell %s should not be in list after KillAll", id)
  217. }
  218. }
  219. }