package accounting import ( "testing" "time" ) // Two control planes each allowing a key six of its ten a minute: once each // has heard the other's count, neither allows another. func TestALimitIsTheClustersOnceTheCountsAreShared(t *testing.T) { now := time.Date(2026, 11, 1, 12, 0, 10, 0, time.UTC) a, b := NewLimiter(), NewLimiter() a.now, b.now = func() time.Time { return now }, func() time.Time { return now } for range 6 { if !a.Allow("k", 10, 1) || !b.Allow("k", 10, 1) { t.Fatal("refused within the limit") } } a.SetOthers(b.Counts()) b.SetOthers(a.Counts()) if a.Allow("k", 10, 1) || b.Allow("k", 10, 1) { t.Error("twelve of ten allowed once both knew") } // Reporting the same counts again changes nothing: they are absolute. a.SetOthers(b.Counts()) if got := a.Rate("k"); got != 12 { t.Errorf("rate %v after the same counts twice", got) } // What the others counted is never charged back to them. if c := a.Counts(); c.Current["k"] != 6 { t.Errorf("own count %d", c.Current["k"]) } } // A key only the others have used is limited here from its first request, // and their counts move into the previous minute with the clock. func TestOthersCountsFollowTheMinute(t *testing.T) { now := time.Date(2026, 11, 1, 12, 0, 50, 0, time.UTC) a, b := NewLimiter(), NewLimiter() a.now, b.now = func() time.Time { return now }, func() time.Time { return now } for range 10 { b.Allow("k", 10, 1) } a.SetOthers(b.Counts()) if a.Allow("k", 10, 1) { t.Error("a key the cluster has used up was allowed where it had not been seen") } now = now.Add(20 * time.Second) // 12:01:10, a sixth into the next minute if got := a.Rate("k"); got < 8 || got > 8.5 { t.Errorf("rate %v: the others' last minute should weigh five sixths", got) } // Counts of a minute ago, sent late, land in the previous minute. old := b.Counts() old.Minute = now.Truncate(time.Minute).Add(-time.Minute) old.Current, old.Previous = map[string]int64{"k": 3}, nil a.SetOthers(old) if got := a.Rate("k"); got < 2 || got > 3 { t.Errorf("rate %v from late counts", got) } } func TestRatesAddAlignsMinutes(t *testing.T) { m := time.Date(2026, 11, 1, 12, 1, 0, 0, time.UTC) sum := NewRates(m) sum.Add(Rates{Requests: Counts{Minute: m, Current: map[string]int64{"k": 2}, Previous: map[string]int64{"k": 1}}}) sum.Add(Rates{Requests: Counts{Minute: m.Add(-time.Minute), Current: map[string]int64{"k": 5}}}) sum.Add(Rates{Requests: Counts{Minute: m.Add(-2 * time.Minute), Current: map[string]int64{"k": 100}}}) if sum.Requests.Current["k"] != 2 || sum.Requests.Previous["k"] != 6 { t.Errorf("sum %+v", sum.Requests) } }