55 "os"
66 "os/exec"
77 "path/filepath"
8+ "sync"
89 "testing"
910
1011 "github.com/GrayCodeAI/trace/cli/checkpoint"
@@ -19,6 +20,16 @@ import (
1920 "github.com/stretchr/testify/require"
2021)
2122
23+ // gitCLITestMu serializes git CLI tests that call t.Chdir(). Parallel tests in
24+ // this package can otherwise race on process cwd and temp-repo cleanup under -race.
25+ var gitCLITestMu sync.Mutex
26+
27+ func acquireGitCLITest (t * testing.T ) {
28+ t .Helper ()
29+ gitCLITestMu .Lock ()
30+ t .Cleanup (gitCLITestMu .Unlock )
31+ }
32+
2233func TestHasUnpushedSessionsCommon (t * testing.T ) {
2334 t .Parallel ()
2435
@@ -183,6 +194,8 @@ func TestPushBranchIfNeeded_LocalBareRepo_PushesSuccessfully(t *testing.T) {
183194//
184195// Not parallel: uses t.Chdir() (required for OpenRepository).
185196func TestFetchAndRebase_DivergedBranches (t * testing.T ) {
197+ acquireGitCLITest (t )
198+
186199 ctx := context .Background ()
187200 branchName := paths .MetadataBranchName
188201
@@ -221,10 +234,8 @@ func TestFetchAndRebase_DivergedBranches(t *testing.T) {
221234 gitRun (workDir , "checkout" , "main" )
222235
223236 // 2. Clone into two separate working directories
224- cloneA := filepath .Join (t .TempDir (), "cloneA" )
225- cloneB := filepath .Join (t .TempDir (), "cloneB" )
226- require .NoError (t , os .MkdirAll (cloneA , 0o755 ))
227- require .NoError (t , os .MkdirAll (cloneB , 0o755 ))
237+ cloneA := t .TempDir ()
238+ cloneB := t .TempDir ()
228239
229240 gitRun (cloneA , "clone" , bareDir , "." )
230241 gitRun (cloneA , "config" , "user.email" , "a@test.com" )
@@ -307,6 +318,8 @@ func TestFetchAndRebase_DivergedBranches(t *testing.T) {
307318//
308319// Not parallel: uses t.Chdir() (required for OpenRepository).
309320func TestFetchAndRebase_LocalBehind (t * testing.T ) {
321+ acquireGitCLITest (t )
322+
310323 ctx := context .Background ()
311324 branchName := paths .MetadataBranchName
312325
@@ -343,8 +356,7 @@ func TestFetchAndRebase_LocalBehind(t *testing.T) {
343356 gitRun (workDir , "checkout" , "main" )
344357
345358 // Clone
346- cloneDir := filepath .Join (t .TempDir (), "clone" )
347- require .NoError (t , os .MkdirAll (cloneDir , 0o755 ))
359+ cloneDir := t .TempDir ()
348360 gitRun (cloneDir , "clone" , bareDir , "." )
349361 gitRun (cloneDir , "config" , "user.email" , "test@test.com" )
350362 gitRun (cloneDir , "config" , "user.name" , "Test User" )
@@ -387,6 +399,8 @@ func TestFetchAndRebase_LocalBehind(t *testing.T) {
387399//
388400// Not parallel: uses t.Chdir() (required for OpenRepository).
389401func TestFetchAndRebase_MergeBaseOnSecondParent_DoesNotReplayAncestors (t * testing.T ) {
402+ acquireGitCLITest (t )
403+
390404 ctx := context .Background ()
391405 branchName := paths .MetadataBranchName
392406
@@ -425,10 +439,8 @@ func TestFetchAndRebase_MergeBaseOnSecondParent_DoesNotReplayAncestors(t *testin
425439 gitRun (setupDir , "checkout" , "main" )
426440
427441 // Clone twice: local gets the old merge-commit history, remote advances later.
428- cloneLocal := filepath .Join (t .TempDir (), "clone-local" )
429- cloneRemote := filepath .Join (t .TempDir (), "clone-remote" )
430- require .NoError (t , os .MkdirAll (cloneLocal , 0o755 ))
431- require .NoError (t , os .MkdirAll (cloneRemote , 0o755 ))
442+ cloneLocal := t .TempDir ()
443+ cloneRemote := t .TempDir ()
432444
433445 for _ , dir := range []string {cloneLocal , cloneRemote } {
434446 gitRun (dir , "clone" , bareDir , "." )
@@ -518,6 +530,8 @@ func TestFetchAndRebase_MergeBaseOnSecondParent_DoesNotReplayAncestors(t *testin
518530//
519531// Not parallel: uses t.Chdir() (required for OpenRepository).
520532func TestFetchAndRebase_DoesNotResurrectRemoteOnlyCheckpointFromMerge (t * testing.T ) {
533+ acquireGitCLITest (t )
534+
521535 ctx := context .Background ()
522536 branchName := paths .MetadataBranchName
523537
@@ -553,10 +567,8 @@ func TestFetchAndRebase_DoesNotResurrectRemoteOnlyCheckpointFromMerge(t *testing
553567 gitRun (setupDir , "push" , "origin" , branchName )
554568 gitRun (setupDir , "checkout" , "main" )
555569
556- cloneLocal := filepath .Join (t .TempDir (), "clone-local" )
557- cloneRemote := filepath .Join (t .TempDir (), "clone-remote" )
558- require .NoError (t , os .MkdirAll (cloneLocal , 0o755 ))
559- require .NoError (t , os .MkdirAll (cloneRemote , 0o755 ))
570+ cloneLocal := t .TempDir ()
571+ cloneRemote := t .TempDir ()
560572
561573 for _ , dir := range []string {cloneLocal , cloneRemote } {
562574 gitRun (dir , "clone" , bareDir , "." )
@@ -633,6 +645,8 @@ func TestFetchAndRebase_DoesNotResurrectRemoteOnlyCheckpointFromMerge(t *testing
633645//
634646// Not parallel: uses t.Chdir() (required for OpenRepository).
635647func TestFetchAndRebase_NonOriginRemote_ReconcilesFetchedRef (t * testing.T ) {
648+ acquireGitCLITest (t )
649+
636650 ctx := context .Background ()
637651 branchName := paths .MetadataBranchName
638652
@@ -668,8 +682,7 @@ func TestFetchAndRebase_NonOriginRemote_ReconcilesFetchedRef(t *testing.T) {
668682 gitRun (setupDir , "push" , "origin" , branchName )
669683 gitRun (setupDir , "checkout" , "main" )
670684
671- cloneDir := filepath .Join (t .TempDir (), "clone" )
672- require .NoError (t , os .MkdirAll (cloneDir , 0o755 ))
685+ cloneDir := t .TempDir ()
673686 gitRun (cloneDir , "clone" , bareDir , "." )
674687 gitRun (cloneDir , "config" , "user.email" , "test@test.com" )
675688 gitRun (cloneDir , "config" , "user.name" , "Test User" )
0 commit comments