diff --git a/internal/lockfile/writelock_test.go b/internal/lockfile/writelock_test.go index 9fd069e..5ea91e0 100644 --- a/internal/lockfile/writelock_test.go +++ b/internal/lockfile/writelock_test.go @@ -21,3 +21,82 @@ func TestAcquireCreatesLockFile(t *testing.T) { t.Errorf("lock file should exist after Acquire: %v", err) } } + +func TestReleaseRemovesLockFile(t *testing.T) { + dir := t.TempDir() + lockPath := filepath.Join(dir, "write.lock") + + release, err := Acquire(lockPath, 2*time.Second, 10*time.Second) + if err != nil { + t.Fatalf("Acquire: %v", err) + } + release() + + if _, err := os.Stat(lockPath); !os.IsNotExist(err) { + t.Errorf("lock file should be removed after release, got err=%v", err) + } +} + +func TestReAcquireAfterRelease(t *testing.T) { + dir := t.TempDir() + lockPath := filepath.Join(dir, "write.lock") + + r1, err := Acquire(lockPath, 2*time.Second, 10*time.Second) + if err != nil { + t.Fatalf("first Acquire: %v", err) + } + r1() + + r2, err := Acquire(lockPath, 2*time.Second, 10*time.Second) + if err != nil { + t.Fatalf("second Acquire: %v", err) + } + r2() +} + +func TestContentionTimesOut(t *testing.T) { + dir := t.TempDir() + lockPath := filepath.Join(dir, "write.lock") + + r1, err := Acquire(lockPath, 2*time.Second, 10*time.Second) + if err != nil { + t.Fatalf("first Acquire: %v", err) + } + defer r1() + + start := time.Now() + _, err = Acquire(lockPath, 300*time.Millisecond, 10*time.Second) + elapsed := time.Since(start) + + if err == nil { + t.Fatal("expected timeout error, got nil") + } + if err != ErrTimeout { + t.Errorf("expected ErrTimeout, got %v", err) + } + if elapsed < 300*time.Millisecond { + t.Errorf("returned too early: %v < 300ms", elapsed) + } + if elapsed > 1*time.Second { + t.Errorf("returned too late: %v > 1s", elapsed) + } +} + +func TestStaleLockIsRemoved(t *testing.T) { + dir := t.TempDir() + lockPath := filepath.Join(dir, "write.lock") + + if err := os.WriteFile(lockPath, nil, 0644); err != nil { + t.Fatalf("planting stale lock: %v", err) + } + old := time.Now().Add(-5 * time.Second) + if err := os.Chtimes(lockPath, old, old); err != nil { + t.Fatalf("chtimes: %v", err) + } + + release, err := Acquire(lockPath, 1*time.Second, 1*time.Second) + if err != nil { + t.Fatalf("Acquire should remove stale lock: %v", err) + } + defer release() +}