2024-02-16 18:05:14 +03:00
|
|
|
package v1
|
|
|
|
|
|
|
|
import (
|
|
|
|
"github.com/jonboulle/clockwork"
|
|
|
|
"github.com/stretchr/testify/suite"
|
2024-02-16 18:10:34 +03:00
|
|
|
"runtime"
|
2024-02-16 18:05:14 +03:00
|
|
|
"sync"
|
|
|
|
"sync/atomic"
|
|
|
|
"testing"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
type TokensBucketTest struct {
|
|
|
|
suite.Suite
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestTokensBucket(t *testing.T) {
|
|
|
|
suite.Run(t, new(TokensBucketTest))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *TokensBucketTest) Test_NewTokensBucket() {
|
|
|
|
t.Assert().NotNil(NewTokensBucket(10, time.Hour, time.Hour))
|
|
|
|
}
|
|
|
|
|
2024-02-16 18:10:34 +03:00
|
|
|
func (t *TokensBucketTest) new(
|
|
|
|
maxRPS uint32, unusedTokenTime, checkTokenTime time.Duration, sleeper sleeper) *TokensBucket {
|
|
|
|
bucket := &TokensBucket{
|
|
|
|
maxRPS: maxRPS,
|
|
|
|
unusedTokenTime: unusedTokenTime,
|
|
|
|
checkTokenTime: checkTokenTime,
|
|
|
|
sleep: sleeper,
|
|
|
|
}
|
|
|
|
runtime.SetFinalizer(bucket, destructBasket)
|
|
|
|
return bucket
|
|
|
|
}
|
|
|
|
|
2024-02-16 18:05:14 +03:00
|
|
|
func (t *TokensBucketTest) Test_Obtain_NoThrottle() {
|
2024-02-16 18:10:34 +03:00
|
|
|
tb := t.new(100, time.Hour, time.Minute, &realSleeper{})
|
2024-02-16 18:05:14 +03:00
|
|
|
start := time.Now()
|
|
|
|
for i := 0; i < 100; i++ {
|
|
|
|
tb.Obtain("a")
|
|
|
|
}
|
|
|
|
t.Assert().True(time.Since(start) < time.Second) // check that rate limiter did not perform throttle.
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *TokensBucketTest) Test_Obtain_Sleep() {
|
|
|
|
clock := &fakeSleeper{}
|
2024-02-16 18:10:34 +03:00
|
|
|
tb := t.new(100, time.Hour, time.Minute, clock)
|
2024-02-16 18:05:14 +03:00
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
wg.Add(1)
|
|
|
|
go func() {
|
|
|
|
for i := 0; i < 301; i++ {
|
|
|
|
tb.Obtain("a")
|
|
|
|
}
|
|
|
|
wg.Done()
|
|
|
|
}()
|
|
|
|
|
|
|
|
wg.Wait()
|
|
|
|
t.Assert().Equal(3, int(clock.total.Load()))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *TokensBucketTest) Test_Obtain_AddRPS() {
|
|
|
|
clock := clockwork.NewFakeClock()
|
2024-02-16 18:10:34 +03:00
|
|
|
tb := t.new(100, time.Hour, time.Minute, clock)
|
|
|
|
go tb.deleteUnusedToken()
|
2024-02-16 18:05:14 +03:00
|
|
|
tb.Obtain("a")
|
|
|
|
clock.Advance(time.Minute * 2)
|
|
|
|
|
|
|
|
item, found := tb.tokens.Load("a")
|
|
|
|
t.Require().True(found)
|
|
|
|
t.Assert().Equal(1, int(item.(*token).rps.Load()))
|
|
|
|
tb.Obtain("a")
|
|
|
|
t.Assert().Equal(2, int(item.(*token).rps.Load()))
|
|
|
|
}
|
|
|
|
|
|
|
|
type fakeSleeper struct {
|
|
|
|
total atomic.Uint32
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *fakeSleeper) Sleep(time.Duration) {
|
|
|
|
s.total.Add(1)
|
|
|
|
}
|