11import { describe , it , expect , beforeEach } from "bun:test" ;
22import { BackgroundProcessManager } from "./backgroundProcessManager" ;
33import { BashExecutionService } from "./bashExecutionService" ;
4+ import { LocalBackgroundExecutor } from "./localBackgroundExecutor" ;
5+
6+ // Helper to create manager with executor registered for a workspace
7+ function createManagerWithExecutor ( workspaceId : string ) : BackgroundProcessManager {
8+ const manager = new BackgroundProcessManager ( ) ;
9+ manager . registerExecutor ( workspaceId , new LocalBackgroundExecutor ( new BashExecutionService ( ) ) ) ;
10+ return manager ;
11+ }
412
513describe ( "BackgroundProcessManager" , ( ) => {
614 let manager : BackgroundProcessManager ;
7- let bashService : BashExecutionService ;
15+ const testWorkspaceId = "workspace-1" ;
16+ const testWorkspaceId2 = "workspace-2" ;
817
918 beforeEach ( ( ) => {
10- bashService = new BashExecutionService ( ) ;
11- manager = new BackgroundProcessManager ( bashService ) ;
19+ manager = createManagerWithExecutor ( testWorkspaceId ) ;
1220 } ) ;
1321
1422 describe ( "spawn" , ( ) => {
1523 it ( "should spawn a background process and return process ID" , async ( ) => {
16- const result = await manager . spawn ( "workspace-1" , "echo hello" , {
24+ const result = await manager . spawn ( testWorkspaceId , "echo hello" , {
1725 cwd : process . cwd ( ) ,
1826 } ) ;
1927
@@ -23,16 +31,28 @@ describe("BackgroundProcessManager", () => {
2331 }
2432 } ) ;
2533
34+ it ( "should return error when no executor is registered" , async ( ) => {
35+ const managerNoExecutor = new BackgroundProcessManager ( ) ;
36+ const result = await managerNoExecutor . spawn ( "unregistered-workspace" , "echo hello" , {
37+ cwd : process . cwd ( ) ,
38+ } ) ;
39+
40+ expect ( result . success ) . toBe ( false ) ;
41+ if ( ! result . success ) {
42+ expect ( result . error ) . toContain ( "No executor registered" ) ;
43+ }
44+ } ) ;
45+
2646 it ( "should return error on spawn failure" , async ( ) => {
27- const result = await manager . spawn ( "workspace-1" , "echo test" , {
47+ const result = await manager . spawn ( testWorkspaceId , "echo test" , {
2848 cwd : "/nonexistent/path/that/does/not/exist" ,
2949 } ) ;
3050
3151 expect ( result . success ) . toBe ( false ) ;
3252 } ) ;
3353
3454 it ( "should capture stdout and stderr" , async ( ) => {
35- const result = await manager . spawn ( "workspace-1" , "echo hello; echo world >&2" , {
55+ const result = await manager . spawn ( testWorkspaceId , "echo hello; echo world >&2" , {
3656 cwd : process . cwd ( ) ,
3757 } ) ;
3858
@@ -55,7 +75,7 @@ describe("BackgroundProcessManager", () => {
5575 . map ( ( _ , i ) => `echo line${ i } ` )
5676 . join ( "; " ) ;
5777
58- const result = await manager . spawn ( "workspace-1" , script , {
78+ const result = await manager . spawn ( testWorkspaceId , script , {
5979 cwd : process . cwd ( ) ,
6080 } ) ;
6181
@@ -73,7 +93,7 @@ describe("BackgroundProcessManager", () => {
7393
7494 describe ( "getProcess" , ( ) => {
7595 it ( "should return process by ID" , async ( ) => {
76- const spawnResult = await manager . spawn ( "workspace-1" , "sleep 1" , {
96+ const spawnResult = await manager . spawn ( testWorkspaceId , "sleep 1" , {
7797 cwd : process . cwd ( ) ,
7898 } ) ;
7999
@@ -93,30 +113,36 @@ describe("BackgroundProcessManager", () => {
93113
94114 describe ( "list" , ( ) => {
95115 it ( "should list all processes" , async ( ) => {
96- await manager . spawn ( "workspace-1" , "sleep 1" , { cwd : process . cwd ( ) } ) ;
97- await manager . spawn ( "workspace-1" , "sleep 1" , { cwd : process . cwd ( ) } ) ;
116+ await manager . spawn ( testWorkspaceId , "sleep 1" , { cwd : process . cwd ( ) } ) ;
117+ await manager . spawn ( testWorkspaceId , "sleep 1" , { cwd : process . cwd ( ) } ) ;
98118
99119 const processes = manager . list ( ) ;
100120 expect ( processes . length ) . toBeGreaterThanOrEqual ( 2 ) ;
101121 } ) ;
102122
103123 it ( "should filter by workspace ID" , async ( ) => {
104- await manager . spawn ( "workspace-1" , "sleep 1" , { cwd : process . cwd ( ) } ) ;
105- await manager . spawn ( "workspace-2" , "sleep 1" , { cwd : process . cwd ( ) } ) ;
124+ // Register second workspace executor
125+ manager . registerExecutor (
126+ testWorkspaceId2 ,
127+ new LocalBackgroundExecutor ( new BashExecutionService ( ) )
128+ ) ;
106129
107- const ws1Processes = manager . list ( "workspace-1" ) ;
108- const ws2Processes = manager . list ( "workspace-2" ) ;
130+ await manager . spawn ( testWorkspaceId , "sleep 1" , { cwd : process . cwd ( ) } ) ;
131+ await manager . spawn ( testWorkspaceId2 , "sleep 1" , { cwd : process . cwd ( ) } ) ;
132+
133+ const ws1Processes = manager . list ( testWorkspaceId ) ;
134+ const ws2Processes = manager . list ( testWorkspaceId2 ) ;
109135
110136 expect ( ws1Processes . length ) . toBeGreaterThanOrEqual ( 1 ) ;
111137 expect ( ws2Processes . length ) . toBeGreaterThanOrEqual ( 1 ) ;
112- expect ( ws1Processes . every ( ( p ) => p . workspaceId === "workspace-1" ) ) . toBe ( true ) ;
113- expect ( ws2Processes . every ( ( p ) => p . workspaceId === "workspace-2" ) ) . toBe ( true ) ;
138+ expect ( ws1Processes . every ( ( p ) => p . workspaceId === testWorkspaceId ) ) . toBe ( true ) ;
139+ expect ( ws2Processes . every ( ( p ) => p . workspaceId === testWorkspaceId2 ) ) . toBe ( true ) ;
114140 } ) ;
115141 } ) ;
116142
117143 describe ( "terminate" , ( ) => {
118144 it ( "should terminate a running process" , async ( ) => {
119- const spawnResult = await manager . spawn ( "workspace-1" , "sleep 10" , {
145+ const spawnResult = await manager . spawn ( testWorkspaceId , "sleep 10" , {
120146 cwd : process . cwd ( ) ,
121147 } ) ;
122148
@@ -135,7 +161,7 @@ describe("BackgroundProcessManager", () => {
135161 } ) ;
136162
137163 it ( "should be idempotent (double-terminate succeeds)" , async ( ) => {
138- const spawnResult = await manager . spawn ( "workspace-1" , "sleep 10" , {
164+ const spawnResult = await manager . spawn ( testWorkspaceId , "sleep 10" , {
139165 cwd : process . cwd ( ) ,
140166 } ) ;
141167
@@ -151,17 +177,28 @@ describe("BackgroundProcessManager", () => {
151177
152178 describe ( "cleanup" , ( ) => {
153179 it ( "should kill all processes for a workspace and remove them from memory" , async ( ) => {
154- await manager . spawn ( "workspace-1" , "sleep 10" , { cwd : process . cwd ( ) } ) ;
155- await manager . spawn ( "workspace-1" , "sleep 10" , { cwd : process . cwd ( ) } ) ;
156- await manager . spawn ( "workspace-2" , "sleep 10" , { cwd : process . cwd ( ) } ) ;
180+ // Register second workspace executor
181+ manager . registerExecutor (
182+ testWorkspaceId2 ,
183+ new LocalBackgroundExecutor ( new BashExecutionService ( ) )
184+ ) ;
157185
158- await manager . cleanup ( "workspace-1" ) ;
186+ await manager . spawn ( testWorkspaceId , "sleep 10" , { cwd : process . cwd ( ) } ) ;
187+ await manager . spawn ( testWorkspaceId , "sleep 10" , { cwd : process . cwd ( ) } ) ;
188+ await manager . spawn ( testWorkspaceId2 , "sleep 10" , { cwd : process . cwd ( ) } ) ;
159189
160- const ws1Processes = manager . list ( "workspace-1" ) ;
161- const ws2Processes = manager . list ( "workspace-2" ) ;
190+ await manager . cleanup ( testWorkspaceId ) ;
162191
163- // All workspace-1 processes should be removed from memory
192+ const ws1Processes = manager . list ( testWorkspaceId ) ;
193+ const ws2Processes = manager . list ( testWorkspaceId2 ) ;
194+ // All testWorkspaceId processes should be removed from memory
164195 expect ( ws1Processes . length ) . toBe ( 0 ) ;
196+ // Executor should also be unregistered - spawning should fail
197+ const spawnResult = await manager . spawn ( testWorkspaceId , "echo test" , { cwd : process . cwd ( ) } ) ;
198+ expect ( spawnResult . success ) . toBe ( false ) ;
199+ if ( ! spawnResult . success ) {
200+ expect ( spawnResult . error ) . toContain ( "No executor registered" ) ;
201+ }
165202 // workspace-2 processes should still exist and be running
166203 expect ( ws2Processes . length ) . toBeGreaterThanOrEqual ( 1 ) ;
167204 expect ( ws2Processes . some ( ( p ) => p . status === "running" ) ) . toBe ( true ) ;
@@ -170,7 +207,7 @@ describe("BackgroundProcessManager", () => {
170207
171208 describe ( "process state tracking" , ( ) => {
172209 it ( "should track process exit" , async ( ) => {
173- const result = await manager . spawn ( "workspace-1" , "exit 42" , {
210+ const result = await manager . spawn ( testWorkspaceId , "exit 42" , {
174211 cwd : process . cwd ( ) ,
175212 } ) ;
176213
@@ -186,7 +223,7 @@ describe("BackgroundProcessManager", () => {
186223 } ) ;
187224
188225 it ( "should keep buffer after process exits" , async ( ) => {
189- const result = await manager . spawn ( "workspace-1" , "echo test; exit 0" , {
226+ const result = await manager . spawn ( testWorkspaceId , "echo test; exit 0" , {
190227 cwd : process . cwd ( ) ,
191228 } ) ;
192229
@@ -201,7 +238,7 @@ describe("BackgroundProcessManager", () => {
201238
202239 it ( "should preserve killed status after onExit callback fires" , async ( ) => {
203240 // Spawn a long-running process
204- const result = await manager . spawn ( "workspace-1" , "sleep 60" , {
241+ const result = await manager . spawn ( testWorkspaceId , "sleep 60" , {
205242 cwd : process . cwd ( ) ,
206243 } ) ;
207244
0 commit comments