Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: upgrade interop tests #2289

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
b60fc00
chore: upgrade nwaku to v0.33.1
danisharora099 Oct 9, 2024
c4e9d10
chore: upgrade to nwaku 0.34.0
danisharora099 Jan 15, 2025
4696acf
feat: connect nwaku nodes amongst each other over relay
danisharora099 Jan 17, 2025
8f53c30
chore(lightpush): use multiple service nodes for lightpush (instead o…
danisharora099 Jan 17, 2025
620ec73
chore: all single-node lightpush requests should now be expected to fail
danisharora099 Jan 17, 2025
a53cdb3
chore: update sharding tests
danisharora099 Jan 17, 2025
3bdc596
chore: update tests
danisharora099 Jan 20, 2025
f95e3f3
chore: improve Docker network config reliability
danisharora099 Jan 20, 2025
6ab5a21
chore: deduplicate ecies encrypted payloads
danisharora099 Jan 22, 2025
623524b
chore: update to precise expects
danisharora099 Jan 22, 2025
4084aab
fix: return early if expect passes
danisharora099 Jan 22, 2025
4133fba
chore: lightpush 5 times instead of 30
danisharora099 Jan 22, 2025
bc3a9dd
fix: non duplicacy should happen in application-specific scenario
danisharora099 Jan 22, 2025
010480d
chore: update mocha config + fix epehermal tests
danisharora099 Jan 23, 2025
94c2d88
chore: reinstall deps after rebase
danisharora099 Jan 28, 2025
09a9dac
chore: attempt stability for test suite
danisharora099 Jan 31, 2025
1698697
fix: store tests to now use multiple nodes, delete uneeded test
danisharora099 Feb 3, 2025
5d209e9
fix: memory leak
danisharora099 Feb 3, 2025
6e31e20
Merge branch 'master' into weboko/interop-upgrade
weboko Feb 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -122,14 +122,14 @@ jobs:
uses: ./.github/workflows/test-node.yml
secrets: inherit
with:
nim_wakunode_image: ${{ inputs.nim_wakunode_image || 'wakuorg/nwaku:v0.31.0' }}
nim_wakunode_image: ${{ inputs.nim_wakunode_image || 'wakuorg/nwaku:v0.34.0' }}
test_type: node
allure_reports: true

node_optional:
uses: ./.github/workflows/test-node.yml
with:
nim_wakunode_image: ${{ inputs.nim_wakunode_image || 'wakuorg/nwaku:v0.31.0' }}
nim_wakunode_image: ${{ inputs.nim_wakunode_image || 'wakuorg/nwaku:v0.34.0' }}
test_type: node-optional

node_with_nwaku_master:
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/test-node.yml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ env:
jobs:
node:
runs-on: ubuntu-latest
timeout-minutes: 60 # Add a 1-hour timeout to fail faster
env:
WAKUNODE_IMAGE: ${{ inputs.nim_wakunode_image }}
ALLURE_REPORTS: ${{ inputs.allure_reports }}
Expand Down
3 changes: 2 additions & 1 deletion packages/tests/.mocharc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ const config = {
'loader=ts-node/esm'
],
exit: true,
retries: 4
retries: 2,
timeout: 150_000
};

