feat: migrates e2e tests to use rest instead of rpc

This commit is contained in:
Arseniy Klempner 2024-02-15 17:53:51 -08:00
parent 199f6ab2ff
commit 6009af7453
No known key found for this signature in database
GPG Key ID: 59967D458EFBF01B
8 changed files with 97 additions and 255 deletions

View File

@ -58,8 +58,7 @@ export const TEST_TIMESTAMPS = [
BigInt(Date.now()) * BigInt(1000000), BigInt(Date.now()) * BigInt(1000000),
Date.now(), Date.now(),
1649153314, 1649153314,
1949153314000, 1949153314000
undefined
]; ];
export const MOCHA_HOOK_MAX_TIMEOUT = 50_000; export const MOCHA_HOOK_MAX_TIMEOUT = 50_000;

View File

@ -2,9 +2,10 @@ import { DecodedMessage } from "@waku/core";
import { import {
DefaultPubsubTopic, DefaultPubsubTopic,
PubsubTopic, PubsubTopic,
ShardInfo,
ShardingParams ShardingParams
} from "@waku/interfaces"; } from "@waku/interfaces";
import { Logger } from "@waku/utils"; import { ensureShardingConfigured, Logger } from "@waku/utils";
import { expect } from "chai"; import { expect } from "chai";
import { Args, MessageRpcQuery, MessageRpcResponse } from "../types"; import { Args, MessageRpcQuery, MessageRpcResponse } from "../types";
@ -27,7 +28,7 @@ export class ServiceNodesFleet {
pubsubTopics: PubsubTopic[], pubsubTopics: PubsubTopic[],
nodesToCreate: number = 3, nodesToCreate: number = 3,
strictChecking: boolean = false, strictChecking: boolean = false,
shardInfo?: ShardingParams, shardInfo?: ShardInfo,
_args?: Args, _args?: Args,
withoutFilter = false withoutFilter = false
): Promise<ServiceNodesFleet> { ): Promise<ServiceNodesFleet> {
@ -39,6 +40,9 @@ export class ServiceNodesFleet {
Math.random().toString(36).substring(7) Math.random().toString(36).substring(7)
); );
shardInfo = shardInfo
? ensureShardingConfigured(shardInfo).shardInfo
: undefined;
const args = getArgs(pubsubTopics, shardInfo, _args); const args = getArgs(pubsubTopics, shardInfo, _args);
await node.start(args, { await node.start(args, {
retries: 3 retries: 3
@ -100,22 +104,11 @@ export class ServiceNodesFleet {
async sendRelayMessage( async sendRelayMessage(
message: MessageRpcQuery, message: MessageRpcQuery,
pubsubTopic?: string, pubsubTopic: string = DefaultPubsubTopic
raw = false
): Promise<boolean> { ): Promise<boolean> {
let relayMessagePromises: Promise<boolean>[]; const relayMessagePromises: Promise<boolean>[] = this.nodes.map((node) =>
if (raw) { node.sendMessage(message, pubsubTopic)
relayMessagePromises = this.nodes.map((node) => );
node.rpcCall<boolean>("post_waku_v2_relay_v1_message", [
pubsubTopic && pubsubTopic,
message
])
);
} else {
relayMessagePromises = this.nodes.map((node) =>
node.sendMessage(message, pubsubTopic)
);
}
const relayMessages = await Promise.all(relayMessagePromises); const relayMessages = await Promise.all(relayMessagePromises);
return relayMessages.every((message) => message); return relayMessages.every((message) => message);
} }

View File

@ -4,13 +4,11 @@ import { Multiaddr, multiaddr } from "@multiformats/multiaddr";
import { DefaultPubsubTopic } from "@waku/interfaces"; import { DefaultPubsubTopic } from "@waku/interfaces";
import { isDefined } from "@waku/utils"; import { isDefined } from "@waku/utils";
import { Logger } from "@waku/utils"; import { Logger } from "@waku/utils";
import { bytesToHex, hexToBytes } from "@waku/utils/bytes";
import pRetry from "p-retry"; import pRetry from "p-retry";
import portfinder from "portfinder"; import portfinder from "portfinder";
import { import {
Args, Args,
KeyPair,
LogLevel, LogLevel,
MessageRpcQuery, MessageRpcQuery,
MessageRpcResponse, MessageRpcResponse,
@ -210,13 +208,26 @@ export class ServiceNode {
async peers(): Promise<string[]> { async peers(): Promise<string[]> {
this.checkProcess(); this.checkProcess();
return this.rpcCall<string[]>("get_waku_v2_admin_v1_peers", []); return this.restCall<string[]>(
"/admin/v1/peers",
"GET",
undefined,
async (response) => {
const data = await response.json();
return data?.length ? data : [];
}
);
} }
async info(): Promise<RpcInfoResponse> { async info(): Promise<RpcInfoResponse> {
this.checkProcess(); this.checkProcess();
return this.rpcCall<RpcInfoResponse>("get_waku_v2_debug_v1_info", []); return this.restCall<RpcInfoResponse>(
"/debug/v1/info",
"GET",
undefined,
async (response) => await response.json()
);
} }
async ensureSubscriptions( async ensureSubscriptions(
@ -233,9 +244,8 @@ export class ServiceNode {
async messages( async messages(
pubsubTopic: string = DefaultPubsubTopic pubsubTopic: string = DefaultPubsubTopic
): Promise<MessageRpcResponse[]> { ): Promise<MessageRpcResponse[]> {
pubsubTopic = encodeURIComponent(pubsubTopic);
return this.restCall<MessageRpcResponse[]>( return this.restCall<MessageRpcResponse[]>(
`/relay/v1/messages/${pubsubTopic}`, `/relay/v1/messages/${encodeURIComponent(pubsubTopic)}`,
"GET", "GET",
null, null,
async (response) => { async (response) => {
@ -268,10 +278,12 @@ export class ServiceNode {
message.timestamp = BigInt(new Date().valueOf()) * OneMillion; message.timestamp = BigInt(new Date().valueOf()) * OneMillion;
} }
return this.rpcCall<boolean>("post_waku_v2_relay_v1_message", [ return this.restCall<boolean>(
pubsubTopic, `/relay/v1/messages/${encodeURIComponent(pubsubTopic)}`,
message "POST",
]); message,
async (response) => response.status === 200
);
} }
async sendMessageAutosharding(message: MessageRpcQuery): Promise<boolean> { async sendMessageAutosharding(message: MessageRpcQuery): Promise<boolean> {
@ -281,9 +293,12 @@ export class ServiceNode {
message.timestamp = BigInt(new Date().valueOf()) * OneMillion; message.timestamp = BigInt(new Date().valueOf()) * OneMillion;
} }
return this.rpcCall<boolean>("post_waku_v2_relay_v1_auto_message", [ return this.restCall<boolean>(
message `/relay/v1/auto/message`,
]); "POST",
message,
async (response) => response.status === 200
);
} }
async messagesAutosharding( async messagesAutosharding(
@ -291,9 +306,8 @@ export class ServiceNode {
): Promise<MessageRpcResponse[]> { ): Promise<MessageRpcResponse[]> {
this.checkProcess(); this.checkProcess();
contentTopic = encodeURIComponent(contentTopic);
return this.restCall<MessageRpcResponse[]>( return this.restCall<MessageRpcResponse[]>(
`/relay/v1/auto/messages/${contentTopic}`, `/relay/v1/auto/messages/${encodeURIComponent(contentTopic)}`,
"GET", "GET",
null, null,
async (response) => { async (response) => {
@ -303,99 +317,6 @@ export class ServiceNode {
); );
} }
async getAsymmetricKeyPair(): Promise<KeyPair> {
this.checkProcess();
const { privateKey, publicKey, seckey, pubkey } = await this.rpcCall<{
seckey: string;
pubkey: string;
privateKey: string;
publicKey: string;
}>("get_waku_v2_private_v1_asymmetric_keypair", []);
// To be removed once https://github.com/vacp2p/rfc/issues/507 is fixed
if (seckey) {
return { privateKey: seckey, publicKey: pubkey };
} else {
return { privateKey, publicKey };
}
}
async postAsymmetricMessage(
message: MessageRpcQuery,
publicKey: Uint8Array,
pubsubTopic?: string
): Promise<boolean> {
this.checkProcess();
if (!message.payload) {
throw "Attempting to send empty message";
}
return this.rpcCall<boolean>("post_waku_v2_private_v1_asymmetric_message", [
pubsubTopic ? pubsubTopic : DefaultPubsubTopic,
message,
"0x" + bytesToHex(publicKey)
]);
}
async getAsymmetricMessages(
privateKey: Uint8Array,
pubsubTopic?: string
): Promise<MessageRpcResponse[]> {
this.checkProcess();
return await this.rpcCall<MessageRpcResponse[]>(
"get_waku_v2_private_v1_asymmetric_messages",
[
pubsubTopic ? pubsubTopic : DefaultPubsubTopic,
"0x" + bytesToHex(privateKey)
]
);
}
async getSymmetricKey(): Promise<Uint8Array> {
this.checkProcess();
return this.rpcCall<string>(
"get_waku_v2_private_v1_symmetric_key",
[]
).then(hexToBytes);
}
async postSymmetricMessage(
message: MessageRpcQuery,
symKey: Uint8Array,
pubsubTopic?: string
): Promise<boolean> {
this.checkProcess();
if (!message.payload) {
throw "Attempting to send empty message";
}
return this.rpcCall<boolean>("post_waku_v2_private_v1_symmetric_message", [
pubsubTopic ? pubsubTopic : DefaultPubsubTopic,
message,
"0x" + bytesToHex(symKey)
]);
}
async getSymmetricMessages(
symKey: Uint8Array,
pubsubTopic?: string
): Promise<MessageRpcResponse[]> {
this.checkProcess();
return await this.rpcCall<MessageRpcResponse[]>(
"get_waku_v2_private_v1_symmetric_messages",
[
pubsubTopic ? pubsubTopic : DefaultPubsubTopic,
"0x" + bytesToHex(symKey)
]
);
}
async getPeerId(): Promise<PeerId> { async getPeerId(): Promise<PeerId> {
if (this.peerId) return this.peerId; if (this.peerId) return this.peerId;
this.peerId = await this._getPeerId(); this.peerId = await this._getPeerId();
@ -437,37 +358,6 @@ export class ServiceNode {
return `http://127.0.0.1:${this.restPort}`; return `http://127.0.0.1:${this.restPort}`;
} }
async rpcCall<T>(
method: string,
params: Array<string | number | unknown>
): Promise<T> {
return await pRetry(
async () => {
try {
log.info("Making an RPC Query: ", method, params);
const res = await fetch(this.rpcUrl, {
method: "POST",
body: JSON.stringify({
jsonrpc: "2.0",
id: 1,
method,
params
}),
headers: new Headers({ "Content-Type": "application/json" })
});
const json = await res.json();
log.info(`Received RPC Response: `, JSON.stringify(json));
return json.result;
} catch (error) {
log.error(`${this.rpcUrl} failed with error:`, error);
await delay(10);
throw error;
}
},
{ retries: 5 }
);
}
async restCall<T>( async restCall<T>(
endpoint: string, endpoint: string,
method: "GET" | "POST", method: "GET" | "POST",
@ -507,6 +397,7 @@ export function defaultArgs(): Args {
relay: false, relay: false,
rest: true, rest: true,
rpcAdmin: true, rpcAdmin: true,
restAdmin: true,
websocketSupport: true, websocketSupport: true,
logLevel: LogLevel.Trace logLevel: LogLevel.Trace
}; };

View File

@ -5,6 +5,7 @@ export interface Args {
relay?: boolean; relay?: boolean;
rest?: boolean; rest?: boolean;
rpc?: boolean; rpc?: boolean;
restAdmin?: boolean;
rpcAdmin?: boolean; rpcAdmin?: boolean;
nodekey?: string; nodekey?: string;
portsShift?: number; portsShift?: number;

View File

@ -79,8 +79,7 @@ const runTests = (strictCheckNodes: boolean): void => {
payload: Buffer.from(utf8ToBytes(messageText)).toString("base64"), payload: Buffer.from(utf8ToBytes(messageText)).toString("base64"),
timestamp: testItem as any timestamp: testItem as any
}, },
DefaultPubsubTopic, DefaultPubsubTopic
true
); );
expect(await serviceNodes.messageCollector.waitForMessages(1)).to.eq( expect(await serviceNodes.messageCollector.waitForMessages(1)).to.eq(
@ -118,8 +117,7 @@ const runTests = (strictCheckNodes: boolean): void => {
payload: Buffer.from(utf8ToBytes(messageText)).toString("base64"), payload: Buffer.from(utf8ToBytes(messageText)).toString("base64"),
timestamp: "2023-09-06T12:05:38.609Z" as any timestamp: "2023-09-06T12:05:38.609Z" as any
}, },
DefaultPubsubTopic, DefaultPubsubTopic
true
); );
// Verify that no message was received // Verify that no message was received
@ -149,28 +147,6 @@ const runTests = (strictCheckNodes: boolean): void => {
); );
}); });
it("Check message with no pubsub topic is not received", async function () {
await subscription.subscribe(
[TestDecoder],
serviceNodes.messageCollector.callback
);
await delay(400);
await serviceNodes.sendRelayMessage(
{
contentTopic: TestContentTopic,
payload: Buffer.from(utf8ToBytes(messageText)).toString("base64"),
timestamp: BigInt(Date.now()) * BigInt(1000000)
},
undefined,
true
);
expect(await serviceNodes.messageCollector.waitForMessages(1)).to.eq(
false
);
});
it("Check message with no content topic is not received", async function () { it("Check message with no content topic is not received", async function () {
await subscription.subscribe( await subscription.subscribe(
[TestDecoder], [TestDecoder],
@ -204,8 +180,7 @@ const runTests = (strictCheckNodes: boolean): void => {
timestamp: BigInt(Date.now()) * BigInt(1000000), timestamp: BigInt(Date.now()) * BigInt(1000000),
payload: undefined as any payload: undefined as any
}, },
DefaultPubsubTopic, DefaultPubsubTopic
true
); );
// For go-waku the message is received (it is possible to send a message with no payload) // For go-waku the message is received (it is possible to send a message with no payload)

View File

@ -64,14 +64,17 @@ describe("Waku Filter V2: FilterPush", function () {
await subscription.subscribe([TestDecoder], messageCollector.callback); await subscription.subscribe([TestDecoder], messageCollector.callback);
await delay(400); await delay(400);
await nwaku.rpcCall("post_waku_v2_relay_v1_message", [ await nwaku.restCall<boolean>(
DefaultPubsubTopic, `/relay/v1/messages/${encodeURIComponent(DefaultPubsubTopic)}`,
"POST",
{ {
contentTopic: TestContentTopic, contentTopic: TestContentTopic,
payload: Buffer.from(utf8ToBytes(messageText)).toString("base64"), payload: Buffer.from(utf8ToBytes(messageText)).toString("base64"),
timestamp: testItem timestamp: testItem,
} version: 0
]); },
async (res) => res.status === 200
);
expect(await messageCollector.waitForMessages(1)).to.eq(true); expect(await messageCollector.waitForMessages(1)).to.eq(true);
messageCollector.verifyReceivedMessage(0, { messageCollector.verifyReceivedMessage(0, {
@ -95,14 +98,16 @@ describe("Waku Filter V2: FilterPush", function () {
await subscription.subscribe([TestDecoder], messageCollector.callback); await subscription.subscribe([TestDecoder], messageCollector.callback);
await delay(400); await delay(400);
await nwaku.rpcCall("post_waku_v2_relay_v1_message", [ await nwaku.restCall<boolean>(
DefaultPubsubTopic, `/relay/v1/messages/${encodeURIComponent(DefaultPubsubTopic)}`,
"POST",
{ {
contentTopic: TestContentTopic, contentTopic: TestContentTopic,
payload: Buffer.from(utf8ToBytes(messageText)).toString("base64"), payload: Buffer.from(utf8ToBytes(messageText)).toString("base64"),
timestamp: "2023-09-06T12:05:38.609Z" timestamp: "2023-09-06T12:05:38.609Z"
} },
]); async (res) => res.status === 200
);
// Verify that no message was received // Verify that no message was received
expect(await messageCollector.waitForMessages(1)).to.eq(false); expect(await messageCollector.waitForMessages(1)).to.eq(false);
@ -112,14 +117,16 @@ describe("Waku Filter V2: FilterPush", function () {
await subscription.subscribe([TestDecoder], messageCollector.callback); await subscription.subscribe([TestDecoder], messageCollector.callback);
await delay(400); await delay(400);
await nwaku.rpcCall("post_waku_v2_relay_v1_message", [ await nwaku.restCall<boolean>(
"DefaultPubsubTopic", `/relay/v1/messages/${encodeURIComponent("/othertopic")}`,
"POST",
{ {
contentTopic: TestContentTopic, contentTopic: TestContentTopic,
payload: Buffer.from(utf8ToBytes(messageText)).toString("base64"), payload: Buffer.from(utf8ToBytes(messageText)).toString("base64"),
timestamp: BigInt(Date.now()) * BigInt(1000000) timestamp: BigInt(Date.now()) * BigInt(1000000)
} },
]); async (res) => res.status === 200
);
expect(await messageCollector.waitForMessages(1)).to.eq(false); expect(await messageCollector.waitForMessages(1)).to.eq(false);
}); });
@ -128,13 +135,16 @@ describe("Waku Filter V2: FilterPush", function () {
await subscription.subscribe([TestDecoder], messageCollector.callback); await subscription.subscribe([TestDecoder], messageCollector.callback);
await delay(400); await delay(400);
await nwaku.rpcCall("post_waku_v2_relay_v1_message", [ await nwaku.restCall<boolean>(
`/relay/v1/messages/`,
"POST",
{ {
contentTopic: TestContentTopic, contentTopic: TestContentTopic,
payload: Buffer.from(utf8ToBytes(messageText)).toString("base64"), payload: Buffer.from(utf8ToBytes(messageText)).toString("base64"),
timestamp: BigInt(Date.now()) * BigInt(1000000) timestamp: BigInt(Date.now()) * BigInt(1000000)
} },
]); async (res) => res.status === 200
);
expect(await messageCollector.waitForMessages(1)).to.eq(false); expect(await messageCollector.waitForMessages(1)).to.eq(false);
}); });
@ -143,13 +153,15 @@ describe("Waku Filter V2: FilterPush", function () {
await subscription.subscribe([TestDecoder], messageCollector.callback); await subscription.subscribe([TestDecoder], messageCollector.callback);
await delay(400); await delay(400);
await nwaku.rpcCall("post_waku_v2_relay_v1_message", [ await nwaku.restCall<boolean>(
DefaultPubsubTopic, `/relay/v1/messages/${encodeURIComponent(DefaultPubsubTopic)}`,
"POST",
{ {
payload: Buffer.from(utf8ToBytes(messageText)).toString("base64"), payload: Buffer.from(utf8ToBytes(messageText)).toString("base64"),
timestamp: BigInt(Date.now()) * BigInt(1000000) timestamp: BigInt(Date.now()) * BigInt(1000000)
} },
]); async (res) => res.status === 200
);
expect(await messageCollector.waitForMessages(1)).to.eq(false); expect(await messageCollector.waitForMessages(1)).to.eq(false);
}); });
@ -158,13 +170,15 @@ describe("Waku Filter V2: FilterPush", function () {
await subscription.subscribe([TestDecoder], messageCollector.callback); await subscription.subscribe([TestDecoder], messageCollector.callback);
await delay(400); await delay(400);
await nwaku.rpcCall("post_waku_v2_relay_v1_message", [ await nwaku.restCall<boolean>(
DefaultPubsubTopic, `/relay/v1/messages/${encodeURIComponent(DefaultPubsubTopic)}`,
"POST",
{ {
contentTopic: TestContentTopic, contentTopic: TestContentTopic,
timestamp: BigInt(Date.now()) * BigInt(1000000) timestamp: BigInt(Date.now()) * BigInt(1000000)
} },
]); async (res) => res.status === 200
);
// For go-waku the message is received (it is possible to send a message with no payload) // For go-waku the message is received (it is possible to send a message with no payload)
if (nwaku.type == "go-waku") { if (nwaku.type == "go-waku") {
@ -178,56 +192,20 @@ describe("Waku Filter V2: FilterPush", function () {
await subscription.subscribe([TestDecoder], messageCollector.callback); await subscription.subscribe([TestDecoder], messageCollector.callback);
await delay(400); await delay(400);
await nwaku.rpcCall("post_waku_v2_relay_v1_message", [ await nwaku.restCall<boolean>(
DefaultPubsubTopic, `/relay/v1/messages/${encodeURIComponent(DefaultPubsubTopic)}`,
"POST",
{ {
contentTopic: TestContentTopic, contentTopic: TestContentTopic,
payload: 12345, payload: 12345,
timestamp: BigInt(Date.now()) * BigInt(1000000) timestamp: BigInt(Date.now()) * BigInt(1000000)
} },
]); async (res) => res.status === 200
);
expect(await messageCollector.waitForMessages(1)).to.eq(false); expect(await messageCollector.waitForMessages(1)).to.eq(false);
}); });
it("Check message with extra parameter is not received", async function () {
await subscription.subscribe([TestDecoder], messageCollector.callback);
await delay(400);
await nwaku.rpcCall("post_waku_v2_relay_v1_message", [
DefaultPubsubTopic,
"extraField",
{
contentTopic: TestContentTopic,
payload: Buffer.from(utf8ToBytes(messageText)).toString("base64"),
timestamp: BigInt(Date.now()) * BigInt(1000000)
}
]);
expect(await messageCollector.waitForMessages(1)).to.eq(false);
});
it("Check received message with extra option is received", async function () {
await subscription.subscribe([TestDecoder], messageCollector.callback);
await delay(400);
await nwaku.rpcCall("post_waku_v2_relay_v1_message", [
DefaultPubsubTopic,
{
contentTopic: TestContentTopic,
payload: Buffer.from(utf8ToBytes(messageText)).toString("base64"),
timestamp: BigInt(Date.now()) * BigInt(1000000),
extraOption: "extraOption"
}
]);
expect(await messageCollector.waitForMessages(1)).to.eq(true);
messageCollector.verifyReceivedMessage(0, {
expectedMessageText: messageText,
expectedContentTopic: TestContentTopic
});
});
// Will be skipped until https://github.com/waku-org/js-waku/issues/1464 si done // Will be skipped until https://github.com/waku-org/js-waku/issues/1464 si done
it.skip("Check message received after jswaku node is restarted", async function () { it.skip("Check message received after jswaku node is restarted", async function () {
// Subscribe and send message // Subscribe and send message

View File

@ -9,7 +9,11 @@ import {
Waku Waku
} from "@waku/interfaces"; } from "@waku/interfaces";
import { createLightNode } from "@waku/sdk"; import { createLightNode } from "@waku/sdk";
import { Logger } from "@waku/utils"; import {
ensureShardingConfigured,
Logger,
shardInfoToPubsubTopics
} from "@waku/utils";
import { utf8ToBytes } from "@waku/utils/bytes"; import { utf8ToBytes } from "@waku/utils/bytes";
import { Context } from "mocha"; import { Context } from "mocha";
import pRetry from "p-retry"; import pRetry from "p-retry";
@ -64,7 +68,7 @@ export async function runMultipleNodes(
pubsubTopics, pubsubTopics,
numServiceNodes, numServiceNodes,
strictChecking, strictChecking,
shardInfo, shardInfo ? ensureShardingConfigured(shardInfo).shardInfo : shardInfo,
undefined, undefined,
withoutFilter withoutFilter
); );
@ -74,7 +78,7 @@ export async function runMultipleNodes(
libp2p: { libp2p: {
addresses: { listen: ["/ip4/0.0.0.0/tcp/0/ws"] } addresses: { listen: ["/ip4/0.0.0.0/tcp/0/ws"] }
}, },
pubsubTopics: shardInfo ? undefined : pubsubTopics, pubsubTopics: shardInfo ? shardInfoToPubsubTopics(shardInfo) : pubsubTopics,
...((pubsubTopics.length !== 1 || ...((pubsubTopics.length !== 1 ||
pubsubTopics[0] !== DefaultPubsubTopic) && { pubsubTopics[0] !== DefaultPubsubTopic) && {
shardInfo: shardInfo shardInfo: shardInfo

View File

@ -16,6 +16,7 @@ describe("nwaku", () => {
"--relay=false", "--relay=false",
"--rest=true", "--rest=true",
"--rpc-admin=true", "--rpc-admin=true",
"--rest-admin=true",
"--websocket-support=true", "--websocket-support=true",
"--log-level=TRACE", "--log-level=TRACE",
"--ports-shift=42" "--ports-shift=42"