2026-01-27 04:00:55 +01:00

131 lines
4.2 KiB
Nim

{.used.}
import std/options, testutils/unittests, chronos, libp2p/crypto/crypto
import
waku/[node/peer_manager, waku_core, waku_lightpush, waku_lightpush/client],
../testlib/wakucore,
./lightpush_utils
suite "Rate limited push service":
asyncTest "push message with rate limit not violated":
## Setup
let
serverSwitch = newTestSwitch()
clientSwitch = newTestSwitch()
await allFutures(serverSwitch.start(), clientSwitch.start())
## Given
var handlerFuture = newFuture[(string, WakuMessage)]()
let handler: PushMessageHandler = proc(
pubsubTopic: PubsubTopic, message: WakuMessage
): Future[WakuLightPushResult] {.async.} =
handlerFuture.complete((pubsubTopic, message))
return lightpushSuccessResult(1) # succeed to publish to 1 peer.
let
tokenPeriod = 500.millis
server =
await newTestWakuLightpushNode(serverSwitch, handler, some((3, tokenPeriod)))
client = newTestWakuLightpushClient(clientSwitch)
let serverPeerId = serverSwitch.peerInfo.toRemotePeerInfo()
let sendMsgProc = proc(): Future[void] {.async.} =
let message = fakeWakuMessage()
handlerFuture = newFuture[(string, WakuMessage)]()
let requestRes =
await client.publish(some(DefaultPubsubTopic), message, serverPeerId)
check await handlerFuture.withTimeout(50.millis)
check:
requestRes.isOk()
handlerFuture.finished()
let (handledMessagePubsubTopic, handledMessage) = handlerFuture.read()
check:
handledMessagePubsubTopic == DefaultPubsubTopic
handledMessage == message
let waitInBetweenFor = 20.millis
# Test cannot be too explicit about the time when the TokenBucket resets
# the internal timer, although in normal use there is no use case to care about it.
var firstWaitExtend = 300.millis
for runCnt in 0 ..< 3:
let startTime = Moment.now()
for testCnt in 0 ..< 3:
await sendMsgProc()
await sleepAsync(20.millis)
var endTime = Moment.now()
var elapsed: Duration = (endTime - startTime)
await sleepAsync(tokenPeriod - elapsed + firstWaitExtend)
firstWaitExtend = 100.millis
## Cleanup
await allFutures(clientSwitch.stop(), serverSwitch.stop())
asyncTest "push message with rate limit reject":
## Setup
let
serverSwitch = newTestSwitch()
clientSwitch = newTestSwitch()
await allFutures(serverSwitch.start(), clientSwitch.start())
## Given
# Don't rely on per-request timing assumptions or a single shared Future.
# CI can be slow enough that sequential requests accidentally refill tokens.
# Instead we issue a small burst and assert we observe at least one rejection.
let handler = proc(
pubsubTopic: PubsubTopic, message: WakuMessage
): Future[WakuLightPushResult] {.async.} =
return lightpushSuccessResult(1)
let
server =
await newTestWakuLightpushNode(serverSwitch, handler, some((3, 500.millis)))
client = newTestWakuLightpushClient(clientSwitch)
let serverPeerId = serverSwitch.peerInfo.toRemotePeerInfo()
let tokenPeriod = 500.millis
# Fire a burst of requests; require at least one success and one rejection.
var publishFutures = newSeq[Future[WakuLightPushResult]]()
for i in 0 ..< 10:
let message = fakeWakuMessage()
publishFutures.add(
client.publish(some(DefaultPubsubTopic), message, serverPeerId)
)
let finished = await allFinished(publishFutures)
var gotOk = false
var gotTooMany = false
for fut in finished:
check not fut.failed()
let res = fut.read()
if res.isOk():
gotOk = true
else:
check res.error.code == LightPushErrorCode.TOO_MANY_REQUESTS
check res.error.desc == some(TooManyRequestsMessage)
gotTooMany = true
check gotOk
check gotTooMany
# ensure period of time has passed and the client can again use the service
await sleepAsync(tokenPeriod + 100.millis)
let recoveryRes =
await client.publish(some(DefaultPubsubTopic), fakeWakuMessage(), serverPeerId)
check recoveryRes.isOk()
## Cleanup
await allFutures(clientSwitch.stop(), serverSwitch.stop())