package security import ( "os" "path/filepath" "testing" "time" ) func TestFlock_acquireAndRelease(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "test.lock") release, err := Flock(path) if err != nil { t.Fatalf("Flock: %v", err) } if _, statErr := os.Stat(path); statErr != nil { t.Fatalf("lock file not created: %v", statErr) } release() } func TestFlock_reentrantAfterRelease(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "test.lock") r1, err := Flock(path) if err != nil { t.Fatalf("first Flock: %v", err) } r1() r2, err := Flock(path) if err != nil { t.Fatalf("second Flock after release: %v", err) } r2() } func TestFlock_concurrentBlocks(t *testing.T) { dir := t.TempDir() path := filepath.Join(dir, "test.lock") r1, err := Flock(path) if err != nil { t.Fatalf("first Flock: %v", err) } // Give the blocking goroutine a chance to start and block. time.Sleep(50 * time.Millisecond) // Verify the second lock is blocked by checking it hasn't acquired after a short window. // Use a non-blocking attempt: open the file and try LOCK_EX|LOCK_NB. blocked := make(chan bool, 1) go func() { f, err := os.OpenFile(path, os.O_RDWR, 0o600) if err != nil { blocked <- false return } defer f.Close() // LOCK_NB = non-blocking; returns EWOULDBLOCK if locked. if err := tryFlockEx(int(f.Fd())); err != nil { blocked <- true // got EWOULDBLOCK = the lock is held by r1 return } releaseFlock(int(f.Fd())) blocked <- false // acquired = r1 didn't hold the lock (bug) }() select { case b := <-blocked: if !b { t.Fatal("second lock acquired while first holds it — lock not working") } case <-time.After(2 * time.Second): t.Fatal("non-blocking try-lock timed out") } r1() }