Files
felhom-controller/controller/internal/stacks/r1351_filebrowser_serial_test.go
T

59 lines
1.7 KiB
Go

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)
}
}