if (process.env.CI) {
Expand Down
24 changes: 14 additions & 10 deletions packages/tests/src/lib/dockerode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ export default class Dockerode {
public containerId?: string;

private static network: Docker.Network;
private containerIp: string;
public readonly containerIp: string;

private constructor(imageName: string, containerIp: string) {
this.docker = new Docker();
Expand Down Expand Up @@ -107,6 +107,7 @@ export default class Dockerode {
const container = await this.docker.createContainer({
Image: this.IMAGE_NAME,
HostConfig: {
NetworkMode: NETWORK_NAME,
AutoRemove: true,
PortBindings: {
[`${restPort}/tcp`]: [{ HostPort: restPort.toString() }],
Expand All @@ -116,6 +117,8 @@ export default class Dockerode {
[`${discv5UdpPort}/udp`]: [{ HostPort: discv5UdpPort.toString() }]
})
},
Dns: ["8.8.8.8"],
Links: [],
Mounts: args.rlnRelayEthClientAddress
? [
{
Expand All @@ -135,18 +138,19 @@ export default class Dockerode {
[`${discv5UdpPort}/udp`]: {}
})
},
Cmd: argsArrayWithIP
});
await container.start();

await Dockerode.network.connect({
Container: container.id,
EndpointConfig: {
IPAMConfig: {
IPv4Address: this.containerIp
Cmd: argsArrayWithIP,
NetworkingConfig: {
EndpointsConfig: {
[NETWORK_NAME]: {
IPAMConfig: {
IPv4Address: this.containerIp
}
}
}
}
});
await container.start();

const logStream = fs.createWriteStream(logPath);

container.logs(
Expand Down
165 changes: 129 additions & 36 deletions packages/tests/src/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,24 +29,29 @@ export class ServiceNodesFleet {
_args?: Args,
withoutFilter = false
): Promise<ServiceNodesFleet> {
const serviceNodePromises = Array.from(
{ length: nodesToCreate },
async () => {
const node = new ServiceNode(
makeLogFileName(mochaContext) +
Math.random().toString(36).substring(7)
);
const nodes: ServiceNode[] = [];

for (let i = 0; i < nodesToCreate; i++) {
const node = new ServiceNode(
makeLogFileName(mochaContext) + Math.random().toString(36).substring(7)
);

const args = getArgs(networkConfig, _args);
await node.start(args, {
retries: 3
});
const args = getArgs(networkConfig, _args);

return node;
// If this is not the first node and previous node had a nodekey, use its multiaddr as static node
if (i > 0) {
const prevNode = nodes[i - 1];
const multiaddr = await prevNode.getExternalWebsocketMultiaddr();
args.staticnode = multiaddr;
}
);

const nodes = await Promise.all(serviceNodePromises);
await node.start(args, {
retries: 3
});

nodes.push(node);
}

return new ServiceNodesFleet(nodes, withoutFilter, strictChecking);
}

Expand Down Expand Up @@ -95,7 +100,24 @@ export class ServiceNodesFleet {
return relayMessages.every((message) => message);
}

public async confirmMessageLength(numMessages: number): Promise<void> {
public async confirmMessageLength(
numMessages: number,
{ encryptedPayload }: { encryptedPayload?: boolean } = {
encryptedPayload: false
}
): Promise<void> {
if (encryptedPayload) {
const filteredMessageList = Array.from(
new Set(
this.messageCollector.messageList
.filter((msg) => msg.payload?.toString)
.map((msg) => msg.payload.toString())
)
);
expect(filteredMessageList.length).to.equal(numMessages);
return;
}

if (this.strictChecking) {
await Promise.all(
this.nodes.map(async (node) =>
Expand All @@ -120,7 +142,7 @@ export class ServiceNodesFleet {

class MultipleNodesMessageCollector {
public callback: (msg: DecodedMessage) => void = () => {};
protected messageList: Array<DecodedMessage> = [];
public readonly messageList: Array<DecodedMessage> = [];
public constructor(
private messageCollectors: MessageCollector[],
private relayNodes?: ServiceNode[],
Expand Down Expand Up @@ -170,21 +192,21 @@ class MultipleNodesMessageCollector {
}
): boolean {
if (this.strictChecking) {
return this.messageCollectors.every((collector) => {
return this.messageCollectors.every((collector, _i) => {
try {
collector.verifyReceivedMessage(index, options);
return true; // Verification successful
return true;
} catch (error) {
return false; // Verification failed, continue with the next collector
return false;
}
});
} else {
return this.messageCollectors.some((collector) => {
return this.messageCollectors.some((collector, _i) => {
try {
collector.verifyReceivedMessage(index, options);
return true; // Verification successful
return true;
} catch (error) {
return false; // Verification failed, continue with the next collector
return false;
}
});
}
Expand All @@ -204,34 +226,106 @@ class MultipleNodesMessageCollector {
const startTime = Date.now();
const pubsubTopic = options?.pubsubTopic || DefaultTestPubsubTopic;
const timeoutDuration = options?.timeoutDuration || 400;
const maxTimeout = Math.min(timeoutDuration * numMessages, 30000);
const exact = options?.exact || false;

try {
while (Date.now() - startTime < maxTimeout) {
// Check if we already have enough messages
if (this.messageList.length >= numMessages) {
if (exact && this.messageList.length !== numMessages) {
log.warn(
`Was expecting exactly ${numMessages} messages. Received: ${this.messageList.length}`
);
return false;
}
return true;
}

// If we have relay nodes, check their messages
if (this.relayNodes) {
const nodeMessages = await Promise.all(
this.relayNodes.map(async (node) => node.messages(pubsubTopic))
);

// Check if we have enough messages according to strictness
const hasEnoughMessages = this.strictChecking
? nodeMessages.every((msgs) => msgs.length >= numMessages)
: nodeMessages.some((msgs) => msgs.length >= numMessages);

if (hasEnoughMessages) {
return true;
}
}

await delay(10);
}

log.warn(`Timeout waiting for messages after ${maxTimeout}ms`);
return false;
} catch (error) {
log.error("Error in waitForMessages:", error);
return false;
}
}

/**
* Waits for a total number of messages across all nodes using autosharding.
*/
public async waitForMessagesAutosharding(
numMessages: number,
options?: {
contentTopic: string;
timeoutDuration?: number;
exact?: boolean;
}
): Promise<boolean> {
const startTime = Date.now();
const timeoutDuration = options?.timeoutDuration || 400;
const exact = options?.exact || false;

while (this.messageList.length < numMessages) {
if (this.relayNodes) {
if (this.strictChecking) {
// In strict mode, all nodes must have the messages
const results = await Promise.all(
this.relayNodes.map(async (node) => {
const msgs = await node.messages(pubsubTopic);
return msgs.length >= numMessages;
this.messageCollectors.map(async (collector) => {
return collector.waitForMessagesAutosharding(
numMessages,
options
);
})
);
return results.every((result) => result);
if (results.every((result) => result)) {
return true;
}
} else {
// In non-strict mode, at least one node must have the messages
const results = await Promise.all(
this.relayNodes.map(async (node) => {
const msgs = await node.messages(pubsubTopic);
return msgs.length >= numMessages;
this.messageCollectors.map(async (collector) => {
return collector.waitForMessagesAutosharding(
numMessages,
options
);
})
);
return results.some((result) => result);
if (results.some((result) => result)) {
return true;
}
}
}

if (Date.now() - startTime > timeoutDuration * numMessages) {
return false;
}
if (Date.now() - startTime > timeoutDuration * numMessages) {
return false;
}

await delay(10);
await delay(10);
} else {
// If no relay nodes, just wait for messages in the list
if (Date.now() - startTime > timeoutDuration * numMessages) {
return false;
}
await delay(10);
}
}

if (exact) {
Expand All @@ -241,7 +335,6 @@ class MultipleNodesMessageCollector {
log.warn(
`Was expecting exactly ${numMessages} messages. Received: ${this.messageList.length}`
);

return false;
}
} else {
Expand Down
28 changes: 27 additions & 1 deletion packages/tests/src/lib/service_node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
const NODE_READY_LOG_LINE = "Node setup complete";

export const DOCKER_IMAGE_NAME =
process.env.WAKUNODE_IMAGE || "wakuorg/nwaku:v0.31.0";
process.env.WAKUNODE_IMAGE || "wakuorg/nwaku:v0.34.0";

const LOG_DIR = "./log";

Expand Down Expand Up @@ -355,7 +355,7 @@
public async restCall<T>(
endpoint: string,
method: "GET" | "POST",
body: any = null,

Check warning on line 358 in packages/tests/src/lib/service_node.ts

View workflow job for this annotation

GitHub Actions / check

Unexpected any. Specify a different type

Check warning on line 358 in packages/tests/src/lib/service_node.ts

View workflow job for this annotation

GitHub Actions / proto

Unexpected any. Specify a different type
processResponse: (response: Response) => Promise<T>
): Promise<T> {
this.checkProcess();
Expand All @@ -382,6 +382,15 @@
throw `Container hasn't started`;
}
}

public async getExternalWebsocketMultiaddr(): Promise<string | undefined> {
if (!this.docker?.container) {
return undefined;
}
const containerIp = this.docker.containerIp;
const peerId = await this.getPeerId();
return `/ip4/${containerIp}/tcp/${this.websocketPort}/ws/p2p/${peerId}`;
}
}

export function defaultArgs(): Args {
Expand All @@ -402,3 +411,20 @@
listenAddresses: string[];
enrUri?: string;
}

export async function verifyServiceNodesConnected(
nodes: ServiceNode[]
): Promise<void> {
for (const node of nodes) {
const peers = await node.peers();
log.info(`Service node ${node.containerName} peers:`, peers.length);
log.info(`Service node ${node.containerName} peers:`, peers);

if (nodes.length > 1 && peers.length === 0) {
log.error(`Service node ${node.containerName} has no peers connected`);
throw new Error(
`Service node ${node.containerName} has no peers connected`
);
}
}
}
11 changes: 10 additions & 1 deletion packages/tests/src/run-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { promisify } from "util";

const execAsync = promisify(exec);

const WAKUNODE_IMAGE = process.env.WAKUNODE_IMAGE || "wakuorg/nwaku:v0.31.0";
const WAKUNODE_IMAGE = process.env.WAKUNODE_IMAGE || "wakuorg/nwaku:v0.34.0";

async function main() {
try {
Expand Down Expand Up @@ -40,6 +40,15 @@ async function main() {

mocha.on("exit", (code) => {
console.log(`Mocha tests exited with code ${code}`);
try {
execAsync(
`docker ps -q -f "ancestor=${WAKUNODE_IMAGE}" | xargs -r docker stop`
).catch((error) => {
console.error("Error cleaning up containers:", error);
});
} catch (error) {
console.error("Error cleaning up containers:", error);
}
process.exit(code || 0);
});
}
Expand Down
Loading
Loading