background_test.go 6.3 KB

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