1+ import { describe , test , expect } from "bun:test"
2+ import { $ } from "bun"
3+ import path from "path"
4+ import { tmpdir } from "../fixture/fixture"
5+ import fs from "fs/promises"
6+
7+ describe ( "taskctl start: branch creation resilience" , ( ) => {
8+ test ( "checks out existing branch instead of creating duplicate" , async ( ) => {
9+ await using tmp = await tmpdir ( { git : true } )
10+
11+ const branchName = "feature/test-branch"
12+
13+ // Create the branch once
14+ await $ `git checkout -b ${ branchName } ` . cwd ( tmp . path ) . quiet ( ) . nothrow ( )
15+ const firstCheckout = await $ `git rev-parse --abbrev-ref HEAD` . cwd ( tmp . path ) . quiet ( ) . nothrow ( )
16+ expect ( new TextDecoder ( ) . decode ( firstCheckout . stdout ) . trim ( ) ) . toBe ( branchName )
17+
18+ // Make a commit to establish history
19+ await Bun . write ( path . join ( tmp . path , "test.txt" ) , "content" )
20+ await $ `git add test.txt` . cwd ( tmp . path ) . quiet ( ) . nothrow ( )
21+ await $ `git commit -m "test commit"` . cwd ( tmp . path ) . quiet ( ) . nothrow ( )
22+
23+ // Switch back to main
24+ await $ `git checkout -` . cwd ( tmp . path ) . quiet ( ) . nothrow ( )
25+
26+ // Verify branch exists
27+ const branchCheck = await $ `git rev-parse --verify ${ branchName } ` . cwd ( tmp . path ) . quiet ( ) . nothrow ( )
28+ expect ( branchCheck . exitCode ) . toBe ( 0 )
29+
30+ // Simulate the start command logic: check if branch exists
31+ const existsCheck = await $ `git rev-parse --verify ${ branchName } ` . cwd ( tmp . path ) . quiet ( ) . nothrow ( )
32+
33+ if ( existsCheck . exitCode === 0 ) {
34+ // Branch exists — check it out
35+ const checkoutResult = await $ `git checkout ${ branchName } ` . cwd ( tmp . path ) . quiet ( ) . nothrow ( )
36+ expect ( checkoutResult . exitCode ) . toBe ( 0 )
37+
38+ const currentBranch = await $ `git rev-parse --abbrev-ref HEAD` . cwd ( tmp . path ) . quiet ( ) . nothrow ( )
39+ expect ( new TextDecoder ( ) . decode ( currentBranch . stdout ) . trim ( ) ) . toBe ( branchName )
40+ } else {
41+ throw new Error ( "Branch should exist" )
42+ }
43+ } )
44+
45+ test ( "creates new branch when it doesn't exist" , async ( ) => {
46+ await using tmp = await tmpdir ( { git : true } )
47+
48+ const branchName = "feature/new-branch-test"
49+
50+ // Verify branch doesn't exist
51+ const branchCheck = await $ `git rev-parse --verify ${ branchName } ` . cwd ( tmp . path ) . quiet ( ) . nothrow ( )
52+ expect ( branchCheck . exitCode ) . not . toBe ( 0 )
53+
54+ // Simulate the start command logic: branch doesn't exist, create it
55+ const base = "main"
56+ const createResult = await $ `git checkout -b ${ branchName } ${ base } ` . cwd ( tmp . path ) . quiet ( ) . nothrow ( )
57+ expect ( createResult . exitCode ) . toBe ( 0 )
58+
59+ const currentBranch = await $ `git rev-parse --abbrev-ref HEAD` . cwd ( tmp . path ) . quiet ( ) . nothrow ( )
60+ expect ( new TextDecoder ( ) . decode ( currentBranch . stdout ) . trim ( ) ) . toBe ( branchName )
61+ } )
62+
63+ test ( "handles remote branch already exists gracefully" , async ( ) => {
64+ await using tmp = await tmpdir ( { git : true } )
65+
66+ const branchName = "feature/remote-exists-test"
67+
68+ // Create branch
69+ await $ `git checkout -b ${ branchName } ` . cwd ( tmp . path ) . quiet ( ) . nothrow ( )
70+ await Bun . write ( path . join ( tmp . path , "test.txt" ) , "content" )
71+ await $ `git add test.txt` . cwd ( tmp . path ) . quiet ( ) . nothrow ( )
72+ await $ `git commit -m "test commit"` . cwd ( tmp . path ) . quiet ( ) . nothrow ( )
73+
74+ // Add a fake remote that points to the current directory
75+ const remotePath = tmp . path
76+ await $ `git remote add test-remote ${ remotePath } ` . cwd ( tmp . path ) . quiet ( ) . nothrow ( )
77+
78+ // Push to the remote
79+ const pushResult = await $ `git push test-remote ${ branchName } ` . cwd ( tmp . path ) . quiet ( ) . nothrow ( ) . nothrow ( )
80+
81+ // If push failed with "already exists" error (which it shouldn't in this case, but we test the logic)
82+ // then we would set upstream manually
83+ if ( pushResult . exitCode !== 0 ) {
84+ const stderr = pushResult . stderr ? new TextDecoder ( ) . decode ( pushResult . stderr ) : ""
85+ if ( stderr . includes ( "already exists" ) || stderr . includes ( "would clobber" ) ) {
86+ await $ `git branch --set-upstream-to=test-remote/${ branchName } ${ branchName } ` . cwd ( tmp . path ) . quiet ( ) . nothrow ( )
87+ }
88+ }
89+
90+ // The branch should be checked out and ready
91+ const currentBranch = await $ `git rev-parse --abbrev-ref HEAD` . cwd ( tmp . path ) . quiet ( ) . nothrow ( )
92+ expect ( new TextDecoder ( ) . decode ( currentBranch . stdout ) . trim ( ) ) . toBe ( branchName )
93+ } )
94+ } )
0 commit comments