test(limiter/conn): add unit tests for conn limiter and wrappers

This commit is contained in:
ginuerzh
2026-05-24 19:38:54 +08:00
parent fa708f4b5f
commit 05fe690f67
5 changed files with 1037 additions and 0 deletions
+155
View File
@@ -0,0 +1,155 @@
package conn
import (
"sync"
"sync/atomic"
"testing"
limiter "github.com/go-gost/core/limiter/conn"
)
func TestLimiter_Allow(t *testing.T) {
l := NewLimiter(3).(*llimiter)
if l.Limit() != 3 {
t.Fatalf("expected limit 3, got %d", l.Limit())
}
// Basic acquire/release within limit.
for i := 0; i < 3; i++ {
if !l.Allow(1) {
t.Fatalf("Allow(1) #%d should succeed", i)
}
}
if l.Allow(1) {
t.Fatal("Allow(1) should fail after reaching limit")
}
// Release one and re-acquire.
if !l.Allow(-1) {
t.Fatal("Allow(-1) should always succeed")
}
if !l.Allow(1) {
t.Fatal("Allow(1) should succeed after release")
}
}
func TestLimiter_AllowZero(t *testing.T) {
l := NewLimiter(1)
if !l.Allow(0) {
t.Fatal("Allow(0) should not fail with no connections")
}
}
func TestLimiter_AllowNegative(t *testing.T) {
l := NewLimiter(1)
// Allow(-1) should always return true even without prior acquire.
if !l.Allow(-1) {
t.Fatal("Allow(-1) should succeed")
}
// Allow(-1) again — still succeeds.
if !l.Allow(-1) {
t.Fatal("Allow(-1) should succeed again")
}
}
func TestLimiter_Concurrent(t *testing.T) {
l := NewLimiter(100)
var wg sync.WaitGroup
n := 200
var success int64
for i := 0; i < n; i++ {
wg.Add(1)
go func() {
defer wg.Done()
if l.Allow(1) {
atomic.AddInt64(&success, 1)
}
}()
}
wg.Wait()
if v := atomic.LoadInt64(&success); v != 100 {
t.Errorf("expected 100 successes, got %d", v)
}
}
func TestLimiterGroup_Allow(t *testing.T) {
lg := newLimiterGroup(NewLimiter(2), NewLimiter(5), NewLimiter(10))
if lg.Limit() != 2 {
t.Fatalf("expected group limit 2 (smallest), got %d", lg.Limit())
}
// Each Allow must pass all limiters.
for i := 0; i < 2; i++ {
if !lg.Allow(1) {
t.Fatalf("lg.Allow(1) #%d should succeed", i)
}
}
if lg.Allow(1) {
t.Fatal("lg.Allow(1) should fail after 2 connections (tightest limit=2)")
}
}
func TestLimiterGroup_AllowRelease(t *testing.T) {
lg := newLimiterGroup(NewLimiter(1), NewLimiter(10))
if !lg.Allow(1) {
t.Fatal("Allow(1) should succeed")
}
if lg.Allow(1) {
t.Fatal("Allow(1) should fail")
}
// Release on group must propagate to all limiters.
if !lg.Allow(-1) {
t.Fatal("Allow(-1) should succeed")
}
// Should be able to acquire again.
if !lg.Allow(1) {
t.Fatal("Allow(1) should succeed after release")
}
}
func TestLimiterGroup_AllowRollback(t *testing.T) {
// limiters sorted: [1, 5] (ascending by limit).
lg := newLimiterGroup(NewLimiter(5), NewLimiter(1))
// First Allow passes both limiters.
if !lg.Allow(1) {
t.Fatal("first Allow(1) should succeed")
}
// Second Allow fails on the tightest limiter (limit=1) and rolls back.
if lg.Allow(1) {
t.Fatal("second Allow(1) should fail after limit reached")
}
// The tightest limiter should still be at 1 (rollback worked).
ll := lg.limiters[0].(*llimiter)
if ll.current != 1 {
t.Errorf("first limiter should be at 1 after rollback, got %d", ll.current)
}
}
func TestLimiterGroup_BadInput(t *testing.T) {
lg := newLimiterGroup(NewLimiter(-5), NewLimiter(10))
if lg.Limit() != -5 {
t.Fatalf("expected limit -5 for negative-limit limiter")
}
}
func TestLimiterGroup_Empty(t *testing.T) {
lg := newLimiterGroup()
if lg.Limit() != 0 {
t.Fatal("empty group limit should be 0")
}
// Empty group returns false (no limiters to consult).
if lg.Allow(1) {
t.Fatal("Allow(1) on empty group should return false")
}
}
var _ limiter.Limiter = (*llimiter)(nil)
var _ limiter.Limiter = (*limiterGroup)(nil)