@@ -75,6 +75,52 @@ describe('Session', () => {
7575 // Session directory should be created (we can't easily check without accessing private fields)
7676 expect ( session . isReady ( ) ) . toBe ( true ) ;
7777 } ) ;
78+
79+ it ( 'should initialize with non-existent cwd by falling back to / (issue #288)' , async ( ) => {
80+ // This tests the fix for issue #288 where session creation failed
81+ // if /workspace was deleted before a new session was created
82+ session = new Session ( {
83+ id : 'test-session-nonexistent-cwd' ,
84+ cwd : '/nonexistent/path/that/does/not/exist'
85+ } ) ;
86+
87+ // This should NOT throw - instead it should fall back to /
88+ await session . initialize ( ) ;
89+
90+ expect ( session . isReady ( ) ) . toBe ( true ) ;
91+
92+ // Verify we can execute commands
93+ const result = await session . exec ( 'pwd' ) ;
94+ expect ( result . exitCode ) . toBe ( 0 ) ;
95+ // The shell should have started in / since the requested cwd doesn't exist
96+ expect ( result . stdout . trim ( ) ) . toBe ( '/' ) ;
97+ } ) ;
98+
99+ it ( 'should initialize with missing /workspace by falling back to / (issue #288)' , async ( ) => {
100+ // Simulate the exact scenario from issue #288
101+ // Create a workspace, then delete it, then try to create a session with it
102+ const workspaceDir = join ( testDir , 'workspace' ) ;
103+ await mkdir ( workspaceDir , { recursive : true } ) ;
104+
105+ // Delete the workspace
106+ await rm ( workspaceDir , { recursive : true , force : true } ) ;
107+
108+ // Now try to create a session with the deleted workspace as cwd
109+ session = new Session ( {
110+ id : 'test-session-deleted-workspace' ,
111+ cwd : workspaceDir
112+ } ) ;
113+
114+ // This should NOT throw - instead it should fall back to /
115+ await session . initialize ( ) ;
116+
117+ expect ( session . isReady ( ) ) . toBe ( true ) ;
118+
119+ // Verify we can execute commands
120+ const result = await session . exec ( 'echo "session works"' ) ;
121+ expect ( result . exitCode ) . toBe ( 0 ) ;
122+ expect ( result . stdout . trim ( ) ) . toBe ( 'session works' ) ;
123+ } ) ;
78124 } ) ;
79125
80126 describe ( 'exec' , ( ) => {
@@ -496,6 +542,92 @@ describe('Session', () => {
496542 expect ( result . exitCode ) . toBe ( 1 ) ;
497543 expect ( result . stderr ) . toContain ( 'Failed to change directory' ) ;
498544 } ) ;
545+
546+ it ( 'should continue working after session cwd is deleted (issue #288)' , async ( ) => {
547+ // Create a working directory for the session
548+ const workspaceDir = join ( testDir , 'workspace' ) ;
549+ await mkdir ( workspaceDir , { recursive : true } ) ;
550+
551+ session = new Session ( {
552+ id : 'test-cwd-deletion' ,
553+ cwd : workspaceDir
554+ } ) ;
555+
556+ await session . initialize ( ) ;
557+
558+ // Verify baseline works
559+ const baseline = await session . exec ( 'echo "baseline"' ) ;
560+ expect ( baseline . exitCode ) . toBe ( 0 ) ;
561+ expect ( baseline . stdout . trim ( ) ) . toBe ( 'baseline' ) ;
562+
563+ // Delete the workspace directory (this is the bug scenario)
564+ await session . exec ( `rm -rf ${ workspaceDir } ` ) ;
565+
566+ // Try a subsequent command - this should NOT fail with an obscure error
567+ // It should either work (falling back to /) or give a clear error message
568+ const afterRemoval = await session . exec ( 'echo "after removal"' ) ;
569+
570+ // The command should succeed - bash can still run commands even if cwd is deleted
571+ // It will use the deleted directory's inode until a cd happens
572+ expect ( afterRemoval . exitCode ) . toBe ( 0 ) ;
573+ expect ( afterRemoval . stdout . trim ( ) ) . toBe ( 'after removal' ) ;
574+ } ) ;
575+
576+ it ( 'should handle cwd being replaced with symlink (issue #288)' , async ( ) => {
577+ // Create directories for the test
578+ const workspaceDir = join ( testDir , 'workspace' ) ;
579+ const backupDir = join ( testDir , 'backup' ) ;
580+ await mkdir ( workspaceDir , { recursive : true } ) ;
581+ await mkdir ( backupDir , { recursive : true } ) ;
582+
583+ session = new Session ( {
584+ id : 'test-cwd-symlink' ,
585+ cwd : workspaceDir
586+ } ) ;
587+
588+ await session . initialize ( ) ;
589+
590+ // Verify baseline works
591+ const baseline = await session . exec ( 'echo "baseline"' ) ;
592+ expect ( baseline . exitCode ) . toBe ( 0 ) ;
593+ expect ( baseline . stdout . trim ( ) ) . toBe ( 'baseline' ) ;
594+
595+ // Replace workspace with a symlink to backup directory
596+ await session . exec (
597+ `rm -rf ${ workspaceDir } && ln -sf ${ backupDir } ${ workspaceDir } `
598+ ) ;
599+
600+ // Try a subsequent command - should continue working
601+ const afterSymlink = await session . exec ( 'echo "after symlink"' ) ;
602+ expect ( afterSymlink . exitCode ) . toBe ( 0 ) ;
603+ expect ( afterSymlink . stdout . trim ( ) ) . toBe ( 'after symlink' ) ;
604+ } ) ;
605+
606+ it ( 'should be recoverable by cd-ing to valid directory after cwd deletion' , async ( ) => {
607+ // Create a working directory for the session
608+ const workspaceDir = join ( testDir , 'workspace' ) ;
609+ await mkdir ( workspaceDir , { recursive : true } ) ;
610+
611+ session = new Session ( {
612+ id : 'test-cwd-recovery' ,
613+ cwd : workspaceDir
614+ } ) ;
615+
616+ await session . initialize ( ) ;
617+
618+ // Delete the workspace directory
619+ await session . exec ( `rm -rf ${ workspaceDir } ` ) ;
620+
621+ // Change to a valid directory - this should work and recover the session
622+ const cdResult = await session . exec ( 'cd /tmp && pwd' ) ;
623+ expect ( cdResult . exitCode ) . toBe ( 0 ) ;
624+ expect ( cdResult . stdout . trim ( ) ) . toBe ( '/tmp' ) ;
625+
626+ // Subsequent commands should work
627+ const afterCd = await session . exec ( 'echo "recovered"' ) ;
628+ expect ( afterCd . exitCode ) . toBe ( 0 ) ;
629+ expect ( afterCd . stdout . trim ( ) ) . toBe ( 'recovered' ) ;
630+ } ) ;
499631 } ) ;
500632
501633 describe ( 'FIFO cleanup' , ( ) => {
0 commit comments