Skip to content

Commit 2e473ea

Browse files
committed
test: add integration tests for squash-merge detection
Added two new SSH-specific tests: - 'should allow deletion of squash-merged branches without force flag' Simulates a squash-merge scenario where branch content matches main but commits differ, verifies deletion succeeds without force. - 'should block deletion when branch has genuinely unmerged content' Verifies branches with content not in main still require force.
1 parent 552d94c commit 2e473ea

File tree

1 file changed

+154
-0
lines changed

1 file changed

+154
-0
lines changed

tests/ipcMain/removeWorkspace.test.ts

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -799,5 +799,159 @@ describeIntegration("Workspace deletion integration tests", () => {
799799
},
800800
TEST_TIMEOUT_SSH_MS
801801
);
802+
803+
test.concurrent(
804+
"should allow deletion of squash-merged branches without force flag",
805+
async () => {
806+
const env = await createTestEnvironment();
807+
const tempGitRepo = await createTempGitRepo();
808+
809+
try {
810+
const branchName = generateBranchName("squash-merge-test");
811+
const runtimeConfig = getRuntimeConfig(branchName);
812+
const { workspaceId } = await createWorkspaceWithInit(
813+
env,
814+
tempGitRepo,
815+
branchName,
816+
runtimeConfig,
817+
true, // waitForInit
818+
true // isSSH
819+
);
820+
821+
// Configure git for committing
822+
await executeBash(env, workspaceId, 'git config user.email "test@example.com"');
823+
await executeBash(env, workspaceId, 'git config user.name "Test User"');
824+
825+
// Set origin to be the tempGitRepo (local bare-ish repo)
826+
// This gives us a real origin to simulate squash-merge against
827+
await executeBash(
828+
env,
829+
workspaceId,
830+
`git remote set-url origin "${tempGitRepo}" || git remote add origin "${tempGitRepo}"`
831+
);
832+
833+
// Create feature commits on the branch
834+
await executeBash(env, workspaceId, 'echo "feature1" > feature.txt');
835+
await executeBash(env, workspaceId, "git add feature.txt");
836+
await executeBash(env, workspaceId, 'git commit -m "Feature commit 1"');
837+
838+
await executeBash(env, workspaceId, 'echo "feature2" >> feature.txt');
839+
await executeBash(env, workspaceId, "git add feature.txt");
840+
await executeBash(env, workspaceId, 'git commit -m "Feature commit 2"');
841+
842+
// Get the feature branch's final file content
843+
const featureContent = await executeBash(env, workspaceId, "cat feature.txt");
844+
845+
// Now simulate squash-merge: add the same content to main in the origin
846+
// First, check out main in the temp repo and add the squash commit
847+
using checkoutProc = execAsync(
848+
`cd "${tempGitRepo}" && git checkout main 2>/dev/null || git checkout -b main`
849+
);
850+
await checkoutProc.result;
851+
852+
// Create the squash commit with identical content
853+
using squashProc = execAsync(
854+
`cd "${tempGitRepo}" && echo "${featureContent.output.trim()}" > feature.txt && git add feature.txt && git commit -m "Squash: Feature commits"`
855+
);
856+
await squashProc.result;
857+
858+
// Fetch the updated origin in the workspace
859+
await executeBash(env, workspaceId, "git fetch origin");
860+
861+
// Verify we have unpushed commits (branch commits are not ancestors of origin/main)
862+
const logResult = await executeBash(
863+
env,
864+
workspaceId,
865+
"git log --branches --not --remotes --oneline"
866+
);
867+
// Should show commits since our branch commits != squash commit SHA
868+
expect(logResult.output.trim()).not.toBe("");
869+
870+
// Now attempt deletion without force - should succeed because content matches
871+
const deleteResult = await env.mockIpcRenderer.invoke(
872+
IPC_CHANNELS.WORKSPACE_REMOVE,
873+
workspaceId
874+
);
875+
876+
// Should succeed - squash-merge detection should recognize content is in main
877+
expect(deleteResult.success).toBe(true);
878+
879+
// Verify workspace was removed from config
880+
const config = env.config.loadConfigOrDefault();
881+
const project = config.projects.get(tempGitRepo);
882+
if (project) {
883+
const stillInConfig = project.workspaces.some((w) => w.id === workspaceId);
884+
expect(stillInConfig).toBe(false);
885+
}
886+
} finally {
887+
await cleanupTestEnvironment(env);
888+
await cleanupTempGitRepo(tempGitRepo);
889+
}
890+
},
891+
TEST_TIMEOUT_SSH_MS
892+
);
893+
894+
test.concurrent(
895+
"should block deletion when branch has genuinely unmerged content",
896+
async () => {
897+
const env = await createTestEnvironment();
898+
const tempGitRepo = await createTempGitRepo();
899+
900+
try {
901+
const branchName = generateBranchName("unmerged-content-test");
902+
const runtimeConfig = getRuntimeConfig(branchName);
903+
const { workspaceId } = await createWorkspaceWithInit(
904+
env,
905+
tempGitRepo,
906+
branchName,
907+
runtimeConfig,
908+
true, // waitForInit
909+
true // isSSH
910+
);
911+
912+
// Configure git for committing
913+
await executeBash(env, workspaceId, 'git config user.email "test@example.com"');
914+
await executeBash(env, workspaceId, 'git config user.name "Test User"');
915+
916+
// Set origin to be the tempGitRepo
917+
await executeBash(
918+
env,
919+
workspaceId,
920+
`git remote set-url origin "${tempGitRepo}" || git remote add origin "${tempGitRepo}"`
921+
);
922+
923+
// Create feature commits with unique content
924+
await executeBash(env, workspaceId, 'echo "unique-unmerged-content" > unique.txt');
925+
await executeBash(env, workspaceId, "git add unique.txt");
926+
await executeBash(env, workspaceId, 'git commit -m "Unique commit"');
927+
928+
// Fetch origin (main doesn't have our content)
929+
await executeBash(env, workspaceId, "git fetch origin");
930+
931+
// Attempt deletion without force - should fail because content differs
932+
const deleteResult = await env.mockIpcRenderer.invoke(
933+
IPC_CHANNELS.WORKSPACE_REMOVE,
934+
workspaceId
935+
);
936+
937+
// Should fail - genuinely unmerged content
938+
expect(deleteResult.success).toBe(false);
939+
expect(deleteResult.error).toMatch(/unpushed|changes/i);
940+
941+
// Verify workspace still exists
942+
const stillExists = await workspaceExists(env, workspaceId);
943+
expect(stillExists).toBe(true);
944+
945+
// Cleanup: force delete
946+
await env.mockIpcRenderer.invoke(IPC_CHANNELS.WORKSPACE_REMOVE, workspaceId, {
947+
force: true,
948+
});
949+
} finally {
950+
await cleanupTestEnvironment(env);
951+
await cleanupTempGitRepo(tempGitRepo);
952+
}
953+
},
954+
TEST_TIMEOUT_SSH_MS
955+
);
802956
});
803957
});

0 commit comments

Comments
 (0)