package stacks import ( "io" "log" "os" "path/filepath" "sync" "testing" "time" ) // R-1351: at a guest boot EnsureBaseStack's `compose up` and web.SyncFileBrowserMounts' `compose up --force-recreate` ran // in the same second; one lost with „Conflict. The container name … is already in use" and a false first-boot WARN. The // two paths now share one lock: the base stack's bring-up holds it around its FileBrowser step, the web sync around its // compose call (TestFileBrowserSyncRunsInsideTheStackLock pins that side, internal/web). // // RED-PROOF (2026-11-02): seen failing before the lock existed (build: WithFileBrowserStack); with ensureFileBrowser not // taking the lock it fails on „overlap". func TestR1351_TheTwoFileBrowserStartPathsNeverOverlap(t *testing.T) { dir := t.TempDir() if err := os.WriteFile(filepath.Join(dir, "docker-compose.yml"), []byte("services: {}\n"), 0o644); err != nil { t.Fatal(err) } m := &Manager{logger: log.New(io.Discard, "", 0)} var mu sync.Mutex inside, overlaps := 0, 0 enter := func() { mu.Lock() inside++ if inside > 1 { overlaps++ } mu.Unlock() time.Sleep(30 * time.Millisecond) mu.Lock() inside-- mu.Unlock() } m.fbComposeUpFn = func(string) error { enter(); return nil } m.fbRunningFn = func() bool { return false } var wg sync.WaitGroup for i := 0; i < 4; i++ { wg.Add(2) go func() { defer wg.Done() if err := m.ensureFileBrowser(dir); err != nil { t.Error(err) } }() go func() { defer wg.Done(); m.WithFileBrowserStack(enter) }() } wg.Wait() if overlaps != 0 { t.Fatalf("overlap: the two start paths ran compose at the same time %d time(s) — the „Conflict” of R-1351", overlaps) } }