@@ -356,141 +356,6 @@ describeIntegration("Workspace deletion integration tests", () => {
356356 TEST_TIMEOUT
357357 ) ;
358358
359- test . concurrent (
360- "should fail to delete SSH workspace with unpushed refs without force flag" ,
361- async ( ) => {
362- // This test only applies to SSH runtime (local worktrees share .git so unpushed refs are safe)
363- if ( type !== "ssh" ) {
364- return ;
365- }
366-
367- const env = await createTestEnvironment ( ) ;
368- const tempGitRepo = await createTempGitRepo ( ) ;
369-
370- try {
371- const branchName = generateBranchName ( "delete-unpushed" ) ;
372- const runtimeConfig = getRuntimeConfig ( branchName ) ;
373- const { workspaceId } = await createWorkspaceWithInit (
374- env ,
375- tempGitRepo ,
376- branchName ,
377- runtimeConfig ,
378- true , // waitForInit
379- true // isSSH
380- ) ;
381-
382- // Configure git for committing (SSH environment needs this)
383- await executeBash ( env , workspaceId , 'git config user.email "test@example.com"' ) ;
384- await executeBash ( env , workspaceId , 'git config user.name "Test User"' ) ;
385-
386- // Add a fake remote (needed for unpushed check to work)
387- // Without a remote, SSH workspaces have no concept of "unpushed" commits
388- await executeBash (
389- env ,
390- workspaceId ,
391- "git remote add origin https://github.com/fake/repo.git"
392- ) ;
393-
394- // Create a commit in the workspace (unpushed)
395- await executeBash ( env , workspaceId , 'echo "new content" > newfile.txt' ) ;
396- await executeBash ( env , workspaceId , "git add newfile.txt" ) ;
397- await executeBash ( env , workspaceId , 'git commit -m "Unpushed commit"' ) ;
398-
399- // Verify commit was created and working tree is clean
400- const statusResult = await executeBash ( env , workspaceId , "git status --porcelain" ) ;
401- expect ( statusResult . output . trim ( ) ) . toBe ( "" ) ; // Should be clean
402-
403- // Attempt to delete without force should fail
404- const deleteResult = await env . mockIpcRenderer . invoke (
405- IPC_CHANNELS . WORKSPACE_REMOVE ,
406- workspaceId
407- ) ;
408- expect ( deleteResult . success ) . toBe ( false ) ;
409- expect ( deleteResult . error ) . toMatch ( / u n p u s h e d .* c o m m i t | u n p u s h e d .* r e f / i) ;
410-
411- // Verify workspace still exists
412- const stillExists = await workspaceExists ( env , workspaceId ) ;
413- expect ( stillExists ) . toBe ( true ) ;
414-
415- // Cleanup: force delete for cleanup
416- await env . mockIpcRenderer . invoke ( IPC_CHANNELS . WORKSPACE_REMOVE , workspaceId , {
417- force : true ,
418- } ) ;
419- } finally {
420- await cleanupTestEnvironment ( env ) ;
421- await cleanupTempGitRepo ( tempGitRepo ) ;
422- }
423- } ,
424- TEST_TIMEOUT
425- ) ;
426-
427- test . concurrent (
428- "should delete SSH workspace with unpushed refs when force flag is set" ,
429- async ( ) => {
430- // This test only applies to SSH runtime
431- if ( type !== "ssh" ) {
432- return ;
433- }
434-
435- const env = await createTestEnvironment ( ) ;
436- const tempGitRepo = await createTempGitRepo ( ) ;
437-
438- try {
439- const branchName = generateBranchName ( "delete-unpushed-force" ) ;
440- const runtimeConfig = getRuntimeConfig ( branchName ) ;
441- const { workspaceId } = await createWorkspaceWithInit (
442- env ,
443- tempGitRepo ,
444- branchName ,
445- runtimeConfig ,
446- true , // waitForInit
447- true // isSSH
448- ) ;
449-
450- // Configure git for committing (SSH environment needs this)
451- await executeBash ( env , workspaceId , 'git config user.email "test@example.com"' ) ;
452- await executeBash ( env , workspaceId , 'git config user.name "Test User"' ) ;
453-
454- // Add a fake remote (needed for unpushed check to work)
455- // Without a remote, SSH workspaces have no concept of "unpushed" commits
456- await executeBash (
457- env ,
458- workspaceId ,
459- "git remote add origin https://github.com/fake/repo.git"
460- ) ;
461-
462- // Create a commit in the workspace (unpushed)
463- await executeBash ( env , workspaceId , 'echo "new content" > newfile.txt' ) ;
464- await executeBash ( env , workspaceId , "git add newfile.txt" ) ;
465- await executeBash ( env , workspaceId , 'git commit -m "Unpushed commit"' ) ;
466-
467- // Verify commit was created and working tree is clean
468- const statusResult = await executeBash ( env , workspaceId , "git status --porcelain" ) ;
469- expect ( statusResult . output . trim ( ) ) . toBe ( "" ) ; // Should be clean
470-
471- // Delete with force should succeed
472- const deleteResult = await env . mockIpcRenderer . invoke (
473- IPC_CHANNELS . WORKSPACE_REMOVE ,
474- workspaceId ,
475- { force : true }
476- ) ;
477- expect ( deleteResult . success ) . toBe ( true ) ;
478-
479- // Verify workspace was removed from config
480- const config = env . config . loadConfigOrDefault ( ) ;
481- const project = config . projects . get ( tempGitRepo ) ;
482- if ( project ) {
483- const stillInConfig = project . workspaces . some ( ( w ) => w . id === workspaceId ) ;
484- expect ( stillInConfig ) . toBe ( false ) ;
485- }
486- } finally {
487- await cleanupTestEnvironment ( env ) ;
488- await cleanupTempGitRepo ( tempGitRepo ) ;
489- }
490- } ,
491- TEST_TIMEOUT
492- ) ;
493-
494359 // Submodule tests only apply to local runtime (SSH doesn't use git worktrees)
495360 if ( type === "local" ) {
496361 test . concurrent (
@@ -609,4 +474,145 @@ describeIntegration("Workspace deletion integration tests", () => {
609474 }
610475 }
611476 ) ;
477+
478+ // SSH-specific tests (unpushed refs only matter for SSH, not local worktrees which share .git)
479+ describe ( "SSH-only tests" , ( ) => {
480+ const getRuntimeConfig = ( branchName : string ) : RuntimeConfig | undefined => {
481+ if ( ! sshConfig ) {
482+ throw new Error ( "SSH config not initialized" ) ;
483+ }
484+ return {
485+ type : "ssh" ,
486+ host : `testuser@localhost` ,
487+ srcBaseDir : sshConfig . workdir ,
488+ identityFile : sshConfig . privateKeyPath ,
489+ port : sshConfig . port ,
490+ } ;
491+ } ;
492+
493+ test . concurrent (
494+ "should fail to delete SSH workspace with unpushed refs without force flag" ,
495+ async ( ) => {
496+ const env = await createTestEnvironment ( ) ;
497+ const tempGitRepo = await createTempGitRepo ( ) ;
498+
499+ try {
500+ const branchName = generateBranchName ( "delete-unpushed" ) ;
501+ const runtimeConfig = getRuntimeConfig ( branchName ) ;
502+ const { workspaceId } = await createWorkspaceWithInit (
503+ env ,
504+ tempGitRepo ,
505+ branchName ,
506+ runtimeConfig ,
507+ true , // waitForInit
508+ true // isSSH
509+ ) ;
510+
511+ // Configure git for committing (SSH environment needs this)
512+ await executeBash ( env , workspaceId , 'git config user.email "test@example.com"' ) ;
513+ await executeBash ( env , workspaceId , 'git config user.name "Test User"' ) ;
514+
515+ // Add a fake remote (needed for unpushed check to work)
516+ // Without a remote, SSH workspaces have no concept of "unpushed" commits
517+ await executeBash (
518+ env ,
519+ workspaceId ,
520+ "git remote add origin https://github.com/fake/repo.git"
521+ ) ;
522+
523+ // Create a commit in the workspace (unpushed)
524+ await executeBash ( env , workspaceId , 'echo "new content" > newfile.txt' ) ;
525+ await executeBash ( env , workspaceId , "git add newfile.txt" ) ;
526+ await executeBash ( env , workspaceId , 'git commit -m "Unpushed commit"' ) ;
527+
528+ // Verify commit was created and working tree is clean
529+ const statusResult = await executeBash ( env , workspaceId , "git status --porcelain" ) ;
530+ expect ( statusResult . output . trim ( ) ) . toBe ( "" ) ; // Should be clean
531+
532+ // Attempt to delete without force should fail
533+ const deleteResult = await env . mockIpcRenderer . invoke (
534+ IPC_CHANNELS . WORKSPACE_REMOVE ,
535+ workspaceId
536+ ) ;
537+ expect ( deleteResult . success ) . toBe ( false ) ;
538+ expect ( deleteResult . error ) . toMatch ( / u n p u s h e d .* c o m m i t | u n p u s h e d .* r e f / i) ;
539+
540+ // Verify workspace still exists
541+ const stillExists = await workspaceExists ( env , workspaceId ) ;
542+ expect ( stillExists ) . toBe ( true ) ;
543+
544+ // Cleanup: force delete for cleanup
545+ await env . mockIpcRenderer . invoke ( IPC_CHANNELS . WORKSPACE_REMOVE , workspaceId , {
546+ force : true ,
547+ } ) ;
548+ } finally {
549+ await cleanupTestEnvironment ( env ) ;
550+ await cleanupTempGitRepo ( tempGitRepo ) ;
551+ }
552+ } ,
553+ TEST_TIMEOUT_SSH_MS
554+ ) ;
555+
556+ test . concurrent (
557+ "should delete SSH workspace with unpushed refs when force flag is set" ,
558+ async ( ) => {
559+ const env = await createTestEnvironment ( ) ;
560+ const tempGitRepo = await createTempGitRepo ( ) ;
561+
562+ try {
563+ const branchName = generateBranchName ( "delete-unpushed-force" ) ;
564+ const runtimeConfig = getRuntimeConfig ( branchName ) ;
565+ const { workspaceId } = await createWorkspaceWithInit (
566+ env ,
567+ tempGitRepo ,
568+ branchName ,
569+ runtimeConfig ,
570+ true , // waitForInit
571+ true // isSSH
572+ ) ;
573+
574+ // Configure git for committing (SSH environment needs this)
575+ await executeBash ( env , workspaceId , 'git config user.email "test@example.com"' ) ;
576+ await executeBash ( env , workspaceId , 'git config user.name "Test User"' ) ;
577+
578+ // Add a fake remote (needed for unpushed check to work)
579+ // Without a remote, SSH workspaces have no concept of "unpushed" commits
580+ await executeBash (
581+ env ,
582+ workspaceId ,
583+ "git remote add origin https://github.com/fake/repo.git"
584+ ) ;
585+
586+ // Create a commit in the workspace (unpushed)
587+ await executeBash ( env , workspaceId , 'echo "new content" > newfile.txt' ) ;
588+ await executeBash ( env , workspaceId , "git add newfile.txt" ) ;
589+ await executeBash ( env , workspaceId , 'git commit -m "Unpushed commit"' ) ;
590+
591+ // Verify commit was created and working tree is clean
592+ const statusResult = await executeBash ( env , workspaceId , "git status --porcelain" ) ;
593+ expect ( statusResult . output . trim ( ) ) . toBe ( "" ) ; // Should be clean
594+
595+ // Delete with force should succeed
596+ const deleteResult = await env . mockIpcRenderer . invoke (
597+ IPC_CHANNELS . WORKSPACE_REMOVE ,
598+ workspaceId ,
599+ { force : true }
600+ ) ;
601+ expect ( deleteResult . success ) . toBe ( true ) ;
602+
603+ // Verify workspace was removed from config
604+ const config = env . config . loadConfigOrDefault ( ) ;
605+ const project = config . projects . get ( tempGitRepo ) ;
606+ if ( project ) {
607+ const stillInConfig = project . workspaces . some ( ( w ) => w . id === workspaceId ) ;
608+ expect ( stillInConfig ) . toBe ( false ) ;
609+ }
610+ } finally {
611+ await cleanupTestEnvironment ( env ) ;
612+ await cleanupTempGitRepo ( tempGitRepo ) ;
613+ }
614+ } ,
615+ TEST_TIMEOUT_SSH_MS
616+ ) ;
617+ } ) ;
612618} ) ;
0 commit comments