2025-04-24 08:36:30 +02:00
|
|
|
{.used.}
|
|
|
|
|
|
|
|
|
|
import
|
|
|
|
|
std/sequtils,
|
|
|
|
|
stew/byteutils,
|
|
|
|
|
testutils/unittests,
|
|
|
|
|
presto,
|
|
|
|
|
presto/client as presto_client,
|
|
|
|
|
libp2p/crypto/crypto
|
|
|
|
|
|
|
|
|
|
import
|
|
|
|
|
waku/[
|
2025-11-15 23:31:09 +01:00
|
|
|
rest_api/message_cache,
|
2025-04-24 08:36:30 +02:00
|
|
|
waku_core,
|
|
|
|
|
waku_node,
|
|
|
|
|
node/peer_manager,
|
|
|
|
|
waku_lightpush/common,
|
2025-11-15 23:31:09 +01:00
|
|
|
rest_api/endpoint/server,
|
|
|
|
|
rest_api/endpoint/client,
|
|
|
|
|
rest_api/endpoint/responses,
|
|
|
|
|
rest_api/endpoint/lightpush/types,
|
|
|
|
|
rest_api/endpoint/lightpush/handlers as lightpush_rest_interface,
|
|
|
|
|
rest_api/endpoint/lightpush/client as lightpush_rest_client,
|
2025-04-24 08:36:30 +02:00
|
|
|
waku_relay,
|
|
|
|
|
common/rate_limit/setting,
|
|
|
|
|
],
|
|
|
|
|
../testlib/wakucore,
|
|
|
|
|
../testlib/wakunode
|
|
|
|
|
|
|
|
|
|
proc testWakuNode(): WakuNode =
|
|
|
|
|
let
|
|
|
|
|
privkey = generateSecp256k1Key()
|
|
|
|
|
bindIp = parseIpAddress("0.0.0.0")
|
|
|
|
|
extIp = parseIpAddress("127.0.0.1")
|
|
|
|
|
port = Port(0)
|
|
|
|
|
|
|
|
|
|
return newTestWakuNode(privkey, bindIp, port, some(extIp), some(port))
|
|
|
|
|
|
|
|
|
|
type RestLightPushTest = object
|
|
|
|
|
serviceNode: WakuNode
|
|
|
|
|
pushNode: WakuNode
|
|
|
|
|
consumerNode: WakuNode
|
|
|
|
|
restServer: WakuRestServerRef
|
|
|
|
|
restClient: RestClientRef
|
|
|
|
|
|
|
|
|
|
proc init(
|
|
|
|
|
T: type RestLightPushTest, rateLimit: RateLimitSetting = (0, 0.millis)
|
|
|
|
|
): Future[T] {.async.} =
|
|
|
|
|
var testSetup = RestLightPushTest()
|
|
|
|
|
testSetup.serviceNode = testWakuNode()
|
|
|
|
|
testSetup.pushNode = testWakuNode()
|
|
|
|
|
testSetup.consumerNode = testWakuNode()
|
|
|
|
|
|
|
|
|
|
await allFutures(
|
|
|
|
|
testSetup.serviceNode.start(),
|
|
|
|
|
testSetup.pushNode.start(),
|
|
|
|
|
testSetup.consumerNode.start(),
|
|
|
|
|
)
|
|
|
|
|
|
2025-05-05 22:57:20 +02:00
|
|
|
(await testSetup.consumerNode.mountRelay()).isOkOr:
|
|
|
|
|
assert false, "Failed to mount relay: " & $error
|
|
|
|
|
(await testSetup.serviceNode.mountRelay()).isOkOr:
|
|
|
|
|
assert false, "Failed to mount relay: " & $error
|
2025-12-11 10:51:47 +01:00
|
|
|
check (await testSetup.serviceNode.mountLightPush(rateLimit)).isOk()
|
2025-04-24 08:36:30 +02:00
|
|
|
testSetup.pushNode.mountLightPushClient()
|
|
|
|
|
|
|
|
|
|
testSetup.serviceNode.peerManager.addServicePeer(
|
|
|
|
|
testSetup.consumerNode.peerInfo.toRemotePeerInfo(), WakuRelayCodec
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
await testSetup.serviceNode.connectToNodes(
|
|
|
|
|
@[testSetup.consumerNode.peerInfo.toRemotePeerInfo()]
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
testSetup.pushNode.peerManager.addServicePeer(
|
|
|
|
|
testSetup.serviceNode.peerInfo.toRemotePeerInfo(), WakuLightPushCodec
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var restPort = Port(0)
|
|
|
|
|
let restAddress = parseIpAddress("127.0.0.1")
|
|
|
|
|
testSetup.restServer = WakuRestServerRef.init(restAddress, restPort).tryGet()
|
|
|
|
|
restPort = testSetup.restServer.httpServer.address.port
|
|
|
|
|
# update with bound port for restClient use
|
|
|
|
|
|
|
|
|
|
installLightPushRequestHandler(testSetup.restServer.router, testSetup.pushNode)
|
|
|
|
|
|
|
|
|
|
testSetup.restServer.start()
|
|
|
|
|
|
|
|
|
|
testSetup.restClient = newRestHttpClient(initTAddress(restAddress, restPort))
|
|
|
|
|
|
|
|
|
|
return testSetup
|
|
|
|
|
|
|
|
|
|
proc shutdown(self: RestLightPushTest) {.async.} =
|
|
|
|
|
await self.restServer.stop()
|
|
|
|
|
await self.restServer.closeWait()
|
|
|
|
|
await allFutures(
|
|
|
|
|
self.serviceNode.stop(), self.pushNode.stop(), self.consumerNode.stop()
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
suite "Waku v2 Rest API - lightpush":
|
|
|
|
|
asyncTest "Push message with proof":
|
|
|
|
|
let restLightPushTest = await RestLightPushTest.init()
|
|
|
|
|
|
|
|
|
|
let message: RelayWakuMessage = fakeWakuMessage(
|
|
|
|
|
contentTopic = DefaultContentTopic,
|
|
|
|
|
payload = toBytes("TEST-1"),
|
|
|
|
|
proof = toBytes("proof-test"),
|
|
|
|
|
)
|
|
|
|
|
.toRelayWakuMessage()
|
|
|
|
|
|
|
|
|
|
check message.proof.isSome()
|
|
|
|
|
|
|
|
|
|
let requestBody =
|
|
|
|
|
PushRequest(pubsubTopic: some(DefaultPubsubTopic), message: message)
|
|
|
|
|
|
|
|
|
|
let response =
|
|
|
|
|
await restLightPushTest.restClient.sendPushRequest(body = requestBody)
|
|
|
|
|
|
|
|
|
|
## Validate that the push request failed because the node is not
|
|
|
|
|
## connected to other node but, doesn't fail because of not properly
|
|
|
|
|
## handling the proof message attribute within the REST request.
|
|
|
|
|
check:
|
|
|
|
|
response.status == 505
|
|
|
|
|
response.data.statusDesc == some("No peers for topic, skipping publish")
|
|
|
|
|
response.data.relayPeerCount == none[uint32]()
|
|
|
|
|
|
|
|
|
|
asyncTest "Push message request":
|
|
|
|
|
# Given
|
|
|
|
|
let restLightPushTest = await RestLightPushTest.init()
|
|
|
|
|
|
2025-06-02 22:02:49 +02:00
|
|
|
let simpleHandler = proc(
|
|
|
|
|
topic: PubsubTopic, msg: WakuMessage
|
|
|
|
|
): Future[void] {.async, gcsafe.} =
|
|
|
|
|
await sleepAsync(0.milliseconds)
|
|
|
|
|
|
2025-04-24 08:36:30 +02:00
|
|
|
restLightPushTest.consumerNode.subscribe(
|
2025-06-02 22:02:49 +02:00
|
|
|
(kind: PubsubSub, topic: DefaultPubsubTopic), simpleHandler
|
2025-05-05 22:57:20 +02:00
|
|
|
).isOkOr:
|
|
|
|
|
assert false, "Failed to subscribe to relay: " & $error
|
|
|
|
|
|
2025-04-24 08:36:30 +02:00
|
|
|
restLightPushTest.serviceNode.subscribe(
|
2025-06-02 22:02:49 +02:00
|
|
|
(kind: PubsubSub, topic: DefaultPubsubTopic), simpleHandler
|
2025-05-05 22:57:20 +02:00
|
|
|
).isOkOr:
|
|
|
|
|
assert false, "Failed to subscribe to relay: " & $error
|
2025-04-24 08:36:30 +02:00
|
|
|
require:
|
|
|
|
|
toSeq(restLightPushTest.serviceNode.wakuRelay.subscribedTopics).len == 1
|
|
|
|
|
|
|
|
|
|
# When
|
|
|
|
|
let message: RelayWakuMessage = fakeWakuMessage(
|
|
|
|
|
contentTopic = DefaultContentTopic, payload = toBytes("TEST-1")
|
|
|
|
|
)
|
|
|
|
|
.toRelayWakuMessage()
|
|
|
|
|
|
|
|
|
|
let requestBody =
|
|
|
|
|
PushRequest(pubsubTopic: some(DefaultPubsubTopic), message: message)
|
|
|
|
|
let response = await restLightPushTest.restClient.sendPushRequest(requestBody)
|
|
|
|
|
|
|
|
|
|
echo "response", $response
|
|
|
|
|
|
|
|
|
|
# Then
|
|
|
|
|
check:
|
|
|
|
|
response.status == 200
|
|
|
|
|
response.data.relayPeerCount == some(1.uint32)
|
|
|
|
|
|
|
|
|
|
await restLightPushTest.shutdown()
|
|
|
|
|
|
|
|
|
|
asyncTest "Push message bad-request":
|
|
|
|
|
# Given
|
|
|
|
|
let restLightPushTest = await RestLightPushTest.init()
|
2025-06-02 22:02:49 +02:00
|
|
|
let simpleHandler = proc(
|
|
|
|
|
topic: PubsubTopic, msg: WakuMessage
|
|
|
|
|
): Future[void] {.async, gcsafe.} =
|
|
|
|
|
await sleepAsync(0.milliseconds)
|
2025-04-24 08:36:30 +02:00
|
|
|
|
|
|
|
|
restLightPushTest.serviceNode.subscribe(
|
2025-06-02 22:02:49 +02:00
|
|
|
(kind: PubsubSub, topic: DefaultPubsubTopic), simpleHandler
|
2025-05-05 22:57:20 +02:00
|
|
|
).isOkOr:
|
|
|
|
|
assert false, "Failed to subscribe to relay: " & $error
|
2025-04-24 08:36:30 +02:00
|
|
|
require:
|
|
|
|
|
toSeq(restLightPushTest.serviceNode.wakuRelay.subscribedTopics).len == 1
|
|
|
|
|
|
|
|
|
|
# When
|
|
|
|
|
let badMessage1: RelayWakuMessage = fakeWakuMessage(
|
|
|
|
|
contentTopic = DefaultContentTopic, payload = toBytes("")
|
|
|
|
|
)
|
|
|
|
|
.toRelayWakuMessage()
|
|
|
|
|
let badRequestBody1 =
|
|
|
|
|
PushRequest(pubsubTopic: some(DefaultPubsubTopic), message: badMessage1)
|
|
|
|
|
|
|
|
|
|
let badMessage2: RelayWakuMessage =
|
|
|
|
|
fakeWakuMessage(contentTopic = "", payload = toBytes("Sthg")).toRelayWakuMessage()
|
|
|
|
|
let badRequestBody2 =
|
|
|
|
|
PushRequest(pubsubTopic: some(DefaultPubsubTopic), message: badMessage2)
|
|
|
|
|
|
|
|
|
|
let badRequestBody3 =
|
|
|
|
|
PushRequest(pubsubTopic: none(PubsubTopic), message: badMessage2)
|
|
|
|
|
|
|
|
|
|
# var response: RestResponse[PushResponse]
|
|
|
|
|
|
|
|
|
|
var response = await restLightPushTest.restClient.sendPushRequest(badRequestBody1)
|
|
|
|
|
|
|
|
|
|
# Then
|
|
|
|
|
check:
|
|
|
|
|
response.status == 400
|
|
|
|
|
response.data.statusDesc.isSome()
|
|
|
|
|
response.data.statusDesc.get().startsWith("Invalid push request")
|
|
|
|
|
|
|
|
|
|
# when
|
|
|
|
|
response = await restLightPushTest.restClient.sendPushRequest(badRequestBody2)
|
|
|
|
|
|
|
|
|
|
# Then
|
|
|
|
|
check:
|
|
|
|
|
response.status == 400
|
|
|
|
|
response.data.statusDesc.isSome()
|
|
|
|
|
response.data.statusDesc.get().startsWith("Invalid push request")
|
|
|
|
|
|
|
|
|
|
# when
|
|
|
|
|
response = await restLightPushTest.restClient.sendPushRequest(badRequestBody3)
|
|
|
|
|
|
|
|
|
|
# Then
|
|
|
|
|
check:
|
|
|
|
|
response.data.statusDesc.isSome()
|
|
|
|
|
response.data.statusDesc.get().startsWith("Invalid push request")
|
|
|
|
|
|
|
|
|
|
await restLightPushTest.shutdown()
|
|
|
|
|
|
|
|
|
|
asyncTest "Request rate limit push message":
|
|
|
|
|
# Given
|
|
|
|
|
let budgetCap = 3
|
|
|
|
|
let tokenPeriod = 500.millis
|
|
|
|
|
let restLightPushTest = await RestLightPushTest.init((budgetCap, tokenPeriod))
|
2025-06-02 22:02:49 +02:00
|
|
|
let simpleHandler = proc(
|
|
|
|
|
topic: PubsubTopic, msg: WakuMessage
|
|
|
|
|
): Future[void] {.async, gcsafe.} =
|
|
|
|
|
await sleepAsync(0.milliseconds)
|
2025-04-24 08:36:30 +02:00
|
|
|
|
|
|
|
|
restLightPushTest.consumerNode.subscribe(
|
2025-06-02 22:02:49 +02:00
|
|
|
(kind: PubsubSub, topic: DefaultPubsubTopic), simpleHandler
|
2025-05-05 22:57:20 +02:00
|
|
|
).isOkOr:
|
|
|
|
|
assert false, "Failed to subscribe to relay: " & $error
|
|
|
|
|
|
2025-04-24 08:36:30 +02:00
|
|
|
restLightPushTest.serviceNode.subscribe(
|
2025-06-02 22:02:49 +02:00
|
|
|
(kind: PubsubSub, topic: DefaultPubsubTopic), simpleHandler
|
2025-05-05 22:57:20 +02:00
|
|
|
).isOkOr:
|
|
|
|
|
assert false, "Failed to subscribe to relay: " & $error
|
2025-04-24 08:36:30 +02:00
|
|
|
require:
|
|
|
|
|
toSeq(restLightPushTest.serviceNode.wakuRelay.subscribedTopics).len == 1
|
|
|
|
|
|
|
|
|
|
# When
|
|
|
|
|
let pushProc = proc() {.async.} =
|
|
|
|
|
let message: RelayWakuMessage = fakeWakuMessage(
|
|
|
|
|
contentTopic = DefaultContentTopic, payload = toBytes("TEST-1")
|
|
|
|
|
)
|
|
|
|
|
.toRelayWakuMessage()
|
|
|
|
|
|
|
|
|
|
let requestBody =
|
|
|
|
|
PushRequest(pubsubTopic: some(DefaultPubsubTopic), message: message)
|
|
|
|
|
let response = await restLightPushTest.restClient.sendPushRequest(requestBody)
|
|
|
|
|
|
|
|
|
|
echo "response", $response
|
|
|
|
|
|
|
|
|
|
# Then
|
|
|
|
|
check:
|
|
|
|
|
response.status == 200
|
|
|
|
|
response.data.relayPeerCount == some(1.uint32)
|
|
|
|
|
|
|
|
|
|
let pushRejectedProc = proc() {.async.} =
|
|
|
|
|
let message: RelayWakuMessage = fakeWakuMessage(
|
|
|
|
|
contentTopic = DefaultContentTopic, payload = toBytes("TEST-1")
|
|
|
|
|
)
|
|
|
|
|
.toRelayWakuMessage()
|
|
|
|
|
|
|
|
|
|
let requestBody =
|
|
|
|
|
PushRequest(pubsubTopic: some(DefaultPubsubTopic), message: message)
|
|
|
|
|
let response = await restLightPushTest.restClient.sendPushRequest(requestBody)
|
|
|
|
|
|
|
|
|
|
echo "response", $response
|
|
|
|
|
|
|
|
|
|
# Then
|
|
|
|
|
check:
|
|
|
|
|
response.status == 429
|
|
|
|
|
response.data.statusDesc.isSome() # Ensure error status description is present
|
|
|
|
|
response.data.statusDesc.get().startsWith(
|
|
|
|
|
"Request rejected due to too many requests"
|
|
|
|
|
) # Check specific error message
|
|
|
|
|
|
|
|
|
|
await pushProc()
|
|
|
|
|
await pushProc()
|
|
|
|
|
await pushProc()
|
|
|
|
|
await pushRejectedProc()
|
|
|
|
|
|
|
|
|
|
await sleepAsync(tokenPeriod)
|
|
|
|
|
|
|
|
|
|
for runCnt in 0 ..< 3:
|
|
|
|
|
let startTime = Moment.now()
|
|
|
|
|
for sendCnt in 0 ..< budgetCap:
|
|
|
|
|
await pushProc()
|
|
|
|
|
|
|
|
|
|
let endTime = Moment.now()
|
|
|
|
|
let elapsed: Duration = (endTime - startTime)
|
|
|
|
|
await sleepAsync(tokenPeriod - elapsed + 10.millis)
|
|
|
|
|
|
|
|
|
|
await restLightPushTest.shutdown()
|