81 lines
2.3 KiB
Go
81 lines
2.3 KiB
Go
package app
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestRefreshCoordinatorLifecycle(t *testing.T) {
|
|
now := time.Date(2026, time.March, 9, 21, 0, 0, 0, time.UTC)
|
|
rc := newRefreshCoordinator(10*time.Minute, 2*time.Second, 60*time.Second)
|
|
|
|
if !rc.shouldRefresh(now, false, false) {
|
|
t.Fatalf("expected refresh when cache is empty")
|
|
}
|
|
if !rc.beginRefresh(now, false, false) {
|
|
t.Fatalf("expected beginRefresh=true for empty cache")
|
|
}
|
|
if rc.shouldRefresh(now, false, false) {
|
|
t.Fatalf("must not refresh while refresh is in progress")
|
|
}
|
|
|
|
rc.finishSuccess(now)
|
|
snap := rc.snapshot(now)
|
|
if snap.RefreshInProgress {
|
|
t.Fatalf("refresh must be finished after success")
|
|
}
|
|
if snap.Stale {
|
|
t.Fatalf("fresh snapshot must not be stale")
|
|
}
|
|
if snap.LastError != "" || snap.NextRetryAt != "" {
|
|
t.Fatalf("success must clear error and retry metadata: %#v", snap)
|
|
}
|
|
|
|
if rc.shouldRefresh(now.Add(5*time.Minute), false, true) {
|
|
t.Fatalf("fresh cache should not refresh yet")
|
|
}
|
|
if !rc.shouldRefresh(now.Add(11*time.Minute), false, true) {
|
|
t.Fatalf("stale cache should refresh")
|
|
}
|
|
}
|
|
|
|
func TestRefreshCoordinatorBackoffAndReset(t *testing.T) {
|
|
start := time.Date(2026, time.March, 9, 21, 5, 0, 0, time.UTC)
|
|
rc := newRefreshCoordinator(10*time.Minute, 2*time.Second, 60*time.Second)
|
|
|
|
type step struct {
|
|
at time.Time
|
|
expected time.Duration
|
|
}
|
|
steps := []step{
|
|
{at: start, expected: 2 * time.Second},
|
|
{at: start.Add(2 * time.Second), expected: 4 * time.Second},
|
|
{at: start.Add(6 * time.Second), expected: 8 * time.Second},
|
|
{at: start.Add(14 * time.Second), expected: 16 * time.Second},
|
|
{at: start.Add(30 * time.Second), expected: 32 * time.Second},
|
|
{at: start.Add(62 * time.Second), expected: 60 * time.Second},
|
|
}
|
|
|
|
for i, st := range steps {
|
|
rc.finishError("probe failed", st.at)
|
|
got := rc.nextRetryAt().Sub(st.at)
|
|
if got != st.expected {
|
|
t.Fatalf("step=%d unexpected backoff: got=%s want=%s", i+1, got, st.expected)
|
|
}
|
|
if rc.lastError() == "" {
|
|
t.Fatalf("step=%d expected non-empty lastError", i+1)
|
|
}
|
|
}
|
|
|
|
rc.finishSuccess(start.Add(2 * time.Minute))
|
|
if rc.consecutiveErrors() != 0 {
|
|
t.Fatalf("expected consecutiveErrors reset, got %d", rc.consecutiveErrors())
|
|
}
|
|
if !rc.nextRetryAt().IsZero() {
|
|
t.Fatalf("expected nextRetryAt reset")
|
|
}
|
|
if rc.lastError() != "" {
|
|
t.Fatalf("expected lastError reset, got %q", rc.lastError())
|
|
}
|
|
}
|