package graph import ( "sync/atomic" "testing" "testing/synctest" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestDebouncer_Coalesces(t *testing.T) { synctest.Test(t, func(t *testing.T) { d := NewDebouncer(50 * time.Millisecond) var calls atomic.Int32 // Fire 10 rapid calls for the same key — only the last should execute. for range 10 { d.Debounce("key1", func() { calls.Add(1) }) } // Advance fake clock past the debounce delay and let goroutines settle. time.Sleep(50 * time.Millisecond) synctest.Wait() assert.Equal(t, int32(1), calls.Load(), "rapid calls should coalesce into a single execution") }) } func TestDebouncer_DifferentKeys(t *testing.T) { synctest.Test(t, func(t *testing.T) { d := NewDebouncer(50 * time.Millisecond) var calls atomic.Int32 d.Debounce("key-a", func() { calls.Add(1) }) d.Debounce("key-b", func() { calls.Add(1) }) d.Debounce("key-c", func() { calls.Add(1) }) time.Sleep(50 * time.Millisecond) synctest.Wait() assert.Equal(t, int32(3), calls.Load(), "different keys should fire independently") }) } func TestDebouncer_TimerReset(t *testing.T) { synctest.Test(t, func(t *testing.T) { d := NewDebouncer(100 * time.Millisecond) var value atomic.Int32 // First call sets value to 1. d.Debounce("key", func() { value.Store(1) }) // Advance 60ms (less than the 100ms delay) — first timer hasn't fired. time.Sleep(60 * time.Millisecond) // Replace with value 2 — resets the timer to fire at 60+100 = 160ms. d.Debounce("key", func() { value.Store(2) }) // Advance another 100ms (total 160ms) to fire the reset timer. time.Sleep(100 * time.Millisecond) synctest.Wait() require.Equal(t, int32(2), value.Load(), "later call should replace the earlier one") }) }