mirror of
https://github.com/logos-messaging/js-waku.git
synced 2026-01-10 09:43:10 +00:00
362 lines
12 KiB
TypeScript
362 lines
12 KiB
TypeScript
import { test, expect } from "@playwright/test";
|
|
import axios from "axios";
|
|
import { GenericContainer, StartedTestContainer } from "testcontainers";
|
|
import { createLightNode, waitForRemotePeer, LightNode, Protocols } from "@waku/sdk";
|
|
|
|
test.describe.configure({ mode: "serial" });
|
|
|
|
let container: StartedTestContainer;
|
|
let baseUrl = "http://127.0.0.1:8080";
|
|
let wakuNode: LightNode;
|
|
let unsubscribe: () => void;
|
|
|
|
test.beforeAll(async () => {
|
|
// Build and run the container once for the suite; reuse across tests
|
|
const generic = new GenericContainer(
|
|
"waku-browser-tests:local",
|
|
).withExposedPorts(8080);
|
|
|
|
container = await generic.start();
|
|
|
|
console.log("Container started, waiting for initialization...");
|
|
await new Promise((r) => setTimeout(r, 2000)); // Give container more time to start
|
|
|
|
// Get initial container logs for debugging
|
|
const logs = await container.logs({ tail: 100 });
|
|
logs.on("data", (b) => process.stdout.write("[container] " + b.toString()));
|
|
logs.on("error", (err) => console.error("[container log error]", err));
|
|
|
|
const mappedPort = container.getMappedPort(8080);
|
|
baseUrl = `http://127.0.0.1:${mappedPort}`;
|
|
|
|
// Probe readiness - wait for both server and browser
|
|
let serverReady = false;
|
|
// let browserReady = false;
|
|
|
|
// Wait for server to be ready with more debugging
|
|
for (let i = 0; i < 60; i++) { // Increased attempts from 40 to 60
|
|
try {
|
|
const res = await axios.get(`${baseUrl}/`, { timeout: 2000 }); // Increased timeout
|
|
if (res.status === 200) {
|
|
console.log(`Server is ready after ${i + 1} attempts`);
|
|
serverReady = true;
|
|
break;
|
|
}
|
|
} catch (error: any) {
|
|
if (i % 10 === 0) { // Log every 10th attempt
|
|
console.log(`Attempt ${i + 1}/60 failed:`, error.code || error.message);
|
|
}
|
|
}
|
|
await new Promise((r) => setTimeout(r, 1000)); // Increased wait time from 500ms to 1000ms
|
|
}
|
|
|
|
if (!serverReady) {
|
|
// Get final container logs for debugging
|
|
try {
|
|
const finalLogs = await container.logs({ tail: 50 });
|
|
console.log("=== Final Container Logs ===");
|
|
finalLogs.on("data", (b) => console.log(b.toString()));
|
|
await new Promise(r => setTimeout(r, 1000)); // Give logs time to print
|
|
} catch (logError) {
|
|
console.error("Failed to get container logs:", logError);
|
|
}
|
|
}
|
|
|
|
expect(serverReady).toBe(true);
|
|
|
|
await new Promise((r) => setTimeout(r, 500));
|
|
});
|
|
|
|
test.afterAll(async () => {
|
|
// Clean up subscription first
|
|
try {
|
|
if (typeof unsubscribe === 'function') {
|
|
unsubscribe();
|
|
console.log("Filter subscription cleaned up");
|
|
}
|
|
} catch (error) {
|
|
console.warn("Filter cleanup had issues:", (error as any).message);
|
|
}
|
|
|
|
if (wakuNode) {
|
|
console.log("Stopping Waku node...");
|
|
try {
|
|
await wakuNode.stop();
|
|
console.log("Waku node stopped successfully");
|
|
} catch (error) {
|
|
console.warn("Waku node stop had issues:", (error as any).message);
|
|
}
|
|
}
|
|
|
|
if (container) {
|
|
console.log("Stopping container gracefully...");
|
|
try {
|
|
// Give the container a chance to shut down gracefully
|
|
await container.stop({ timeout: 10000 });
|
|
console.log("Container stopped successfully");
|
|
} catch (error) {
|
|
console.warn("Container stop had issues (expected):", (error as any).message);
|
|
}
|
|
}
|
|
});
|
|
|
|
test("container: health endpoint", async () => {
|
|
const res = await axios.get(`${baseUrl}/`);
|
|
expect(res.status).toBe(200);
|
|
expect(res.data.status).toBe("Waku simulation server is running");
|
|
});
|
|
|
|
// New focused test: validate create-node using window.wakuApi.start()
|
|
test("container: create-node only (wakuApi.start)", async () => {
|
|
const res = await axios.post(`${baseUrl}/admin/v1/create-node`, {
|
|
defaultBootstrap: true,
|
|
networkConfig: {
|
|
clusterId: 1,
|
|
numShardsInCluster: 8 // Enable auto-sharding
|
|
}
|
|
});
|
|
expect(res.status).toBe(200);
|
|
expect(res.data.success).toBe(true);
|
|
});
|
|
|
|
test("container: create/start node and push", async () => {
|
|
// Create node with required networkConfig
|
|
await axios.post(`${baseUrl}/admin/v1/create-node`, {
|
|
defaultBootstrap: true,
|
|
networkConfig: {
|
|
clusterId: 1,
|
|
numShardsInCluster: 8 // Enable auto-sharding
|
|
}
|
|
});
|
|
await axios.post(`${baseUrl}/admin/v1/start-node`);
|
|
|
|
// Wait for Lightpush peers with longer timeout for real network connections
|
|
console.log("⏳ Waiting for Lightpush peers to connect...");
|
|
try {
|
|
await axios.post(`${baseUrl}/waku/v1/wait-for-peers`, {
|
|
timeoutMs: 30000,
|
|
protocols: ["lightpush"] // 30 second timeout for real network
|
|
});
|
|
console.log("✅ Found Lightpush peers");
|
|
} catch (e) {
|
|
console.error("❌ Failed to find Lightpush peers:", e);
|
|
throw new Error("Failed to connect to Lightpush peers - this should succeed in all environments");
|
|
}
|
|
|
|
// Also wait for Filter peers
|
|
console.log("⏳ Waiting for Filter peers to connect...");
|
|
try {
|
|
await axios.post(`${baseUrl}/waku/v1/wait-for-peers`, {
|
|
timeoutMs: 30000,
|
|
protocols: ["filter"] // 30 second timeout for real network
|
|
});
|
|
console.log("✅ Found Filter peers");
|
|
} catch (e) {
|
|
console.warn("⚠️ No Filter peers found (non-critical):", e);
|
|
}
|
|
|
|
// Test lightpush endpoint - expect it to succeed with real peers
|
|
console.log("📤 Attempting to push message to Waku network...");
|
|
const testMessage = "Hello from Docker container test";
|
|
const base64Payload = btoa(testMessage); // Convert to base64
|
|
|
|
const push = await axios.post(`${baseUrl}/lightpush/v3/message`, {
|
|
pubsubTopic: "/waku/2/default-waku/proto",
|
|
message: {
|
|
contentTopic: "/test/1/message/proto",
|
|
payload: base64Payload,
|
|
version: 1
|
|
},
|
|
});
|
|
|
|
// Verify successful push (v3 API returns { success: boolean, result?: SDKProtocolResult })
|
|
expect(push.status).toBe(200);
|
|
expect(push.data).toBeDefined();
|
|
expect(push.data.success).toBe(true);
|
|
expect(push.data.result).toBeDefined();
|
|
expect(push.data.result.successes).toBeDefined();
|
|
expect(push.data.result.successes.length).toBeGreaterThan(0);
|
|
console.log("✅ Message successfully pushed to Waku network!");
|
|
|
|
// Log a clean summary instead of raw JSON
|
|
const successCount = push.data.result.successes?.length || 0;
|
|
const failureCount = push.data.result.failures?.length || 0;
|
|
console.log(`📊 Push Summary: ${successCount} success(es), ${failureCount} failure(s)`);
|
|
|
|
if (successCount > 0) {
|
|
console.log("📤 Successfully sent to peers:");
|
|
push.data.result.successes.forEach((peerIdString: string, index: number) => {
|
|
console.log(` ${index + 1}. ${peerIdString}`);
|
|
});
|
|
}
|
|
|
|
if (failureCount > 0) {
|
|
console.log("❌ Failed to send to peers:");
|
|
push.data.result.failures.forEach((failure: { error: string; peerId?: string }, index: number) => {
|
|
const peerInfo = failure.peerId || 'unknown peer';
|
|
console.log(` ${index + 1}. ${peerInfo} - ${failure.error}`);
|
|
});
|
|
}
|
|
});
|
|
|
|
test("cross-network message delivery: SDK light node receives server lightpush", async () => {
|
|
const contentTopic = "/test/1/cross-network/proto";
|
|
const pubsubTopic = "/waku/2/default-waku/proto";
|
|
const testMessage = "Hello from SDK to Docker server test";
|
|
|
|
console.log("🚀 Creating SDK light node with same config as server...");
|
|
|
|
// Create light node with same configuration as the docker server
|
|
wakuNode = await createLightNode({
|
|
defaultBootstrap: true,
|
|
networkConfig: {
|
|
clusterId: 1,
|
|
numShardsInCluster: 8
|
|
},
|
|
libp2p: {
|
|
filterMultiaddrs: false
|
|
}
|
|
});
|
|
|
|
await wakuNode.start();
|
|
console.log("✅ SDK light node started");
|
|
|
|
// Wait for filter peer to connect
|
|
console.log("⏳ Waiting for Filter peers to connect...");
|
|
await waitForRemotePeer(wakuNode, [Protocols.Filter]);
|
|
console.log("✅ Connected to Filter peers");
|
|
|
|
// Set up message subscription
|
|
console.log("📡 Setting up message subscription...");
|
|
const messages: any[] = [];
|
|
|
|
console.log(`🔍 Subscribing to contentTopic: "${contentTopic}" on pubsubTopic: "${pubsubTopic}"`);
|
|
|
|
// Create decoder that matches the server's encoder (using same pattern as server)
|
|
const decoder = wakuNode.createDecoder({ contentTopic, pubsubTopic });
|
|
console.log("🔧 Created decoder with pubsubTopic:", decoder.pubsubTopic);
|
|
|
|
// Set up message subscription and WAIT for it to be established
|
|
try {
|
|
unsubscribe = await wakuNode.filter.subscribe(
|
|
[decoder],
|
|
(message) => {
|
|
console.log("📥 Received message via Filter!");
|
|
console.log(`📝 Message details: topic=${message.contentTopic}, payload="${new TextDecoder().decode(message.payload)}"`);
|
|
messages.push(message);
|
|
}
|
|
);
|
|
console.log("✅ Filter subscription established successfully");
|
|
} catch (error) {
|
|
console.error("❌ Failed to subscribe to Filter:", error);
|
|
throw error;
|
|
}
|
|
|
|
// Give extra time for subscription to propagate to network
|
|
console.log("⏳ Waiting for subscription to propagate...");
|
|
await new Promise(r => setTimeout(r, 2000));
|
|
|
|
const messagePromise = new Promise<void>((resolve) => {
|
|
const originalLength = messages.length;
|
|
const checkForMessage = () => {
|
|
if (messages.length > originalLength) {
|
|
resolve();
|
|
} else {
|
|
setTimeout(checkForMessage, 100);
|
|
}
|
|
};
|
|
checkForMessage();
|
|
});
|
|
|
|
// Create and start server node
|
|
console.log("🔧 Creating server node...");
|
|
await axios.post(`${baseUrl}/admin/v1/create-node`, {
|
|
defaultBootstrap: true,
|
|
networkConfig: {
|
|
clusterId: 1,
|
|
numShardsInCluster: 8
|
|
}
|
|
});
|
|
|
|
await axios.post(`${baseUrl}/admin/v1/start-node`);
|
|
console.log("✅ Server node created and started");
|
|
|
|
// CRITICAL: Wait for server node to find peers BEFORE attempting to send
|
|
console.log("⏳ Waiting for server to connect to Lightpush peers...");
|
|
await axios.post(`${baseUrl}/waku/v1/wait-for-peers`, {
|
|
timeoutMs: 30000,
|
|
protocols: ["lightpush"]
|
|
});
|
|
console.log("✅ Server connected to Lightpush peers");
|
|
|
|
console.log("⏳ Waiting for server to connect to Filter peers...");
|
|
try {
|
|
await axios.post(`${baseUrl}/waku/v1/wait-for-peers`, {
|
|
timeoutMs: 30000,
|
|
protocols: ["filter"]
|
|
});
|
|
console.log("✅ Server connected to Filter peers");
|
|
} catch (e) {
|
|
console.warn("⚠️ Server didn't connect to Filter peers:", e);
|
|
}
|
|
|
|
// Give nodes extra time to discover each other and establish proper mesh connectivity
|
|
console.log("⏳ Allowing time for nodes to discover each other...");
|
|
await new Promise(r => setTimeout(r, 8000));
|
|
|
|
// Debug: Check peer information before sending
|
|
console.log("🔍 Checking peer connections...");
|
|
try {
|
|
const peerInfo = await axios.get(`${baseUrl}/waku/v1/peer-info`);
|
|
console.log(`📊 Server peer count: ${JSON.stringify(peerInfo.data)}`);
|
|
} catch (e) {
|
|
console.warn("⚠️ Could not get peer info:", e);
|
|
}
|
|
|
|
// IMPORTANT: Verify filter is ready before sending
|
|
console.log("🔍 Verifying filter subscription is active before sending...");
|
|
|
|
// Send message via server's lightpush
|
|
console.log("📤 Sending message via server lightpush...");
|
|
const base64Payload = btoa(testMessage);
|
|
|
|
const pushResponse = await axios.post(`${baseUrl}/lightpush/v3/message`, {
|
|
pubsubTopic,
|
|
message: {
|
|
contentTopic,
|
|
payload: base64Payload,
|
|
version: 1
|
|
}
|
|
});
|
|
|
|
expect(pushResponse.status).toBe(200);
|
|
expect(pushResponse.data.success).toBe(true);
|
|
console.log("✅ Message sent via server lightpush");
|
|
|
|
// Wait for message to be received by SDK node (with longer timeout for network propagation)
|
|
console.log("⏳ Waiting for message to be received by SDK node...");
|
|
console.log("💡 Note: Filter messages may take time to propagate through the network...");
|
|
|
|
await Promise.race([
|
|
messagePromise,
|
|
new Promise((_, reject) =>
|
|
setTimeout(() => {
|
|
console.error(`❌ Timeout after 45 seconds. Messages received: ${messages.length}`);
|
|
reject(new Error("Timeout waiting for message"));
|
|
}, 45000)
|
|
)
|
|
]);
|
|
|
|
// Verify message was received
|
|
expect(messages).toHaveLength(1);
|
|
const receivedMessage = messages[0];
|
|
expect(receivedMessage.contentTopic).toBe(contentTopic);
|
|
|
|
// Decode and verify payload
|
|
const receivedPayload = new TextDecoder().decode(receivedMessage.payload);
|
|
expect(receivedPayload).toBe(testMessage);
|
|
|
|
console.log("🎉 SUCCESS: Message successfully sent from server and received by SDK node!");
|
|
console.log(`📝 Message content: "${receivedPayload}"`);
|
|
});
|