mirror of
https://github.com/router-for-me/CLIProxyAPIPlus.git
synced 2026-03-09 15:25:17 +00:00
test(watcher): add unit test for server update timer cancellation and immediate reload logic
- Add `TestTriggerServerUpdateCancelsPendingTimerOnImmediate` to verify proper handling of server update debounce and timer cancellation. - Fix logic in `triggerServerUpdate` to prevent duplicate timers and ensure proper cleanup of pending state.
This commit is contained in:
@@ -327,6 +327,11 @@ func (w *Watcher) triggerServerUpdate(cfg *config.Config) {
|
||||
w.serverUpdateMu.Lock()
|
||||
if w.serverUpdateLast.IsZero() || now.Sub(w.serverUpdateLast) >= serverUpdateDebounce {
|
||||
w.serverUpdateLast = now
|
||||
if w.serverUpdateTimer != nil {
|
||||
w.serverUpdateTimer.Stop()
|
||||
w.serverUpdateTimer = nil
|
||||
}
|
||||
w.serverUpdatePend = false
|
||||
w.serverUpdateMu.Unlock()
|
||||
w.reloadCallback(cfg)
|
||||
return
|
||||
@@ -344,26 +349,33 @@ func (w *Watcher) triggerServerUpdate(cfg *config.Config) {
|
||||
w.serverUpdatePend = true
|
||||
if w.serverUpdateTimer != nil {
|
||||
w.serverUpdateTimer.Stop()
|
||||
w.serverUpdateTimer = nil
|
||||
}
|
||||
w.serverUpdateTimer = time.AfterFunc(delay, func() {
|
||||
var timer *time.Timer
|
||||
timer = time.AfterFunc(delay, func() {
|
||||
if w.stopped.Load() {
|
||||
return
|
||||
}
|
||||
w.clientsMutex.RLock()
|
||||
latestCfg := w.config
|
||||
w.clientsMutex.RUnlock()
|
||||
|
||||
w.serverUpdateMu.Lock()
|
||||
if w.serverUpdateTimer != timer || !w.serverUpdatePend {
|
||||
w.serverUpdateMu.Unlock()
|
||||
return
|
||||
}
|
||||
w.serverUpdateTimer = nil
|
||||
w.serverUpdatePend = false
|
||||
if latestCfg == nil || w.reloadCallback == nil || w.stopped.Load() {
|
||||
w.serverUpdateMu.Lock()
|
||||
w.serverUpdatePend = false
|
||||
w.serverUpdateMu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
w.serverUpdateMu.Lock()
|
||||
w.serverUpdateLast = time.Now()
|
||||
w.serverUpdatePend = false
|
||||
w.serverUpdateMu.Unlock()
|
||||
w.reloadCallback(latestCfg)
|
||||
})
|
||||
w.serverUpdateTimer = timer
|
||||
w.serverUpdateMu.Unlock()
|
||||
}
|
||||
|
||||
@@ -441,6 +441,46 @@ func TestRemoveClientRemovesHash(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestTriggerServerUpdateCancelsPendingTimerOnImmediate(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
cfg := &config.Config{AuthDir: tmpDir}
|
||||
|
||||
var reloads int32
|
||||
w := &Watcher{
|
||||
reloadCallback: func(*config.Config) {
|
||||
atomic.AddInt32(&reloads, 1)
|
||||
},
|
||||
}
|
||||
w.SetConfig(cfg)
|
||||
|
||||
w.serverUpdateMu.Lock()
|
||||
w.serverUpdateLast = time.Now().Add(-(serverUpdateDebounce - 100*time.Millisecond))
|
||||
w.serverUpdateMu.Unlock()
|
||||
w.triggerServerUpdate(cfg)
|
||||
|
||||
if got := atomic.LoadInt32(&reloads); got != 0 {
|
||||
t.Fatalf("expected no immediate reload, got %d", got)
|
||||
}
|
||||
|
||||
w.serverUpdateMu.Lock()
|
||||
if !w.serverUpdatePend || w.serverUpdateTimer == nil {
|
||||
w.serverUpdateMu.Unlock()
|
||||
t.Fatal("expected a pending server update timer")
|
||||
}
|
||||
w.serverUpdateLast = time.Now().Add(-(serverUpdateDebounce + 10*time.Millisecond))
|
||||
w.serverUpdateMu.Unlock()
|
||||
|
||||
w.triggerServerUpdate(cfg)
|
||||
if got := atomic.LoadInt32(&reloads); got != 1 {
|
||||
t.Fatalf("expected immediate reload once, got %d", got)
|
||||
}
|
||||
|
||||
time.Sleep(250 * time.Millisecond)
|
||||
if got := atomic.LoadInt32(&reloads); got != 1 {
|
||||
t.Fatalf("expected pending timer to be cancelled, got %d reloads", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestShouldDebounceRemove(t *testing.T) {
|
||||
w := &Watcher{}
|
||||
path := filepath.Clean("test.json")
|
||||
|
||||
Reference in New Issue
Block a user