package operationlock_test import ( "context" "os" "path/filepath" "testing" "time" "proxyctl/internal/operationlock" ) func TestLockRejectsSymlinkedStateAncestor(t *testing.T) { root := t.TempDir() victim := filepath.Join(root, "victim") if err := os.Mkdir(victim, 0o700); err != nil { t.Fatal(err) } if err := os.Symlink(victim, filepath.Join(root, "state")); err != nil { t.Fatal(err) } lock := operationlock.New(filepath.Join(root, "state", "proxyctl", "operation.lock")) if _, err := lock.Lock(context.Background()); err == nil { t.Fatal("lock followed a symlinked state ancestor") } entries, err := os.ReadDir(victim) if err != nil { t.Fatal(err) } if len(entries) != 0 { t.Fatalf("victim changed: %v", entries) } } func TestLockSerializesIndependentInstancesAndHonorsContext(t *testing.T) { path := t.TempDir() + "/proxyctl.lock" first := operationlock.New(path) second := operationlock.New(path) unlock, err := first.Lock(context.Background()) if err != nil { t.Fatal(err) } ctx, cancel := context.WithTimeout(context.Background(), 30*time.Millisecond) defer cancel() if _, err := second.Lock(ctx); err == nil { t.Fatal("second lock acquired while first held") } unlock() unlockSecond, err := second.Lock(context.Background()) if err != nil { t.Fatal(err) } unlockSecond() }