From 4e49dc26d2f64cb16d617fda4e29b8393344f2e5 Mon Sep 17 00:00:00 2001
From: pablo
Date: Thu, 17 Jul 2025 08:57:48 +0300
Subject: [PATCH] feat: add unit tests
---
tests/common/test_tokenbucket.nim | 97 +++++++++++++++++++++++++++++++
1 file changed, 97 insertions(+)
diff --git a/tests/common/test_tokenbucket.nim b/tests/common/test_tokenbucket.nim
index 12a02f905..786920ee6 100644
--- a/tests/common/test_tokenbucket.nim
+++ b/tests/common/test_tokenbucket.nim
@@ -152,3 +152,100 @@ suite "Token Bucket":
reqTime += 1.minutes
let (maxCompensationBudget, _, _) = bucket.getAvailableCapacity(reqTime)
check maxCompensationBudget == 1250 # 1000 + 250 max compensation
+
+ test "TokenBucket custom budget parameter":
+ let now = Moment.now()
+
+ # Test with default budget (-1, should use budgetCap)
+ var bucket1 = TokenBucket.new(1000, 1.seconds, budget = -1)
+ let (budget1, budgetCap1, _) = bucket1.getAvailableCapacity(now)
+ check budget1 == 1000
+ check budgetCap1 == 1000
+
+ # Test with custom budget less than capacity
+ var bucket2 = TokenBucket.new(1000, 1.seconds, budget = 500)
+ let (budget2, budgetCap2, _) = bucket2.getAvailableCapacity(now)
+ check budget2 == 500
+ check budgetCap2 == 1000
+
+ # Test with budget equal to capacity
+ var bucket3 = TokenBucket.new(1000, 1.seconds, budget = 1000)
+ let (budget3, budgetCap3, _) = bucket3.getAvailableCapacity(now)
+ check budget3 == 1000
+ check budgetCap3 == 1000
+
+ # Test with zero budget
+ var bucket4 = TokenBucket.new(1000, 1.seconds, budget = 0)
+ let (budget4, budgetCap4, _) = bucket4.getAvailableCapacity(now)
+ check budget4 == 0
+ check budgetCap4 == 1000
+
+ # Test consumption with custom budget
+ check bucket2.tryConsume(300, now) == true
+ let (budget2After, budgetCap2After, _) = bucket2.getAvailableCapacity(now)
+ check budget2After == 200
+ check budgetCap2After == 1000
+ check bucket2.tryConsume(300, now) == false # Should fail, only 200 remaining
+
+ test "TokenBucket custom lastTimeFull parameter":
+ let now = Moment.now()
+ let pastTime = now - 5.seconds
+
+ # Test with past lastTimeFull (bucket should be ready for refill)
+ var bucket1 = TokenBucket.new(1000, 1.seconds, budget = 0, lastTimeFull = pastTime)
+ # Since 5 seconds have passed and period is 1 second, bucket should refill
+ check bucket1.tryConsume(1000, now) == true
+
+ # Test with current time as lastTimeFull
+ var bucket2 = TokenBucket.new(1000, 1.seconds, budget = 500, lastTimeFull = now)
+ check bucket2.getAvailableCapacity(now) == (500, 1000, now)
+
+ # Test strict mode with past lastTimeFull
+ var bucket3 = TokenBucket.new(
+ 1000, 1.seconds, ReplenishMode.Strict, budget = 0, lastTimeFull = pastTime
+ )
+ check bucket3.tryConsume(1000, now) == true # Should refill to full capacity
+
+ # Test compensating mode with past lastTimeFull and partial usage
+ var bucket4 = TokenBucket.new(
+ 1000, 2.seconds, ReplenishMode.Compensating, budget = 300, lastTimeFull = pastTime
+ )
+ # 5 seconds passed, period is 2 seconds, so 2.5 periods elapsed
+ # Usage average = 300/1000/2.5 = 0.12 (12% usage)
+ # Compensation = (1.0 - 0.12) * 1000 = 880, capped at 250
+ let (availableBudget, _, _) = bucket4.getAvailableCapacity(now)
+ check availableBudget == 1250 # 1000 + 250 max compensation
+
+ test "TokenBucket parameter combinations":
+ let now = Moment.now()
+ let pastTime = now - 3.seconds
+
+ # Test strict mode with custom budget and past time
+ var strictBucket = TokenBucket.new(
+ budgetCap = 500,
+ fillDuration = 1.seconds,
+ mode = ReplenishMode.Strict,
+ budget = 100,
+ lastTimeFull = pastTime,
+ )
+ check strictBucket.getAvailableCapacity(now) == (500, 500, pastTime)
+ # Should show full capacity since period elapsed
+
+ # Test compensating mode with custom budget and past time
+ var compBucket = TokenBucket.new(
+ budgetCap = 1000,
+ fillDuration = 2.seconds,
+ mode = ReplenishMode.Compensating,
+ budget = 200,
+ lastTimeFull = pastTime,
+ )
+ # 3 seconds passed, period is 2 seconds, so 1.5 periods elapsed
+ # Usage average = 200/1000/1.5 = 0.133 (13.3% usage)
+ # Compensation = (1.0 - 0.133) * 1000 = 867, capped at 250
+ let (compensatedBudget, _, _) = compBucket.getAvailableCapacity(now)
+ check compensatedBudget == 1250 # 1000 + 250 max compensation
+
+ # Test edge case: zero budget with immediate consumption
+ var zeroBucket = TokenBucket.new(100, 1.seconds, budget = 0, lastTimeFull = now)
+ check zeroBucket.tryConsume(1, now) == false # Should fail immediately
+ check zeroBucket.tryConsume(1, now + 1.seconds) == true # Should succeed after period