#!/usr/bin/env node // Test script for mixed-content message testing // Tests sending a mix of text, json, table, image, audio, video, and binary data // from JavaScript serviceA to JavaScript serviceB using NATSBridge.js smartsend // // This test demonstrates that any combination and any number of mixed content // can be sent and received correctly. const { smartsend, uuid4, log_trace, _serialize_data } = require('./src/NATSBridge'); // Configuration const SUBJECT = "/NATSBridge_mix_test"; const NATS_URL = "nats.yiem.cc"; const FILESERVER_URL = "http://192.168.88.104:8080"; // Create correlation ID for tracing const correlation_id = uuid4(); // Helper: Log with correlation ID function log_trace(message) { const timestamp = new Date().toISOString(); console.log(`[${timestamp}] [Correlation: ${correlation_id}] ${message}`); } // File upload handler for plik server async function plik_upload_handler(fileserver_url, dataname, data, correlation_id) { log_trace(correlation_id, `Uploading ${dataname} to fileserver: ${fileserver_url}`); // Step 1: Get upload ID and token const url_getUploadID = `${fileserver_url}/upload`; const headers = { "Content-Type": "application/json" }; const body = JSON.stringify({ OneShot: true }); let response = await fetch(url_getUploadID, { method: "POST", headers: headers, body: body }); if (!response.ok) { throw new Error(`Failed to get upload ID: ${response.status} ${response.statusText}`); } const responseJson = await response.json(); const uploadid = responseJson.id; const uploadtoken = responseJson.uploadToken; // Step 2: Upload file data const url_upload = `${fileserver_url}/file/${uploadid}`; // Create multipart form data const formData = new FormData(); const blob = new Blob([data], { type: "application/octet-stream" }); formData.append("file", blob, dataname); response = await fetch(url_upload, { method: "POST", headers: { "X-UploadToken": uploadtoken }, body: formData }); if (!response.ok) { throw new Error(`Failed to upload file: ${response.status} ${response.statusText}`); } const fileResponseJson = await response.json(); const fileid = fileResponseJson.id; // Build the download URL const url = `${fileserver_url}/file/${uploadid}/${fileid}/${encodeURIComponent(dataname)}`; log_trace(correlation_id, `Uploaded to URL: ${url}`); return { status: response.status, uploadid: uploadid, fileid: fileid, url: url }; } // Helper: Create sample data for each type function create_sample_data() { // Text data (small - direct transport) const text_data = "Hello! This is a test chat message. 🎉\nHow are you doing today? 😊"; // Dictionary/JSON data (medium - could be direct or link) const dict_data = { type: "chat", sender: "serviceA", receiver: "serviceB", metadata: { timestamp: new Date().toISOString(), priority: "high", tags: ["urgent", "chat", "test"] }, content: { text: "This is a JSON-formatted chat message with nested structure.", format: "markdown", mentions: ["user1", "user2"] } }; // Table data (small - direct transport) - NOT IMPLEMENTED (requires apache-arrow) // const table_data_small = {...}; // Table data (large - link transport) - NOT IMPLEMENTED (requires apache-arrow) // const table_data_large = {...}; // Image data (small binary - direct transport) // Create a simple 10x10 pixel PNG-like data const image_width = 10; const image_height = 10; let image_data = new Uint8Array(128); // PNG header + pixel data // PNG header image_data[0] = 0x89; image_data[1] = 0x50; image_data[2] = 0x4E; image_data[3] = 0x47; image_data[4] = 0x0D; image_data[5] = 0x0A; image_data[6] = 0x1A; image_data[7] = 0x0A; // Simple RGB data (10*10*3 = 300 bytes) for (let i = 0; i < 300; i++) { image_data[i + 8] = 0xFF; // Red pixel } // Image data (large - link transport) const large_image_width = 500; const large_image_height = 1000; const large_image_data = new Uint8Array(large_image_width * large_image_height * 3 + 8); // PNG header large_image_data[0] = 0x89; large_image_data[1] = 0x50; large_image_data[2] = 0x4E; large_image_data[3] = 0x47; large_image_data[4] = 0x0D; large_image_data[5] = 0x0A; large_image_data[6] = 0x1A; large_image_data[7] = 0x0A; // Random RGB data for (let i = 0; i < large_image_width * large_image_height * 3; i++) { large_image_data[i + 8] = Math.floor(Math.random() * 255); } // Audio data (small binary - direct transport) const audio_data = new Uint8Array(100); for (let i = 0; i < 100; i++) { audio_data[i] = Math.floor(Math.random() * 255); } // Audio data (large - link transport) const large_audio_data = new Uint8Array(1_500_000); for (let i = 0; i < 1_500_000; i++) { large_audio_data[i] = Math.floor(Math.random() * 255); } // Video data (small binary - direct transport) const video_data = new Uint8Array(150); for (let i = 0; i < 150; i++) { video_data[i] = Math.floor(Math.random() * 255); } // Video data (large - link transport) const large_video_data = new Uint8Array(1_500_000); for (let i = 0; i < 1_500_000; i++) { large_video_data[i] = Math.floor(Math.random() * 255); } // Binary data (small - direct transport) const binary_data = new Uint8Array(200); for (let i = 0; i < 200; i++) { binary_data[i] = Math.floor(Math.random() * 255); } // Binary data (large - link transport) const large_binary_data = new Uint8Array(1_500_000); for (let i = 0; i < 1_500_000; i++) { large_binary_data[i] = Math.floor(Math.random() * 255); } return { text_data, dict_data, // table_data_small, // table_data_large, image_data, large_image_data, audio_data, large_audio_data, video_data, large_video_data, binary_data, large_binary_data }; } // Sender: Send mixed content via smartsend async function test_mix_send() { // Create sample data const { text_data, dict_data, image_data, large_image_data, audio_data, large_audio_data, video_data, large_video_data, binary_data, large_binary_data } = create_sample_data(); // Create payloads list - mixed content with both small and large data // Small data uses direct transport, large data uses link transport const payloads = [ // Small data (direct transport) - text, dictionary { dataname: "chat_text", data: text_data, type: "text" }, { dataname: "chat_json", data: dict_data, type: "dictionary" }, // { dataname: "chat_table_small", data: table_data_small, type: "table" }, // Large data (link transport) - large image, large audio, large video, large binary // { dataname: "chat_table_large", data: table_data_large, type: "table" }, { dataname: "user_image_large", data: large_image_data, type: "image" }, { dataname: "audio_clip_large", data: large_audio_data, type: "audio" }, { dataname: "video_clip_large", data: large_video_data, type: "video" }, { dataname: "binary_file_large", data: large_binary_data, type: "binary" } ]; // Use smartsend with mixed content const env = await smartsend( SUBJECT, payloads, { natsUrl: NATS_URL, fileserverUrl: FILESERVER_URL, fileserverUploadHandler: plik_upload_handler, sizeThreshold: 1_000_000, correlationId: correlation_id, msgPurpose: "chat", senderName: "mix_sender", receiverName: "", receiverId: "", replyTo: "", replyToMsgId: "" } ); log_trace(`Sent message with ${env.payloads.length} payloads`); // Log transport type for each payload for (let i = 0; i < env.payloads.length; i++) { const payload = env.payloads[i]; log_trace(`Payload ${i + 1} ('${payload.dataname}'):`); log_trace(` Transport: ${payload.transport}`); log_trace(` Type: ${payload.type}`); log_trace(` Size: ${payload.size} bytes`); log_trace(` Encoding: ${payload.encoding}`); if (payload.transport === "link") { log_trace(` URL: ${payload.data}`); } } // Summary console.log("\n--- Transport Summary ---"); const direct_count = env.payloads.filter(p => p.transport === "direct").length; const link_count = env.payloads.filter(p => p.transport === "link").length; log_trace(`Direct transport: ${direct_count} payloads`); log_trace(`Link transport: ${link_count} payloads`); } // Run the test console.log("Starting mixed-content transport test..."); console.log(`Correlation ID: ${correlation_id}`); // Run sender console.log("start smartsend for mixed content"); test_mix_send(); console.log("\nTest completed."); console.log("Note: Run test_js_to_js_mix_receiver.js to receive the messages.");