Skip to content

Commit 3b949e1

Browse files
committed
Add TDD test for trunk branch bug in createWorkspace
This test demonstrates that the trunk branch parameter is not being respected when creating a new workspace. The test: 1. Creates a custom trunk branch with unique content 2. Creates another branch with different content 3. Attempts to create a workspace specifying the custom trunk 4. Verifies the workspace contains content from custom trunk Expected behavior: workspace should be created from specified trunk Current behavior (bug): SSH runtime creates from HEAD instead of trunk This test should: - PASS for LocalRuntime (correctly uses trunk branch) - FAIL for SSHRuntime (ignores trunk branch, uses HEAD) The bug is in src/runtime/SSHRuntime.ts:592 where trunkBranch is renamed to _trunkBranch (unused) and line 618 creates branches from HEAD instead.
1 parent 86ef049 commit 3b949e1

File tree

1 file changed

+132
-0
lines changed

1 file changed

+132
-0
lines changed

tests/ipcMain/createWorkspace.test.ts

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,138 @@ describeIntegration("WORKSPACE_CREATE with both runtimes", () => {
270270
},
271271
TEST_TIMEOUT_MS
272272
);
273+
274+
test.concurrent(
275+
"creates new branch from specified trunk branch, not from default branch",
276+
async () => {
277+
const env = await createTestEnvironment();
278+
const tempGitRepo = await createTempGitRepo();
279+
280+
try {
281+
// Create a custom trunk branch with a unique commit
282+
const customTrunkBranch = "custom-trunk";
283+
await execAsync(
284+
`git checkout -b ${customTrunkBranch} && echo "custom-trunk-content" > trunk-file.txt && git add . && git commit -m "Custom trunk commit"`,
285+
{ cwd: tempGitRepo }
286+
);
287+
288+
// Create a different branch (which will become the default if we checkout to it)
289+
const otherBranch = "other-branch";
290+
await execAsync(
291+
`git checkout -b ${otherBranch} && echo "other-content" > other-file.txt && git add . && git commit -m "Other branch commit"`,
292+
{ cwd: tempGitRepo }
293+
);
294+
295+
// Switch back to the original default branch
296+
const defaultBranch = await detectDefaultTrunkBranch(tempGitRepo);
297+
await execAsync(`git checkout ${defaultBranch}`, { cwd: tempGitRepo });
298+
299+
// Now create a workspace specifying custom-trunk as the trunk branch
300+
const newBranchName = generateBranchName("from-custom-trunk");
301+
const runtimeConfig = getRuntimeConfig(newBranchName);
302+
303+
const { result, cleanup } = await createWorkspaceWithCleanup(
304+
env,
305+
tempGitRepo,
306+
newBranchName,
307+
customTrunkBranch, // Specify custom trunk branch
308+
runtimeConfig
309+
);
310+
311+
expect(result.success).toBe(true);
312+
if (!result.success) {
313+
throw new Error(
314+
`Failed to create workspace from custom trunk '${customTrunkBranch}': ${result.error}`
315+
);
316+
}
317+
318+
// Wait for workspace initialization to complete
319+
await new Promise((resolve) => setTimeout(resolve, getInitWaitTime()));
320+
321+
// Verify the new branch was created from custom-trunk, not from default branch
322+
const workspacePath = result.metadata.namedWorkspacePath;
323+
324+
// For LocalRuntime, check the worktree directly
325+
// For SSHRuntime, we need to check the remote workspace
326+
if (type === "local") {
327+
// Check that trunk-file.txt exists (from custom-trunk)
328+
const trunkFileExists = await fs
329+
.access(path.join(workspacePath, "trunk-file.txt"))
330+
.then(() => true)
331+
.catch(() => false);
332+
expect(trunkFileExists).toBe(true);
333+
334+
// Check that other-file.txt does NOT exist (from other-branch)
335+
const otherFileExists = await fs
336+
.access(path.join(workspacePath, "other-file.txt"))
337+
.then(() => true)
338+
.catch(() => false);
339+
expect(otherFileExists).toBe(false);
340+
341+
// Verify git log shows the custom trunk commit
342+
const { stdout: logOutput } = await execAsync(
343+
`git log --oneline --all`,
344+
{ cwd: workspacePath }
345+
);
346+
expect(logOutput).toContain("Custom trunk commit");
347+
} else if (type === "ssh" && sshConfig) {
348+
// For SSH runtime, check files on the remote host
349+
const checkFileCmd = `test -f ${workspacePath}/trunk-file.txt && echo "exists" || echo "missing"`;
350+
const checkFileStream = await env.mockIpcRenderer.invoke(
351+
IPC_CHANNELS.RUNTIME_EXEC,
352+
result.metadata.id,
353+
checkFileCmd,
354+
{ cwd: workspacePath, timeout: 10 }
355+
);
356+
357+
// Read stdout to check if file exists
358+
let checkOutput = "";
359+
const reader = checkFileStream.stdout.getReader();
360+
const decoder = new TextDecoder();
361+
try {
362+
while (true) {
363+
const { done, value } = await reader.read();
364+
if (done) break;
365+
checkOutput += decoder.decode(value, { stream: true });
366+
}
367+
} finally {
368+
reader.releaseLock();
369+
}
370+
371+
expect(checkOutput.trim()).toBe("exists");
372+
373+
// Check that other-file.txt does NOT exist
374+
const checkOtherFileCmd = `test -f ${workspacePath}/other-file.txt && echo "exists" || echo "missing"`;
375+
const checkOtherStream = await env.mockIpcRenderer.invoke(
376+
IPC_CHANNELS.RUNTIME_EXEC,
377+
result.metadata.id,
378+
checkOtherFileCmd,
379+
{ cwd: workspacePath, timeout: 10 }
380+
);
381+
382+
let checkOtherOutput = "";
383+
const otherReader = checkOtherStream.stdout.getReader();
384+
try {
385+
while (true) {
386+
const { done, value } = await otherReader.read();
387+
if (done) break;
388+
checkOtherOutput += decoder.decode(value, { stream: true });
389+
}
390+
} finally {
391+
otherReader.releaseLock();
392+
}
393+
394+
expect(checkOtherOutput.trim()).toBe("missing");
395+
}
396+
397+
await cleanup();
398+
} finally {
399+
await cleanupTestEnvironment(env);
400+
await cleanupTempGitRepo(tempGitRepo);
401+
}
402+
},
403+
TEST_TIMEOUT_MS
404+
);
273405
});
274406

275407
describe("Init hook execution", () => {

0 commit comments

Comments
 (0)