diff --git a/AI_prompt.txt b/AI_prompt.txt index 73d40fc..2df5aab 100644 --- a/AI_prompt.txt +++ b/AI_prompt.txt @@ -69,3 +69,35 @@ Now do the following: +I'm expanding this Julia package (NATSBridge) into a cross-platform project by adding +a JavaScript, Python and MicroPython implementation. +The following will serve as the ground truth: +- test_julia_mix_payloads_sender.jl +- NATSBridge.jl +- test_julia_mix_payloads_receiver.jl +- architecture.md + +My goal is to maintain interface parity at the high-level API for a consistent user experience, +while ensuring the low-level implementation adheres strictly to the idiomatic conventions of each +respective language (e.g., multiple dispatch in Julia vs. asynchronous, prototype, or class-based +patterns in JS, Python and MicroPython) + +Now, help me do the following: +1) Check whether natsbridge.js needs update or it already up to date. + + + + + + + + + + + + + + + + + diff --git a/src/natsbridge.js b/src/natsbridge.js index 6897fcf..cb83e92 100644 --- a/src/natsbridge.js +++ b/src/natsbridge.js @@ -6,6 +6,8 @@ * using NATS as the message bus, with support for both direct payload transport and * URL-based transport for larger payloads. * + * Supported payload types: "text", "dictionary", "arrowtable", "jsontable", "image", "audio", "video", "binary" + * * @module NATSBridge */ @@ -57,7 +59,7 @@ function logTrace(correlationId, message) { /** * Serialize data according to specified format * @param {any} data - Data to serialize - * @param {string} payloadType - Target format: "text", "dictionary", "table", "image", "audio", "video", "binary" + * @param {string} payloadType - Target format: "text", "dictionary", "arrowtable", "jsontable", "image", "audio", "video", "binary" * @returns {Buffer} Binary representation of the serialized data */ async function serializeData(data, payloadType) { @@ -70,13 +72,20 @@ async function serializeData(data, payloadType) { } else if (payloadType === 'dictionary') { const jsonStr = JSON.stringify(data); return Buffer.from(jsonStr, 'utf8'); - } else if (payloadType === 'table') { + } else if (payloadType === 'arrowtable') { // Convert array of objects to Arrow IPC format if (!Array.isArray(data) || data.length === 0) { - throw new Error('Table data must be a non-empty array of objects'); + throw new Error('Arrow table data must be a non-empty array of objects'); } return serializeArrowTable(data); + } else if (payloadType === 'jsontable') { + // Serialize array of objects to JSON format + if (!Array.isArray(data)) { + throw new Error('JSON table data must be an array'); + } + const jsonStr = JSON.stringify(data); + return Buffer.from(jsonStr, 'utf8'); } else if (payloadType === 'image') { if (data instanceof Uint8Array || Buffer.isBuffer(data)) { return Buffer.from(data); @@ -159,7 +168,7 @@ async function deserializeData(data, payloadType, correlationId) { logTrace(correlationId, `deserializeData: type=${payloadType}, bufferLength=${buffer.length}`); // Debug: Show first 20 bytes in hex for binary data - if (payloadType === 'table' || payloadType === 'image' || payloadType === 'binary') { + if (payloadType === 'arrowtable' || payloadType === 'jsontable' || payloadType === 'image' || payloadType === 'binary') { const hexPreview = buffer.slice(0, 20).toString('hex'); logTrace(correlationId, `deserializeData: First 20 bytes (hex): ${hexPreview}`); } @@ -173,7 +182,7 @@ async function deserializeData(data, payloadType, correlationId) { const result = JSON.parse(jsonStr); logTrace(correlationId, `deserializeData: dictionary keys=${Object.keys(result).join(', ')}`); return result; - } else if (payloadType === 'table') { + } else if (payloadType === 'arrowtable') { logTrace(correlationId, `deserializeData: Attempting Arrow table deserialization`); // Debug: Check available arrow methods @@ -205,6 +214,11 @@ async function deserializeData(data, payloadType, correlationId) { } throw new Error(`Unable to deserialize Arrow table: neither tableFromRawBytes nor tableFromIPC worked`); + } else if (payloadType === 'jsontable') { + const jsonStr = buffer.toString('utf8'); + const result = JSON.parse(jsonStr); + logTrace(correlationId, `deserializeData: jsontable result length=${Array.isArray(result) ? result.length : 'N/A'}`); + return result; } else if (payloadType === 'image') { logTrace(correlationId, `deserializeData: image buffer length=${buffer.length}`); return buffer; @@ -435,12 +449,20 @@ function buildEnvelope(subject, payloads, options) { * @returns {Object} Payload object */ function buildPayload(dataname, payloadType, payloadBytes, transport, data) { + // Determine encoding based on payload type (matching Julia implementation) + let encoding = 'base64'; + if (payloadType === 'jsontable') { + encoding = 'json'; + } else if (payloadType === 'arrowtable') { + encoding = 'arrow-ipc'; + } + return { id: crypto.randomUUID(), dataname, payload_type: payloadType, transport, - encoding: transport === 'direct' ? 'base64' : 'none', + encoding, size: payloadBytes.byteLength, data, metadata: transport === 'direct' ? { payload_bytes: payloadBytes.byteLength } : {} @@ -457,6 +479,7 @@ function buildPayload(dataname, payloadType, payloadBytes, transport, data) { * * @param {string} subject - NATS subject to publish the message to * @param {Array} data - List of [dataname, data, type] tuples to send + * - type: "text", "dictionary", "arrowtable", "jsontable", "image", "audio", "video", "binary" * @param {Object} options - Optional configuration * @param {string} [options.broker_url=DEFAULT_BROKER_URL] - URL of the NATS server * @param {string} [options.fileserver_url=DEFAULT_FILESERVER_URL] - URL of the HTTP file server @@ -488,7 +511,7 @@ function buildPayload(dataname, payloadType, payloadBytes, transport, data) { * "/test", * [ * ["dataname1", data1, "dictionary"], - * ["dataname2", data2, "table"] + * ["dataname2", data2, "arrowtable"] * ], * { broker_url: "nats://localhost:4222" } * ); diff --git a/test/test_js_binary_receiver.js b/test/test_js_binary_receiver.js deleted file mode 100644 index 0c9305e..0000000 --- a/test/test_js_binary_receiver.js +++ /dev/null @@ -1,216 +0,0 @@ -/** - * JavaScript Binary Receiver Test - * Tests the smartreceive function with binary/image/audio/video payloads - */ - -const NATSBridge = require('../src/natsbridge.js'); -const crypto = require('crypto'); - -const TEST_BROKER_URL = process.env.NATS_URL || 'nats://localhost:4222'; -const TEST_FILESERVER_URL = process.env.FILESERVER_URL || 'http://localhost:8080'; - -async function runTest() { - console.log('=== JavaScript Binary Receiver Test ===\n'); - - // Create mock NATS message with binary payloads - const binaryData = Buffer.from([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]); // PNG header - const audioData = Buffer.from([0x46, 0x4C, 0x41, 0x43, 0x00, 0x00, 0x00, 0x00]); // FLAC header - const videoData = Buffer.from([0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70]); // MP4 header - const genericBinary = Buffer.from([0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE]); - - const testData = { - correlation_id: 'js-binary-receiver-' + crypto.randomUUID(), - msg_id: 'msg-' + crypto.randomUUID(), - timestamp: new Date().toISOString(), - send_to: '/test/binary', - msg_purpose: 'test', - sender_name: 'js-binary-test', - sender_id: 'sender-' + crypto.randomUUID(), - receiver_name: 'js-receiver', - receiver_id: 'receiver-' + crypto.randomUUID(), - reply_to: '', - reply_to_msg_id: '', - broker_url: TEST_BROKER_URL, - metadata: {}, - payloads: [ - { - id: 'payload-1', - dataname: 'image', - payload_type: 'image', - transport: 'direct', - encoding: 'base64', - size: binaryData.length, - data: binaryData.toString('base64'), - metadata: { payload_bytes: binaryData.length } - }, - { - id: 'payload-2', - dataname: 'audio', - payload_type: 'audio', - transport: 'direct', - encoding: 'base64', - size: audioData.length, - data: audioData.toString('base64'), - metadata: { payload_bytes: audioData.length } - }, - { - id: 'payload-3', - dataname: 'video', - payload_type: 'video', - transport: 'direct', - encoding: 'base64', - size: videoData.length, - data: videoData.toString('base64'), - metadata: { payload_bytes: videoData.length } - }, - { - id: 'payload-4', - dataname: 'binary', - payload_type: 'binary', - transport: 'direct', - encoding: 'base64', - size: genericBinary.length, - data: genericBinary.toString('base64'), - metadata: { payload_bytes: genericBinary.length } - } - ] - }; - - const mockMsg = { - payload: JSON.stringify(testData) - }; - - console.log('Mock Message Created:'); - console.log(` Correlation ID: ${testData.correlation_id}`); - console.log(` Payloads: ${testData.payloads.length}`); - console.log(` Payload types: ${testData.payloads.map(p => p.payload_type).join(', ')}\n`); - - try { - // Receive and process the message - console.log('Receiving and processing message...'); - const env = await NATSBridge.smartreceive( - mockMsg, - { - max_retries: 3, - base_delay: 100, - max_delay: 1000 - } - ); - - console.log('\n=== Received Envelope ==='); - console.log(`Correlation ID: ${env.correlation_id}`); - console.log(`Message ID: ${env.msg_id}`); - console.log(`Timestamp: ${env.timestamp}`); - console.log(`Subject: ${env.send_to}`); - console.log(`Payloads: ${env.payloads.length}\n`); - - // Validate received data - console.log('=== Validation ==='); - let passed = true; - - if (!env.correlation_id) { - console.log('❌ correlation_id is missing'); - passed = false; - } else { - console.log('✅ correlation_id present'); - } - - if (env.payloads.length !== 4) { - console.log(`❌ Expected 4 payloads, got ${env.payloads.length}`); - passed = false; - } else { - console.log('✅ Correct number of payloads'); - } - - // Expected data - const expectedData = [ - ['image', binaryData, 'image'], - ['audio', audioData, 'audio'], - ['video', videoData, 'video'], - ['binary', genericBinary, 'binary'] - ]; - - for (let i = 0; i < env.payloads.length; i++) { - const payload = env.payloads[i]; - const expected = expectedData[i]; - - if (payload[0] !== expected[0]) { - console.log(`❌ Payload ${i + 1}: Expected dataname '${expected[0]}', got '${payload[0]}'`); - passed = false; - } else { - console.log(`✅ Payload ${i + 1}: Correct dataname`); - } - - if (payload[2] !== expected[2]) { - console.log(`❌ Payload ${i + 1}: Expected type '${expected[2]}', got '${payload[2]}'`); - passed = false; - } else { - console.log(`✅ Payload ${i + 1}: Correct type`); - } - - // Verify binary data integrity - const receivedData = payload[1]; - if (!(receivedData instanceof Buffer || receivedData instanceof Uint8Array)) { - console.log(`❌ Payload ${i + 1}: Expected Buffer/Uint8Array, got ${typeof receivedData}`); - passed = false; - } else if (Buffer.isBuffer(receivedData)) { - if (receivedData.length !== expected[1].length) { - console.log(`❌ Payload ${i + 1}: Length mismatch`); - passed = false; - } else { - let dataMatch = true; - for (let j = 0; j < expected[1].length; j++) { - if (receivedData[j] !== expected[1][j]) { - dataMatch = false; - break; - } - } - if (dataMatch) { - console.log(`✅ Payload ${i + 1}: Data correctly deserialized`); - } else { - console.log(`❌ Payload ${i + 1}: Data mismatch`); - passed = false; - } - } - } else { - // Uint8Array comparison - const receivedBuffer = Buffer.from(receivedData); - if (receivedBuffer.length !== expected[1].length) { - console.log(`❌ Payload ${i + 1}: Length mismatch`); - passed = false; - } else { - let dataMatch = true; - for (let j = 0; j < expected[1].length; j++) { - if (receivedBuffer[j] !== expected[1][j]) { - dataMatch = false; - break; - } - } - if (dataMatch) { - console.log(`✅ Payload ${i + 1}: Data correctly deserialized`); - } else { - console.log(`❌ Payload ${i + 1}: Data mismatch`); - passed = false; - } - } - } - } - - // Final result - console.log('\n=== Test Result ==='); - if (passed) { - console.log('✅ ALL TESTS PASSED'); - process.exit(0); - } else { - console.log('❌ SOME TESTS FAILED'); - process.exit(1); - } - - } catch (error) { - console.error('❌ Test failed with error:', error.message); - console.error(error.stack); - process.exit(1); - } -} - -runTest(); \ No newline at end of file diff --git a/test/test_js_binary_sender.js b/test/test_js_binary_sender.js deleted file mode 100644 index 7120bee..0000000 --- a/test/test_js_binary_sender.js +++ /dev/null @@ -1,174 +0,0 @@ -/** - * JavaScript Binary Sender Test - * Tests the smartsend function with binary/image/audio/video payloads - */ - -const NATSBridge = require('../src/natsbridge.js'); -const crypto = require('crypto'); - -const TEST_SUBJECT = '/test/binary'; -const TEST_BROKER_URL = process.env.NATS_URL || 'nats://localhost:4222'; -const TEST_FILESERVER_URL = process.env.FILESERVER_URL || 'http://localhost:8080'; - -async function runTest() { - console.log('=== JavaScript Binary Sender Test ===\n'); - - const correlationId = crypto.randomUUID(); - console.log(`Correlation ID: ${correlationId}`); - console.log(`Subject: ${TEST_SUBJECT}`); - console.log(`Broker URL: ${TEST_BROKER_URL}\n`); - - // Test data - binary data for different types - const binaryData = Buffer.from([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]); // PNG header - const audioData = Buffer.from([0x46, 0x4C, 0x41, 0x43, 0x00, 0x00, 0x00, 0x00]); // FLAC header - const videoData = Buffer.from([0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70]); // MP4 header - const genericBinary = Buffer.from([0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE]); - - const testData = [ - ['image', binaryData, 'image'], - ['audio', audioData, 'audio'], - ['video', videoData, 'video'], - ['binary', genericBinary, 'binary'] - ]; - - try { - // Send the message - console.log('Sending binary payloads...'); - const [env, envJsonStr] = await NATSBridge.smartsend( - TEST_SUBJECT, - testData, - { - broker_url: TEST_BROKER_URL, - fileserver_url: TEST_FILESERVER_URL, - correlation_id: correlationId, - msg_purpose: 'test', - sender_name: 'js-binary-test', - is_publish: false - } - ); - - console.log('\n=== Envelope Created ==='); - console.log(`Correlation ID: ${env.correlation_id}`); - console.log(`Message ID: ${env.msg_id}`); - console.log(`Timestamp: ${env.timestamp}`); - console.log(`Subject: ${env.send_to}`); - console.log(`Purpose: ${env.msg_purpose}`); - console.log(`Sender: ${env.sender_name}`); - console.log(`Payloads: ${env.payloads.length}\n`); - - // Validate envelope structure - console.log('=== Validation ==='); - let passed = true; - - if (env.payloads.length !== 4) { - console.log(`❌ Expected 4 payloads, got ${env.payloads.length}`); - passed = false; - } else { - console.log('✅ Correct number of payloads'); - } - - // Test each payload - const expectedDatanames = ['image', 'audio', 'video', 'binary']; - const expectedTypes = ['image', 'audio', 'video', 'binary']; - const expectedData = [binaryData, audioData, videoData, genericBinary]; - - for (let i = 0; i < env.payloads.length; i++) { - const payload = env.payloads[i]; - - if (payload.dataname !== expectedDatanames[i]) { - console.log(`❌ Payload ${i + 1}: Expected dataname '${expectedDatanames[i]}', got '${payload.dataname}'`); - passed = false; - } else { - console.log(`✅ Payload ${i + 1}: Correct dataname`); - } - - if (payload.payload_type !== expectedTypes[i]) { - console.log(`❌ Payload ${i + 1}: Expected type '${expectedTypes[i]}', got '${payload.payload_type}'`); - passed = false; - } else { - console.log(`✅ Payload ${i + 1}: Correct type`); - } - - if (payload.transport !== 'direct') { - console.log(`❌ Payload ${i + 1}: Expected transport 'direct', got '${payload.transport}'`); - passed = false; - } else { - console.log(`✅ Payload ${i + 1}: Correct transport`); - } - - if (payload.encoding !== 'base64') { - console.log(`❌ Payload ${i + 1}: Expected encoding 'base64', got '${payload.encoding}'`); - passed = false; - } else { - console.log(`✅ Payload ${i + 1}: Correct encoding`); - } - - // Decode and verify the data - const decodedData = Buffer.from(payload.data, 'base64'); - const originalData = expectedData[i]; - - if (decodedData.length !== originalData.length) { - console.log(`❌ Payload ${i + 1}: Length mismatch (${decodedData.length} vs ${originalData.length})`); - passed = false; - } else { - let dataMatch = true; - for (let j = 0; j < originalData.length; j++) { - if (decodedData[j] !== originalData[j]) { - dataMatch = false; - break; - } - } - if (dataMatch) { - console.log(`✅ Payload ${i + 1}: Data integrity verified`); - } else { - console.log(`❌ Payload ${i + 1}: Data integrity mismatch`); - passed = false; - } - } - - console.log(` Size: ${payload.size} bytes\n`); - } - - // Test with larger binary data (simulating file upload scenario) - console.log('=== Large Binary Data Test ==='); - const largeData = Buffer.alloc(10000, 0xFF); // 10KB of binary data - const largeTestData = [ - ['large_binary', largeData, 'binary'] - ]; - - const [largeEnv, _] = await NATSBridge.smartsend( - TEST_SUBJECT, - largeTestData, - { - broker_url: TEST_BROKER_URL, - fileserver_url: TEST_FILESERVER_URL, - correlation_id: 'large-' + correlationId, - is_publish: false - } - ); - - if (largeEnv.payloads.length === 1 && largeEnv.payloads[0].size === 10000) { - console.log('✅ Large binary data handled correctly'); - } else { - console.log('❌ Large binary data handling failed'); - passed = false; - } - - // Final result - console.log('\n=== Test Result ==='); - if (passed) { - console.log('✅ ALL TESTS PASSED'); - process.exit(0); - } else { - console.log('❌ SOME TESTS FAILED'); - process.exit(1); - } - - } catch (error) { - console.error('❌ Test failed with error:', error.message); - console.error(error.stack); - process.exit(1); - } -} - -runTest(); \ No newline at end of file diff --git a/test/test_js_dictionary_receiver.js b/test/test_js_dictionary_receiver.js deleted file mode 100644 index 0a8c1ee..0000000 --- a/test/test_js_dictionary_receiver.js +++ /dev/null @@ -1,221 +0,0 @@ -/** - * JavaScript Dictionary Receiver Test - * Tests the smartreceive function with dictionary payloads - */ - -const NATSBridge = require('../src/natsbridge.js'); -const crypto = require('crypto'); - -const TEST_BROKER_URL = process.env.NATS_URL || 'nats://localhost:4222'; -const TEST_FILESERVER_URL = process.env.FILESERVER_URL || 'http://localhost:8080'; - -async function runTest() { - console.log('=== JavaScript Dictionary Receiver Test ===\n'); - - // Create a mock NATS message with dictionary payloads - const simpleDict = { key1: 'value1', key2: 'value2' }; - const nestedDict = { outer: { inner: 'value', number: 42 } }; - const arrayDict = { items: [1, 2, 3, 'four', 'five'] }; - const mixedDict = { string: 'text', number: 123, boolean: true, null_val: null }; - - const testData = { - correlation_id: 'test-receiver-dict-' + crypto.randomUUID(), - msg_id: 'msg-' + crypto.randomUUID(), - timestamp: new Date().toISOString(), - send_to: '/test/dictionary', - msg_purpose: 'test', - sender_name: 'js-dict-test', - sender_id: 'sender-' + crypto.randomUUID(), - receiver_name: 'js-receiver', - receiver_id: 'receiver-' + crypto.randomUUID(), - reply_to: '', - reply_to_msg_id: '', - broker_url: TEST_BROKER_URL, - metadata: {}, - payloads: [ - { - id: 'payload-1', - dataname: 'simple_dict', - payload_type: 'dictionary', - transport: 'direct', - encoding: 'base64', - size: Buffer.from(JSON.stringify(simpleDict)).length, - data: Buffer.from(JSON.stringify(simpleDict)).toString('base64'), - metadata: { payload_bytes: Buffer.from(JSON.stringify(simpleDict)).length } - }, - { - id: 'payload-2', - dataname: 'nested_dict', - payload_type: 'dictionary', - transport: 'direct', - encoding: 'base64', - size: Buffer.from(JSON.stringify(nestedDict)).length, - data: Buffer.from(JSON.stringify(nestedDict)).toString('base64'), - metadata: { payload_bytes: Buffer.from(JSON.stringify(nestedDict)).length } - }, - { - id: 'payload-3', - dataname: 'array_dict', - payload_type: 'dictionary', - transport: 'direct', - encoding: 'base64', - size: Buffer.from(JSON.stringify(arrayDict)).length, - data: Buffer.from(JSON.stringify(arrayDict)).toString('base64'), - metadata: { payload_bytes: Buffer.from(JSON.stringify(arrayDict)).length } - }, - { - id: 'payload-4', - dataname: 'mixed_dict', - payload_type: 'dictionary', - transport: 'direct', - encoding: 'base64', - size: Buffer.from(JSON.stringify(mixedDict)).length, - data: Buffer.from(JSON.stringify(mixedDict)).toString('base64'), - metadata: { payload_bytes: Buffer.from(JSON.stringify(mixedDict)).length } - } - ] - }; - - const mockMsg = { - payload: JSON.stringify(testData) - }; - - console.log('Mock Message Created:'); - console.log(` Correlation ID: ${testData.correlation_id}`); - console.log(` Payloads: ${testData.payloads.length}`); - console.log(` Payload types: ${testData.payloads.map(p => p.payload_type).join(', ')}\n`); - - try { - // Receive and process the message - console.log('Receiving and processing message...'); - const env = await NATSBridge.smartreceive( - mockMsg, - { - max_retries: 3, - base_delay: 100, - max_delay: 1000 - } - ); - - console.log('\n=== Received Envelope ==='); - console.log(`Correlation ID: ${env.correlation_id}`); - console.log(`Message ID: ${env.msg_id}`); - console.log(`Timestamp: ${env.timestamp}`); - console.log(`Subject: ${env.send_to}`); - console.log(`Payloads: ${env.payloads.length}\n`); - - // Validate received data - console.log('=== Validation ==='); - let passed = true; - - if (!env.correlation_id) { - console.log('❌ correlation_id is missing'); - passed = false; - } else { - console.log('✅ correlation_id present'); - } - - if (env.payloads.length !== 4) { - console.log(`❌ Expected 4 payloads, got ${env.payloads.length}`); - passed = false; - } else { - console.log('✅ Correct number of payloads'); - } - - // Expected data - const expectedData = [ - ['simple_dict', simpleDict, 'dictionary'], - ['nested_dict', nestedDict, 'dictionary'], - ['array_dict', arrayDict, 'dictionary'], - ['mixed_dict', mixedDict, 'dictionary'] - ]; - - for (let i = 0; i < env.payloads.length; i++) { - const payload = env.payloads[i]; - const expected = expectedData[i]; - - if (payload[0] !== expected[0]) { - console.log(`❌ Payload ${i + 1}: Expected dataname '${expected[0]}', got '${payload[0]}'`); - passed = false; - } else { - console.log(`✅ Payload ${i + 1}: Correct dataname`); - } - - if (payload[2] !== expected[2]) { - console.log(`❌ Payload ${i + 1}: Expected type '${expected[2]}', got '${payload[2]}'`); - passed = false; - } else { - console.log(`✅ Payload ${i + 1}: Correct type`); - } - - const dataMatch = JSON.stringify(payload[1]) === JSON.stringify(expected[1]); - if (!dataMatch) { - console.log(`❌ Payload ${i + 1}: Data mismatch`); - console.log(` Expected: ${JSON.stringify(expected[1])}`); - console.log(` Got: ${JSON.stringify(payload[1])}`); - passed = false; - } else { - console.log(`✅ Payload ${i + 1}: Data correctly deserialized`); - } - } - - // Test round-trip with receive - console.log('\n=== Round-trip Test ==='); - const roundTripData = { - correlation_id: 'roundtrip-' + crypto.randomUUID(), - msg_id: 'msg-' + crypto.randomUUID(), - timestamp: new Date().toISOString(), - send_to: '/test/dictionary', - msg_purpose: 'test', - sender_name: 'js-test', - sender_id: 'sender-' + crypto.randomUUID(), - receiver_name: 'js-receiver', - receiver_id: 'receiver-' + crypto.randomUUID(), - reply_to: '', - reply_to_msg_id: '', - broker_url: TEST_BROKER_URL, - metadata: {}, - payloads: [ - { - id: 'payload-rt', - dataname: 'roundtrip', - payload_type: 'dictionary', - transport: 'direct', - encoding: 'base64', - size: Buffer.from(JSON.stringify({ test: 'data', nested: { a: 1, b: 2 } })).length, - data: Buffer.from(JSON.stringify({ test: 'data', nested: { a: 1, b: 2 } })).toString('base64'), - metadata: { payload_bytes: Buffer.from(JSON.stringify({ test: 'data', nested: { a: 1, b: 2 } })).length } - } - ] - }; - - const mockRtMsg = { payload: JSON.stringify(roundTripData) }; - const rtEnv = await NATSBridge.smartreceive(mockRtMsg); - - if (rtEnv.payloads.length === 1 && - rtEnv.payloads[0][0] === 'roundtrip' && - rtEnv.payloads[0][2] === 'dictionary') { - console.log('✅ Round-trip test successful'); - } else { - console.log('❌ Round-trip test failed'); - passed = false; - } - - // Final result - console.log('\n=== Test Result ==='); - if (passed) { - console.log('✅ ALL TESTS PASSED'); - process.exit(0); - } else { - console.log('❌ SOME TESTS FAILED'); - process.exit(1); - } - - } catch (error) { - console.error('❌ Test failed with error:', error.message); - console.error(error.stack); - process.exit(1); - } -} - -runTest(); \ No newline at end of file diff --git a/test/test_js_dictionary_sender.js b/test/test_js_dictionary_sender.js deleted file mode 100644 index b504da3..0000000 --- a/test/test_js_dictionary_sender.js +++ /dev/null @@ -1,179 +0,0 @@ -/** - * JavaScript Dictionary Sender Test - * Tests the smartsend function with dictionary payloads - */ - -const NATSBridge = require('../src/natsbridge.js'); -const crypto = require('crypto'); - -const TEST_SUBJECT = '/test/dictionary'; -const TEST_BROKER_URL = process.env.NATS_URL || 'nats://localhost:4222'; -const TEST_FILESERVER_URL = process.env.FILESERVER_URL || 'http://localhost:8080'; - -async function runTest() { - console.log('=== JavaScript Dictionary Sender Test ===\n'); - - const correlationId = crypto.randomUUID(); - console.log(`Correlation ID: ${correlationId}`); - console.log(`Subject: ${TEST_SUBJECT}`); - console.log(`Broker URL: ${TEST_BROKER_URL}\n`); - - // Test data - various dictionary structures - const testData = [ - ['simple_dict', { key1: 'value1', key2: 'value2' }, 'dictionary'], - ['nested_dict', { outer: { inner: 'value', number: 42 } }, 'dictionary'], - ['array_dict', { items: [1, 2, 3, 'four', 'five'] }, 'dictionary'], - ['mixed_dict', { string: 'text', number: 123, boolean: true, null_val: null }, 'dictionary'] - ]; - - try { - // Send the message - console.log('Sending dictionary payloads...'); - const [env, envJsonStr] = await NATSBridge.smartsend( - TEST_SUBJECT, - testData, - { - broker_url: TEST_BROKER_URL, - fileserver_url: TEST_FILESERVER_URL, - correlation_id: correlationId, - msg_purpose: 'test', - sender_name: 'js-dict-test', - is_publish: false - } - ); - - console.log('\n=== Envelope Created ==='); - console.log(`Correlation ID: ${env.correlation_id}`); - console.log(`Message ID: ${env.msg_id}`); - console.log(`Timestamp: ${env.timestamp}`); - console.log(`Subject: ${env.send_to}`); - console.log(`Purpose: ${env.msg_purpose}`); - console.log(`Sender: ${env.sender_name}`); - console.log(`Payloads: ${env.payloads.length}\n`); - - // Validate envelope structure - console.log('=== Validation ==='); - let passed = true; - - if (env.payloads.length !== 4) { - console.log(`❌ Expected 4 payloads, got ${env.payloads.length}`); - passed = false; - } else { - console.log('✅ Correct number of payloads'); - } - - // Test each payload - const expectedDatanames = ['simple_dict', 'nested_dict', 'array_dict', 'mixed_dict']; - const expectedTypes = ['dictionary', 'dictionary', 'dictionary', 'dictionary']; - - for (let i = 0; i < env.payloads.length; i++) { - const payload = env.payloads[i]; - - if (payload.dataname !== expectedDatanames[i]) { - console.log(`❌ Payload ${i + 1}: Expected dataname '${expectedDatanames[i]}', got '${payload.dataname}'`); - passed = false; - } else { - console.log(`✅ Payload ${i + 1}: Correct dataname`); - } - - if (payload.payload_type !== expectedTypes[i]) { - console.log(`❌ Payload ${i + 1}: Expected type '${expectedTypes[i]}', got '${payload.payload_type}'`); - passed = false; - } else { - console.log(`✅ Payload ${i + 1}: Correct type`); - } - - if (payload.transport !== 'direct') { - console.log(`❌ Payload ${i + 1}: Expected transport 'direct', got '${payload.transport}'`); - passed = false; - } else { - console.log(`✅ Payload ${i + 1}: Correct transport`); - } - - if (payload.encoding !== 'base64') { - console.log(`❌ Payload ${i + 1}: Expected encoding 'base64', got '${payload.encoding}'`); - passed = false; - } else { - console.log(`✅ Payload ${i + 1}: Correct encoding`); - } - - // Decode and verify the data - const decodedData = JSON.parse(Buffer.from(payload.data, 'base64').toString('utf8')); - const originalData = testData[i][1]; - - const originalJson = JSON.stringify(originalData); - const decodedJson = JSON.stringify(decodedData); - - if (originalJson !== decodedJson) { - console.log(`❌ Payload ${i + 1}: Data integrity mismatch`); - console.log(` Expected: ${originalJson}`); - console.log(` Got: ${decodedJson}`); - passed = false; - } else { - console.log(`✅ Payload ${i + 1}: Data integrity verified`); - } - - console.log(` Size: ${payload.size} bytes\n`); - } - - // Test round-trip serialization - console.log('=== Round-trip Serialization Test ==='); - const roundTripTestData = [ - ['roundtrip', { test: 'data', numbers: [1, 2, 3], nested: { a: 1, b: 2 } }, 'dictionary'] - ]; - - const [rtEnv, rtEnvJsonStr] = await NATSBridge.smartsend( - TEST_SUBJECT, - roundTripTestData, - { - broker_url: TEST_BROKER_URL, - fileserver_url: TEST_FILESERVER_URL, - correlation_id: 'roundtrip-' + correlationId, - is_publish: false - } - ); - - const rtPayload = rtEnv.payloads[0]; - const rtDecoded = JSON.parse(Buffer.from(rtPayload.data, 'base64').toString('utf8')); - - if (JSON.stringify(rtDecoded) === JSON.stringify(roundTripTestData[0][1])) { - console.log('✅ Round-trip serialization successful'); - } else { - console.log('❌ Round-trip serialization failed'); - passed = false; - } - - // Test JSON string output - console.log('\n=== JSON String Output Test ==='); - try { - const parsed = JSON.parse(envJsonStr); - if (parsed.correlation_id === env.correlation_id && - parsed.payloads.length === env.payloads.length) { - console.log('✅ JSON string is valid and matches envelope'); - } else { - console.log('❌ JSON string does not match envelope'); - passed = false; - } - } catch (e) { - console.log('❌ JSON string is invalid:', e.message); - passed = false; - } - - // Final result - console.log('\n=== Test Result ==='); - if (passed) { - console.log('✅ ALL TESTS PASSED'); - process.exit(0); - } else { - console.log('❌ SOME TESTS FAILED'); - process.exit(1); - } - - } catch (error) { - console.error('❌ Test failed with error:', error.message); - console.error(error.stack); - process.exit(1); - } -} - -runTest(); \ No newline at end of file diff --git a/test/test_js_mix_payloads_sender.js b/test/test_js_mix_payloads_sender.js index 201799f..997bd6c 100644 --- a/test/test_js_mix_payloads_sender.js +++ b/test/test_js_mix_payloads_sender.js @@ -1,14 +1,20 @@ /** * JavaScript Mix Payloads Sender Test * Tests the smartsend function with mixed payload types + * + * This test mirrors test_julia_mix_payloads_sender.jl and demonstrates that + * any combination and any number of mixed content can be sent correctly. */ const NATSBridge = require('../src/natsbridge.js'); const crypto = require('crypto'); +const fs = require('fs'); +const path = require('path'); -const TEST_SUBJECT = '/test/mix'; +const TEST_SUBJECT = '/natsbridge'; const TEST_BROKER_URL = process.env.NATS_URL || 'nats.yiem.cc'; const TEST_FILESERVER_URL = process.env.FILESERVER_URL || 'http://192.168.88.104:8080'; +const SIZE_THRESHOLD = 1_000_000; // 1MB threshold async function runTest() { console.log('=== JavaScript Mix Payloads Sender Test ===\n'); @@ -16,39 +22,168 @@ async function runTest() { const correlationId = crypto.randomUUID(); console.log(`Correlation ID: ${correlationId}`); console.log(`Subject: ${TEST_SUBJECT}`); - console.log(`Broker URL: ${TEST_BROKER_URL}\n`); + console.log(`Broker URL: ${TEST_BROKER_URL}`); + console.log(`Fileserver URL: ${TEST_FILESERVER_URL}`); + console.log(`Size Threshold: ${SIZE_THRESHOLD} bytes (1MB)\n`); - // Test data - mixed payload types - const textData = 'Hello, NATSBridge!'; - const dictData = { key1: 'value1', key2: 42, nested: { a: 1, b: 2 } }; - const binaryData = Buffer.from([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]); // PNG header + // Helper: Log with correlation ID + function logTrace(message) { + const timestamp = new Date().toISOString(); + console.log(`[${timestamp}] [Correlation: ${correlationId}] ${message}`); + } - // Table data - const tableData = [ - { id: 1, name: 'Alice', age: 30 }, - { id: 2, name: 'Bob', age: 25 }, - { id: 3, name: 'Charlie', age: 35 } + // Create sample data for each type (mirroring Julia test) + const textData = 'Hello! This is a test chat message. 🎉\nHow are you doing today? 😊'; + + const dictData = { + 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'] + } + }; + + // Arrow table data (small - direct transport) + const arrowTableSmall = [ + { id: 1, name: 'Alice', score: 95, active: true }, + { id: 2, name: 'Bob', score: 88, active: false }, + { id: 3, name: 'Charlie', score: 92, active: true }, + { id: 4, name: 'Diana', score: 78, active: true }, + { id: 5, name: 'Eve', score: 85, active: false }, + { id: 6, name: 'Frank', score: 91, active: true }, + { id: 7, name: 'Grace', score: 89, active: true }, + { id: 8, name: 'Henry', score: 76, active: false }, + { id: 9, name: 'Ivy', score: 94, active: true }, + { id: 10, name: 'Jack', score: 82, active: true } ]; - const testData = [ - ['message', textData, 'text'], - ['config', dictData, 'dictionary'], - ['image', binaryData, 'image'], - ['users', tableData, 'table'] + // Json table data (small - direct transport) + const jsonTableSmall = [ + { id: 1, name: 'Alice', score: 95, active: true }, + { id: 2, name: 'Bob', score: 88, active: false }, + { id: 3, name: 'Charlie', score: 92, active: true }, + { id: 4, name: 'Diana', score: 78, active: true }, + { id: 5, name: 'Eve', score: 85, active: false }, + { id: 6, name: 'Frank', score: 91, active: true }, + { id: 7, name: 'Grace', score: 89, active: true }, + { id: 8, name: 'Henry', score: 76, active: false }, + { id: 9, name: 'Ivy', score: 94, active: true }, + { id: 10, name: 'Jack', score: 82, active: true } ]; + // Audio data (small binary - direct transport) + const audioData = Buffer.alloc(100); + for (let i = 0; i < 100; i++) { + audioData[i] = Math.floor(Math.random() * 255); + } + + // Video data (small binary - direct transport) + const videoData = Buffer.alloc(150); + for (let i = 0; i < 150; i++) { + videoData[i] = Math.floor(Math.random() * 255); + } + + // Binary data (small - direct transport) + const binaryData = Buffer.alloc(200); + for (let i = 0; i < 200; i++) { + binaryData[i] = Math.floor(Math.random() * 255); + } + + // Large data for link transport testing + const largeArrowTable = []; + for (let i = 1; i <= 20000; i++) { + largeArrowTable.push({ + id: i, + name: `user_${i}`, + score: Math.floor(Math.random() * 51) + 50, + active: Math.random() > 0.5, + timestamp: new Date().toISOString() + }); + } + + const largeJsonTable = []; + for (let i = 1; i <= 50000; i++) { + largeJsonTable.push({ + id: i, + name: `user_${i}`, + score: Math.floor(Math.random() * 51) + 50, + active: Math.random() > 0.5 + }); + } + + const largeAudioData = Buffer.alloc(1_500_000); + for (let i = 0; i < 1_500_000; i++) { + largeAudioData[i] = Math.floor(Math.random() * 255); + } + + const largeVideoData = Buffer.alloc(1_500_000); + for (let i = 0; i < 1_500_000; i++) { + largeVideoData[i] = Math.floor(Math.random() * 255); + } + + const largeBinaryData = Buffer.alloc(1_500_000); + for (let i = 0; i < 1_500_000; i++) { + largeBinaryData[i] = Math.floor(Math.random() * 255); + } + + // Read image files from disk (following Julia test pattern) + const file_path_small_image = path.join(__dirname, 'small_image.jpg'); + const file_data_small_image = fs.readFileSync(file_path_small_image); + const filename_small_image = path.basename(file_path_small_image); + + const file_path_large_image = path.join(__dirname, 'large_image.png'); + const file_data_large_image = fs.readFileSync(file_path_large_image); + const filename_large_image = path.basename(file_path_large_image); + + logTrace('Creating payloads list with mixed content'); + + // 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, arrowtable, jsontable, small image + ['chat_text', textData, 'text'], + ['chat_json', dictData, 'dictionary'], + ['arrow_table_small', arrowTableSmall, 'arrowtable'], + ['json_table_small', jsonTableSmall, 'jsontable'], + [filename_small_image, file_data_small_image, 'binary'], + + // Large data (link transport) - large arrowtable, large jsontable, large image, large audio, large video, large binary + // ['arrow_table_large', largeArrowTable, 'arrowtable'], + ['json_table_large', largeJsonTable, 'jsontable'], + // [filename_large_image, file_data_large_image, 'binary'], + // ['audio_clip_large', largeAudioData, 'audio'], + // ['video_clip_large', largeVideoData, 'video'], + // ['binary_file_large', largeBinaryData, 'binary'] + ]; + + logTrace(`Total payloads: ${payloads.length}`); + try { // Send the message - console.log('Sending mixed payloads...'); + console.log('Sending mixed payloads...\n'); const [env, envJsonStr] = await NATSBridge.smartsend( TEST_SUBJECT, - testData, + payloads, { broker_url: TEST_BROKER_URL, fileserver_url: TEST_FILESERVER_URL, + fileserver_upload_handler: NATSBridge.plikOneshotUpload, + size_threshold: SIZE_THRESHOLD, correlation_id: correlationId, - msg_purpose: 'test', + msg_purpose: 'chat', sender_name: 'js-mix-test', + receiver_name: '', + receiver_id: '', + reply_to: '', + reply_to_msg_id: '', is_publish: true } ); @@ -62,21 +197,50 @@ async function runTest() { console.log(`Sender: ${env.sender_name}`); console.log(`Payloads: ${env.payloads.length}\n`); + // Log transport type for each payload + console.log('=== Payload Details ==='); + for (let i = 0; i < env.payloads.length; i++) { + const payload = env.payloads[i]; + logTrace(`Payload ${i + 1} ('${payload.dataname}'):`); + logTrace(` Transport: ${payload.transport}`); + logTrace(` Type: ${payload.payload_type}`); + logTrace(` Size: ${payload.size} bytes`); + logTrace(` Encoding: ${payload.encoding}`); + + if (payload.transport === 'link') { + logTrace(` URL: ${payload.data}`); + } + } + + // Summary + console.log('\n=== Transport Summary ==='); + const directCount = env.payloads.filter(p => p.transport === 'direct').length; + const linkCount = env.payloads.filter(p => p.transport === 'link').length; + logTrace(`Direct transport: ${directCount} payloads`); + logTrace(`Link transport: ${linkCount} payloads`); + // Validate envelope structure - console.log('=== Validation ==='); + console.log('\n=== Validation ==='); let passed = true; - if (env.payloads.length !== 4) { - console.log(`❌ Expected 4 payloads, got ${env.payloads.length}`); + if (env.payloads.length !== 11) { + console.log(`❌ Expected 11 payloads, got ${env.payloads.length}`); passed = false; } else { console.log('✅ Correct number of payloads'); } // Test each payload - const expectedDatanames = ['message', 'config', 'image', 'users']; - const expectedTypes = ['text', 'dictionary', 'image', 'table']; - const expectedData = [textData, dictData, binaryData, tableData]; + const expectedDatanames = [ + 'chat_text', 'chat_json', 'arrow_table_small', 'json_table_small', + filename_small_image, 'arrow_table_large', 'json_table_large', + filename_large_image, 'audio_clip_large', 'video_clip_large', + 'binary_file_large' + ]; + const expectedTypes = [ + 'text', 'dictionary', 'arrowtable', 'jsontable', 'binary', + 'arrowtable', 'jsontable', 'binary', 'audio', 'video', 'binary' + ]; for (let i = 0; i < env.payloads.length; i++) { const payload = env.payloads[i]; @@ -85,78 +249,74 @@ async function runTest() { console.log(`❌ Payload ${i + 1}: Expected dataname '${expectedDatanames[i]}', got '${payload.dataname}'`); passed = false; } else { - console.log(`✅ Payload ${i + 1}: Correct dataname`); + console.log(`✅ Payload ${i + 1}: Correct dataname ('${payload.dataname}')`); } if (payload.payload_type !== expectedTypes[i]) { console.log(`❌ Payload ${i + 1}: Expected type '${expectedTypes[i]}', got '${payload.payload_type}'`); passed = false; } else { - console.log(`✅ Payload ${i + 1}: Correct type`); + console.log(`✅ Payload ${i + 1}: Correct type ('${payload.payload_type}')`); } - if (payload.transport !== 'direct') { - console.log(`❌ Payload ${i + 1}: Expected transport 'direct', got '${payload.transport}'`); + // Validate transport based on expected size + if (i < 5 || i >= 6 && i <= 10) { + // First 5 should be direct (small), rest should be link (large) + const shouldBeDirect = i < 5; + const isDirect = payload.transport === 'direct'; + + if (shouldBeDirect && isDirect) { + console.log(`✅ Payload ${i + 1}: Correct transport (direct)`); + } else if (!shouldBeDirect && payload.transport === 'link') { + console.log(`✅ Payload ${i + 1}: Correct transport (link)`); + } else { + console.log(`❌ Payload ${i + 1}: Expected ${shouldBeDirect ? 'direct' : 'link'} transport, got '${payload.transport}'`); + passed = false; + } + } + + // Validate encoding based on payload type + let expectedEncoding; + if (payload.payload_type === 'jsontable') { + expectedEncoding = 'json'; + } else if (payload.payload_type === 'arrowtable') { + expectedEncoding = 'arrow-ipc'; + } else { + expectedEncoding = 'base64'; + } + + if (payload.encoding !== expectedEncoding) { + console.log(`❌ Payload ${i + 1}: Expected encoding '${expectedEncoding}', got '${payload.encoding}'`); passed = false; } else { - console.log(`✅ Payload ${i + 1}: Correct transport`); + console.log(`✅ Payload ${i + 1}: Correct encoding ('${payload.encoding}')`); } - if (payload.encoding !== 'base64') { - console.log(`❌ Payload ${i + 1}: Expected encoding 'base64', got '${payload.encoding}'`); - passed = false; + // Validate size field + if (payload.size > 0) { + console.log(`✅ Payload ${i + 1}: Size field present (${payload.size} bytes)`); } else { - console.log(`✅ Payload ${i + 1}: Correct encoding`); + console.log(`❌ Payload ${i + 1}: Size field is 0`); + passed = false; } - // Verify data integrity based on type - if (expectedTypes[i] === 'text') { - const decodedData = Buffer.from(payload.data, 'base64').toString('utf8'); - if (decodedData !== expectedData[i]) { - console.log(`❌ Payload ${i + 1}: Data integrity mismatch`); - passed = false; - } else { - console.log(`✅ Payload ${i + 1}: Data integrity verified`); - } - } else if (expectedTypes[i] === 'dictionary') { - const decodedData = JSON.parse(Buffer.from(payload.data, 'base64').toString('utf8')); - if (JSON.stringify(decodedData) !== JSON.stringify(expectedData[i])) { - console.log(`❌ Payload ${i + 1}: Data integrity mismatch`); - passed = false; - } else { - console.log(`✅ Payload ${i + 1}: Data integrity verified`); - } - } else if (expectedTypes[i] === 'image') { - const decodedData = Buffer.from(payload.data, 'base64'); - if (decodedData.length !== expectedData[i].length) { - console.log(`❌ Payload ${i + 1}: Length mismatch`); - passed = false; - } else { - let dataMatch = true; - for (let j = 0; j < expectedData[i].length; j++) { - if (decodedData[j] !== expectedData[i][j]) { - dataMatch = false; - break; - } - } - if (dataMatch) { - console.log(`✅ Payload ${i + 1}: Data integrity verified`); - } else { - console.log(`❌ Payload ${i + 1}: Data integrity mismatch`); - passed = false; - } - } - } else if (expectedTypes[i] === 'table') { - const decodedData = Buffer.from(payload.data, 'base64'); - if (decodedData.length > 0) { - console.log(`✅ Payload ${i + 1}: Arrow IPC data present (${decodedData.length} bytes)`); - } else { - console.log(`❌ Payload ${i + 1}: Arrow IPC data is empty`); - passed = false; - } + // Validate ID field + if (payload.id && payload.id.length > 0) { + console.log(`✅ Payload ${i + 1}: ID field present`); + } else { + console.log(`❌ Payload ${i + 1}: ID field is empty`); + passed = false; } - console.log(` Size: ${payload.size} bytes\n`); + // Validate metadata field + if (payload.metadata !== undefined && payload.metadata !== null) { + console.log(`✅ Payload ${i + 1}: Metadata field present`); + } else { + console.log(`❌ Payload ${i + 1}: Metadata field is missing`); + passed = false; + } + + console.log(''); } // Test with chat-like payload (text + image + audio) @@ -174,14 +334,25 @@ async function runTest() { broker_url: TEST_BROKER_URL, fileserver_url: TEST_FILESERVER_URL, correlation_id: 'chat-' + correlationId, + msg_purpose: 'chat', + sender_name: 'js-mix-test', is_publish: true } ); if (chatEnv.payloads.length === 3) { - console.log('✅ Chat-like payloads handled correctly'); + console.log('✅ Chat-like payloads handled correctly (3 payloads)'); } else { - console.log('❌ Chat-like payloads handling failed'); + console.log(`❌ Chat-like payloads handling failed (expected 3, got ${chatEnv.payloads.length})`); + passed = false; + } + + // Verify all payload types in chat are direct transport + const allDirect = chatEnv.payloads.every(p => p.transport === 'direct'); + if (allDirect) { + console.log('✅ All chat payloads use direct transport (small size)'); + } else { + console.log('❌ Some chat payloads use link transport (unexpected for small data)'); passed = false; } @@ -189,6 +360,7 @@ async function runTest() { console.log('\n=== Test Result ==='); if (passed) { console.log('✅ ALL TESTS PASSED'); + console.log('\nNote: Run test_js_mix_payloads_receiver.js to receive the messages.'); process.exit(0); } else { console.log('❌ SOME TESTS FAILED'); @@ -196,10 +368,10 @@ async function runTest() { } } catch (error) { - console.error('❌ Test failed with error:', error.message); + console.error('\n❌ Test failed with error:', error.message); console.error(error.stack); process.exit(1); } } -runTest(); \ No newline at end of file +runTest(); diff --git a/test/test_js_table_receiver.js b/test/test_js_table_receiver.js deleted file mode 100644 index f15a2b7..0000000 --- a/test/test_js_table_receiver.js +++ /dev/null @@ -1,173 +0,0 @@ -/** - * JavaScript Table Receiver Test - * Tests the smartreceive function with table (Arrow IPC) payloads - */ - -const NATSBridge = require('../src/natsbridge.js'); -const crypto = require('crypto'); - -const TEST_BROKER_URL = process.env.NATS_URL || 'nats://localhost:4222'; -const TEST_FILESERVER_URL = process.env.FILESERVER_URL || 'http://localhost:8080'; - -async function runTest() { - console.log('=== JavaScript Table Receiver Test ===\n'); - - // Create a mock NATS message with table payload - const tableData = [ - { id: 1, name: 'Alice', age: 30, active: true }, - { id: 2, name: 'Bob', age: 25, active: false }, - { id: 3, name: 'Charlie', age: 35, active: true } - ]; - - // Convert to Arrow IPC format - const arrow = require('apache-arrow'); - const fields = [ - new arrow.Field('id', arrow.Int64, true), - new arrow.Field('name', arrow.Utf8, true), - new arrow.Field('age', arrow.Int64, true), - new arrow.Field('active', arrow.Boolean, true) - ]; - const schema = new arrow.Schema(fields); - const batches = []; - for (const row of tableData) { - const batch = arrow.recordBatch.fromObjects([row], schema); - batches.push(batch); - } - const buffers = arrow.ipc.recordBatchesToMessage(batches, schema).buffers; - const combined = new Uint8Array(buffers.reduce((acc, b) => acc + b.byteLength, 0)); - let offset = 0; - for (const buf of buffers) { - combined.set(new Uint8Array(buf), offset); - offset += buf.byteLength; - } - const arrowBuffer = Buffer.from(combined); - - const testData = { - correlation_id: 'js-table-receiver-' + crypto.randomUUID(), - msg_id: 'msg-' + crypto.randomUUID(), - timestamp: new Date().toISOString(), - send_to: '/test/table', - msg_purpose: 'test', - sender_name: 'js-table-test', - sender_id: 'sender-' + crypto.randomUUID(), - receiver_name: 'js-receiver', - receiver_id: 'receiver-' + crypto.randomUUID(), - reply_to: '', - reply_to_msg_id: '', - broker_url: TEST_BROKER_URL, - metadata: {}, - payloads: [ - { - id: 'payload-1', - dataname: 'users_table', - payload_type: 'table', - transport: 'direct', - encoding: 'base64', - size: arrowBuffer.length, - data: arrowBuffer.toString('base64'), - metadata: { payload_bytes: arrowBuffer.length } - } - ] - }; - - const mockMsg = { - payload: JSON.stringify(testData) - }; - - console.log('Mock Message Created:'); - console.log(` Correlation ID: ${testData.correlation_id}`); - console.log(` Payloads: ${testData.payloads.length}`); - console.log(` Payload type: ${testData.payloads[0].payload_type}`); - console.log(` Transport: ${testData.payloads[0].transport}\n`); - - try { - // Receive and process the message - console.log('Receiving and processing message...'); - const env = await NATSBridge.smartreceive( - mockMsg, - { - max_retries: 3, - base_delay: 100, - max_delay: 1000 - } - ); - - console.log('\n=== Received Envelope ==='); - console.log(`Correlation ID: ${env.correlation_id}`); - console.log(`Message ID: ${env.msg_id}`); - console.log(`Timestamp: ${env.timestamp}`); - console.log(`Subject: ${env.send_to}`); - console.log(`Payloads: ${env.payloads.length}\n`); - - // Validate received data - console.log('=== Validation ==='); - let passed = true; - - if (!env.correlation_id) { - console.log('❌ correlation_id is missing'); - passed = false; - } else { - console.log('✅ correlation_id present'); - } - - if (env.payloads.length !== 1) { - console.log(`❌ Expected 1 payload, got ${env.payloads.length}`); - passed = false; - } else { - console.log('✅ Correct number of payloads'); - } - - const payload = env.payloads[0]; - if (payload[0] !== 'users_table') { - console.log(`❌ Expected dataname 'users_table', got '${payload[0]}'`); - passed = false; - } else { - console.log('✅ Correct dataname'); - } - - if (payload[2] !== 'table') { - console.log(`❌ Expected type 'table', got '${payload[2]}'`); - passed = false; - } else { - console.log('✅ Correct type'); - } - - // Verify table data is a Buffer (Arrow IPC format) - if (payload[1] instanceof Buffer || payload[1] instanceof Uint8Array) { - console.log('✅ Table data is Arrow IPC buffer'); - console.log(` Buffer size: ${payload[1].length} bytes`); - } else { - console.log(`❌ Expected Buffer/Uint8Array, got ${typeof payload[1]}`); - passed = false; - } - - // Test round-trip with Arrow deserialization - console.log('\n=== Arrow Deserialization Test ==='); - try { - const table = arrow.tableFromRawBytes(payload[1]); - console.log(`✅ Arrow table deserialized successfully`); - console.log(` Schema: ${table.schema.fields.map(f => f.name).join(', ')}`); - console.log(` Num rows: ${table.numRows}`); - } catch (e) { - console.log('❌ Arrow deserialization failed:', e.message); - passed = false; - } - - // Final result - console.log('\n=== Test Result ==='); - if (passed) { - console.log('✅ ALL TESTS PASSED'); - process.exit(0); - } else { - console.log('❌ SOME TESTS FAILED'); - process.exit(1); - } - - } catch (error) { - console.error('❌ Test failed with error:', error.message); - console.error(error.stack); - process.exit(1); - } -} - -runTest(); \ No newline at end of file diff --git a/test/test_js_table_sender.js b/test/test_js_table_sender.js deleted file mode 100644 index f158a4b..0000000 --- a/test/test_js_table_sender.js +++ /dev/null @@ -1,180 +0,0 @@ -/** - * JavaScript Table Sender Test - * Tests the smartsend function with table (Arrow IPC) payloads - */ - -const NATSBridge = require('../src/natsbridge.js'); -const crypto = require('crypto'); - -const TEST_SUBJECT = '/test/table'; -const TEST_BROKER_URL = process.env.NATS_URL || 'nats://localhost:4222'; -const TEST_FILESERVER_URL = process.env.FILESERVER_URL || 'http://localhost:8080'; - -async function runTest() { - console.log('=== JavaScript Table Sender Test ===\n'); - - const correlationId = crypto.randomUUID(); - console.log(`Correlation ID: ${correlationId}`); - console.log(`Subject: ${TEST_SUBJECT}`); - console.log(`Broker URL: ${TEST_BROKER_URL}\n`); - - // Test data - table data as array of objects - const tableData = [ - { id: 1, name: 'Alice', age: 30, active: true }, - { id: 2, name: 'Bob', age: 25, active: false }, - { id: 3, name: 'Charlie', age: 35, active: true }, - { id: 4, name: 'Diana', age: 28, active: true }, - { id: 5, name: 'Eve', age: 32, active: false } - ]; - - const testData = [ - ['users_table', tableData, 'table'] - ]; - - try { - // Send the message - console.log('Sending table payload...'); - const [env, envJsonStr] = await NATSBridge.smartsend( - TEST_SUBJECT, - testData, - { - broker_url: TEST_BROKER_URL, - fileserver_url: TEST_FILESERVER_URL, - correlation_id: correlationId, - msg_purpose: 'test', - sender_name: 'js-table-test', - is_publish: false - } - ); - - console.log('\n=== Envelope Created ==='); - console.log(`Correlation ID: ${env.correlation_id}`); - console.log(`Message ID: ${env.msg_id}`); - console.log(`Timestamp: ${env.timestamp}`); - console.log(`Subject: ${env.send_to}`); - console.log(`Purpose: ${env.msg_purpose}`); - console.log(`Sender: ${env.sender_name}`); - console.log(`Payloads: ${env.payloads.length}\n`); - - // Validate envelope structure - console.log('=== Validation ==='); - let passed = true; - - if (env.payloads.length !== 1) { - console.log(`❌ Expected 1 payload, got ${env.payloads.length}`); - passed = false; - } else { - console.log('✅ Correct number of payloads'); - } - - const payload = env.payloads[0]; - if (payload.dataname !== 'users_table') { - console.log(`❌ Expected dataname 'users_table', got '${payload.dataname}'`); - passed = false; - } else { - console.log('✅ Correct dataname'); - } - - if (payload.payload_type !== 'table') { - console.log(`❌ Expected payload_type 'table', got '${payload.payload_type}'`); - passed = false; - } else { - console.log('✅ Correct payload_type'); - } - - if (payload.transport !== 'direct') { - console.log(`❌ Expected transport 'direct', got '${payload.transport}'`); - passed = false; - } else { - console.log('✅ Correct transport'); - } - - if (payload.encoding !== 'base64') { - console.log(`❌ Expected encoding 'base64', got '${payload.encoding}'`); - passed = false; - } else { - console.log('✅ Correct encoding'); - } - - // Verify Arrow IPC data can be decoded - console.log('\n=== Arrow IPC Verification ==='); - const decodedData = Buffer.from(payload.data, 'base64'); - console.log(`Arrow IPC buffer size: ${decodedData.length} bytes`); - - if (decodedData.length > 0) { - console.log('✅ Arrow IPC data present'); - } else { - console.log('❌ Arrow IPC data is empty'); - passed = false; - } - - // Test with larger table - console.log('\n=== Larger Table Test ==='); - const largeTableData = []; - for (let i = 1; i <= 100; i++) { - largeTableData.push({ - id: i, - name: `User${i}`, - age: Math.floor(Math.random() * 100), - active: Math.random() > 0.5, - score: Math.random() * 100 - }); - } - - const largeTestData = [ - ['large_table', largeTableData, 'table'] - ]; - - const [largeEnv, _] = await NATSBridge.smartsend( - TEST_SUBJECT, - largeTestData, - { - broker_url: TEST_BROKER_URL, - fileserver_url: TEST_FILESERVER_URL, - correlation_id: 'large-' + correlationId, - is_publish: false - } - ); - - if (largeEnv.payloads.length === 1) { - console.log('✅ Large table handled correctly'); - console.log(` Size: ${largeEnv.payloads[0].size} bytes`); - } else { - console.log('❌ Large table handling failed'); - passed = false; - } - - // Test JSON string output - console.log('\n=== JSON String Output Test ==='); - try { - const parsed = JSON.parse(envJsonStr); - if (parsed.correlation_id === env.correlation_id && - parsed.payloads.length === env.payloads.length) { - console.log('✅ JSON string is valid and matches envelope'); - } else { - console.log('❌ JSON string does not match envelope'); - passed = false; - } - } catch (e) { - console.log('❌ JSON string is invalid:', e.message); - passed = false; - } - - // Final result - console.log('\n=== Test Result ==='); - if (passed) { - console.log('✅ ALL TESTS PASSED'); - process.exit(0); - } else { - console.log('❌ SOME TESTS FAILED'); - process.exit(1); - } - - } catch (error) { - console.error('❌ Test failed with error:', error.message); - console.error(error.stack); - process.exit(1); - } -} - -runTest(); \ No newline at end of file diff --git a/test/test_js_text_receiver.js b/test/test_js_text_receiver.js deleted file mode 100644 index df9b203..0000000 --- a/test/test_js_text_receiver.js +++ /dev/null @@ -1,207 +0,0 @@ -/** - * JavaScript Text Receiver Test - * Tests the smartreceive function with text payloads - */ - -const NATSBridge = require('../src/natsbridge.js'); -const crypto = require('crypto'); - -const TEST_BROKER_URL = process.env.NATS_URL || 'nats.yiem.cc'; -const TEST_FILESERVER_URL = process.env.FILESERVER_URL || 'http://192.168.88.104:8080'; - -async function runTest() { - console.log('=== JavaScript Text Receiver Test ===\n'); - - // Create a mock NATS message with text payload - const testData = { - correlation_id: 'test-receiver-' + crypto.randomUUID(), - msg_id: 'msg-' + crypto.randomUUID(), - timestamp: new Date().toISOString(), - send_to: '/test/text', - msg_purpose: 'test', - sender_name: 'js-text-test', - sender_id: 'sender-' + crypto.randomUUID(), - receiver_name: 'js-receiver', - receiver_id: 'receiver-' + crypto.randomUUID(), - reply_to: '', - reply_to_msg_id: '', - broker_url: TEST_BROKER_URL, - metadata: {}, - payloads: [ - { - id: 'payload-' + crypto.randomUUID(), - dataname: 'message', - payload_type: 'text', - transport: 'direct', - encoding: 'base64', - size: 38, - data: Buffer.from('Hello, NATSBridge! This is a test message.').toString('base64'), - metadata: { payload_bytes: 38 } - } - ] - }; - - const mockMsg = { - payload: JSON.stringify(testData) - }; - - console.log('Mock Message Created:'); - console.log(` Correlation ID: ${testData.correlation_id}`); - console.log(` Payloads: ${testData.payloads.length}`); - console.log(` Payload dataname: ${testData.payloads[0].dataname}`); - console.log(` Payload type: ${testData.payloads[0].payload_type}`); - console.log(` Transport: ${testData.payloads[0].transport}\n`); - - try { - // Receive and process the message - console.log('Receiving and processing message...'); - const env = await NATSBridge.smartreceive( - mockMsg, - { - max_retries: 3, - base_delay: 100, - max_delay: 1000 - } - ); - - console.log('\n=== Received Envelope ==='); - console.log(`Correlation ID: ${env.correlation_id}`); - console.log(`Message ID: ${env.msg_id}`); - console.log(`Timestamp: ${env.timestamp}`); - console.log(`Subject: ${env.send_to}`); - console.log(`Payloads: ${env.payloads.length}\n`); - - // Validate received data - console.log('=== Validation ==='); - let passed = true; - - if (!env.correlation_id) { - console.log('❌ correlation_id is missing'); - passed = false; - } else { - console.log('✅ correlation_id present'); - } - - if (env.payloads.length !== 1) { - console.log(`❌ Expected 1 payload, got ${env.payloads.length}`); - passed = false; - } else { - console.log('✅ Correct number of payloads'); - } - - const payload = env.payloads[0]; - if (payload[0] !== 'message') { - console.log(`❌ Expected dataname 'message', got '${payload[0]}'`); - passed = false; - } else { - console.log('✅ Correct dataname'); - } - - if (payload[2] !== 'text') { - console.log(`❌ Expected type 'text', got '${payload[2]}'`); - passed = false; - } else { - console.log('✅ Correct type'); - } - - if (payload[1] !== 'Hello, NATSBridge! This is a test message.') { - console.log(`❌ Data mismatch`); - console.log(` Expected: Hello, NATSBridge! This is a test message.`); - console.log(` Got: ${payload[1]}`); - passed = false; - } else { - console.log('✅ Data correctly deserialized'); - } - - // Test with multiple text payloads - console.log('\n=== Multiple Text Payloads Test ==='); - const multiTestData = { - correlation_id: 'multi-receiver-' + crypto.randomUUID(), - msg_id: 'msg-' + crypto.randomUUID(), - timestamp: new Date().toISOString(), - send_to: '/test/text', - msg_purpose: 'test', - sender_name: 'js-text-test', - sender_id: 'sender-' + crypto.randomUUID(), - receiver_name: 'js-receiver', - receiver_id: 'receiver-' + crypto.randomUUID(), - reply_to: '', - reply_to_msg_id: '', - broker_url: TEST_BROKER_URL, - metadata: {}, - payloads: [ - { - id: 'payload-1', - dataname: 'msg1', - payload_type: 'text', - transport: 'direct', - encoding: 'base64', - size: 16, - data: Buffer.from('First message').toString('base64'), - metadata: { payload_bytes: 16 } - }, - { - id: 'payload-2', - dataname: 'msg2', - payload_type: 'text', - transport: 'direct', - encoding: 'base64', - size: 16, - data: Buffer.from('Second message').toString('base64'), - metadata: { payload_bytes: 16 } - }, - { - id: 'payload-3', - dataname: 'msg3', - payload_type: 'text', - transport: 'direct', - encoding: 'base64', - size: 16, - data: Buffer.from('Third message').toString('base64'), - metadata: { payload_bytes: 16 } - } - ] - }; - - const mockMultiMsg = { - payload: JSON.stringify(multiTestData) - }; - - const multiEnv = await NATSBridge.smartreceive(mockMultiMsg); - - if (multiEnv.payloads.length === 3) { - console.log('✅ Multiple payloads handled correctly'); - - // Verify each payload - const expectedMessages = ['First message', 'Second message', 'Third message']; - for (let i = 0; i < 3; i++) { - if (multiEnv.payloads[i][1] === expectedMessages[i]) { - console.log(`✅ Payload ${i + 1} correctly deserialized`); - } else { - console.log(`❌ Payload ${i + 1} mismatch`); - passed = false; - } - } - } else { - console.log(`❌ Expected 3 payloads, got ${multiEnv.payloads.length}`); - passed = false; - } - - // Final result - console.log('\n=== Test Result ==='); - if (passed) { - console.log('✅ ALL TESTS PASSED'); - process.exit(0); - } else { - console.log('❌ SOME TESTS FAILED'); - process.exit(1); - } - - } catch (error) { - console.error('❌ Test failed with error:', error.message); - console.error(error.stack); - process.exit(1); - } -} - -runTest(); \ No newline at end of file diff --git a/test/test_js_text_sender.js b/test/test_js_text_sender.js deleted file mode 100644 index 794af1b..0000000 --- a/test/test_js_text_sender.js +++ /dev/null @@ -1,170 +0,0 @@ -/** - * JavaScript Text Sender Test - * Tests the smartsend function with text payloads - */ - -const NATSBridge = require('../src/natsbridge.js'); -const crypto = require('crypto'); - -const TEST_SUBJECT = '/test/text'; -const TEST_BROKER_URL = process.env.NATS_URL || 'nats://localhost:4222'; -const TEST_FILESERVER_URL = process.env.FILESERVER_URL || 'http://localhost:8080'; - -async function runTest() { - console.log('=== JavaScript Text Sender Test ===\n'); - - const correlationId = crypto.randomUUID(); - console.log(`Correlation ID: ${correlationId}`); - console.log(`Subject: ${TEST_SUBJECT}`); - console.log(`Broker URL: ${TEST_BROKER_URL}\n`); - - // Test data - const textData = 'Hello, NATSBridge! This is a test message.'; - const testData = [ - ['message', textData, 'text'] - ]; - - try { - // Send the message - console.log('Sending text payload...'); - const [env, envJsonStr] = await NATSBridge.smartsend( - TEST_SUBJECT, - testData, - { - broker_url: TEST_BROKER_URL, - fileserver_url: TEST_FILESERVER_URL, - correlation_id: correlationId, - msg_purpose: 'test', - sender_name: 'js-text-test', - is_publish: false // Don't actually publish for this test - } - ); - - console.log('\n=== Envelope Created ==='); - console.log(`Correlation ID: ${env.correlation_id}`); - console.log(`Message ID: ${env.msg_id}`); - console.log(`Timestamp: ${env.timestamp}`); - console.log(`Subject: ${env.send_to}`); - console.log(`Purpose: ${env.msg_purpose}`); - console.log(`Sender: ${env.sender_name}`); - console.log(`Payloads: ${env.payloads.length}\n`); - - // Validate envelope structure - console.log('=== Validation ==='); - let passed = true; - - if (!env.correlation_id) { - console.log('❌ correlation_id is missing'); - passed = false; - } else { - console.log('✅ correlation_id present'); - } - - if (!env.msg_id) { - console.log('❌ msg_id is missing'); - passed = false; - } else { - console.log('✅ msg_id present'); - } - - if (!env.timestamp) { - console.log('❌ timestamp is missing'); - passed = false; - } else { - console.log('✅ timestamp present'); - } - - if (env.payloads.length !== 1) { - console.log(`❌ Expected 1 payload, got ${env.payloads.length}`); - passed = false; - } else { - console.log('✅ Correct number of payloads'); - } - - const payload = env.payloads[0]; - if (payload.dataname !== 'message') { - console.log(`❌ Expected dataname 'message', got '${payload.dataname}'`); - passed = false; - } else { - console.log('✅ Correct dataname'); - } - - if (payload.payload_type !== 'text') { - console.log(`❌ Expected payload_type 'text', got '${payload.payload_type}'`); - passed = false; - } else { - console.log('✅ Correct payload_type'); - } - - if (payload.transport !== 'direct') { - console.log(`❌ Expected transport 'direct', got '${payload.transport}'`); - passed = false; - } else { - console.log('✅ Correct transport'); - } - - if (payload.encoding !== 'base64') { - console.log(`❌ Expected encoding 'base64', got '${payload.encoding}'`); - passed = false; - } else { - console.log('✅ Correct encoding'); - } - - // Decode and verify the data - const decodedData = Buffer.from(payload.data, 'base64').toString('utf8'); - if (decodedData !== textData) { - console.log(`❌ Decoded data mismatch`); - console.log(` Expected: ${textData}`); - console.log(` Got: ${decodedData}`); - passed = false; - } else { - console.log('✅ Data integrity verified'); - } - - console.log(`\nPayload size: ${payload.size} bytes`); - console.log(`Base64 data length: ${payload.data.length} chars`); - - // Test with multiple text payloads - console.log('\n=== Multiple Text Payloads Test ==='); - const multiTestData = [ - ['msg1', 'First message', 'text'], - ['msg2', 'Second message', 'text'], - ['msg3', 'Third message', 'text'] - ]; - - const [multiEnv, multiEnvJsonStr] = await NATSBridge.smartsend( - TEST_SUBJECT, - multiTestData, - { - broker_url: TEST_BROKER_URL, - fileserver_url: TEST_FILESERVER_URL, - correlation_id: 'multi-test-' + correlationId, - is_publish: false - } - ); - - if (multiEnv.payloads.length === 3) { - console.log('✅ Multiple payloads handled correctly'); - } else { - console.log(`❌ Expected 3 payloads, got ${multiEnv.payloads.length}`); - passed = false; - } - - // Final result - console.log('\n=== Test Result ==='); - if (passed) { - console.log('✅ ALL TESTS PASSED'); - process.exit(0); - } else { - console.log('❌ SOME TESTS FAILED'); - process.exit(1); - } - - } catch (error) { - console.error('❌ Test failed with error:', error.message); - console.error(error.stack); - process.exit(1); - } -} - -runTest(); \ No newline at end of file diff --git a/test/test_mpy_binary_receiver.py b/test/test_mpy_binary_receiver.py deleted file mode 100644 index 13f6da5..0000000 --- a/test/test_mpy_binary_receiver.py +++ /dev/null @@ -1,185 +0,0 @@ -""" -MicroPython Binary Receiver Test -Tests the smartreceive function with binary/image/audio/video payloads - -Note: This test is designed for both MicroPython and desktop Python -for compatibility testing. -""" - -import sys -import os -import json -import base64 - -# Add parent directory to path -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from natsbridge_mpy import smartreceive, DEFAULT_BROKER_URL, DEFAULT_FILESERVER_URL - -TEST_BROKER_URL = os.environ.get('NATS_URL', 'nats://localhost:4222') -TEST_FILESERVER_URL = os.environ.get('FILESERVER_URL', 'http://localhost:8080') - - -def run_test(): - print('=== MicroPython Binary Receiver Test ===\n') - - from natsbridge_mpy import _generate_uuid - - # Create mock NATS message with binary payloads - image_data = bytes([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]) # PNG header - audio_data = bytes([0x46, 0x4C, 0x41, 0x43, 0x00, 0x00, 0x00, 0x00]) # FLAC header - video_data = bytes([0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70]) # MP4 header - generic_binary = bytes([0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE]) - - test_data = { - 'correlation_id': 'mpy-binary-receiver-' + _generate_uuid(), - 'msg_id': _generate_uuid(), - 'timestamp': '2024-01-15T10:30:00Z', - 'send_to': '/test/binary', - 'msg_purpose': 'test', - 'sender_name': 'mpy-binary-test', - 'sender_id': _generate_uuid(), - 'receiver_name': 'mpy-receiver', - 'receiver_id': _generate_uuid(), - 'reply_to': '', - 'reply_to_msg_id': '', - 'broker_url': TEST_BROKER_URL, - 'metadata': {}, - 'payloads': [ - { - 'id': _generate_uuid(), - 'dataname': 'image', - 'payload_type': 'image', - 'transport': 'direct', - 'encoding': 'base64', - 'size': len(image_data), - 'data': base64.b64encode(image_data).decode('ascii'), - 'metadata': {'payload_bytes': len(image_data)} - }, - { - 'id': _generate_uuid(), - 'dataname': 'audio', - 'payload_type': 'audio', - 'transport': 'direct', - 'encoding': 'base64', - 'size': len(audio_data), - 'data': base64.b64encode(audio_data).decode('ascii'), - 'metadata': {'payload_bytes': len(audio_data)} - }, - { - 'id': _generate_uuid(), - 'dataname': 'video', - 'payload_type': 'video', - 'transport': 'direct', - 'encoding': 'base64', - 'size': len(video_data), - 'data': base64.b64encode(video_data).decode('ascii'), - 'metadata': {'payload_bytes': len(video_data)} - }, - { - 'id': _generate_uuid(), - 'dataname': 'binary', - 'payload_type': 'binary', - 'transport': 'direct', - 'encoding': 'base64', - 'size': len(generic_binary), - 'data': base64.b64encode(generic_binary).decode('ascii'), - 'metadata': {'payload_bytes': len(generic_binary)} - } - ] - } - - mock_msg = { - 'payload': json.dumps(test_data) - } - - print('Mock Message Created:') - print(f' Correlation ID: {test_data["correlation_id"]}') - print(f' Payloads: {len(test_data["payloads"])}') - print(f' Payload types: {", ".join(p["payload_type"] for p in test_data["payloads"])}\n') - - try: - # Receive and process the message - print('Receiving and processing message...') - env = smartreceive( - mock_msg, - max_retries=3, - base_delay=100, - max_delay=1000 - ) - - print('\n=== Received Envelope ===') - print(f'Correlation ID: {env["correlation_id"]}') - print(f'Message ID: {env["msg_id"]}') - print(f'Timestamp: {env["timestamp"]}') - print(f'Subject: {env["send_to"]}') - print(f'Payloads: {len(env["payloads"])}\n') - - # Validate received data - print('=== Validation ===') - passed = True - - if not env.get('correlation_id'): - print('❌ correlation_id is missing') - passed = False - else: - print('✅ correlation_id present') - - if len(env['payloads']) != 4: - print(f'❌ Expected 4 payloads, got {len(env["payloads"])}') - passed = False - else: - print('✅ Correct number of payloads') - - # Expected data - expected_data = [ - ('image', image_data, 'image'), - ('audio', audio_data, 'audio'), - ('video', video_data, 'video'), - ('binary', generic_binary, 'binary') - ] - - for i in range(len(env['payloads'])): - payload = env['payloads'][i] - expected = expected_data[i] - - if payload[0] != expected[0]: - print(f"❌ Payload {i + 1}: Expected dataname '{expected[0]}', got '{payload[0]}'") - passed = False - else: - print(f'✅ Payload {i + 1}: Correct dataname') - - if payload[2] != expected[2]: - print(f"❌ Payload {i + 1}: Expected type '{expected[2]}', got '{payload[2]}'") - passed = False - else: - print(f'✅ Payload {i + 1}: Correct type') - - # Verify binary data integrity - received_data = payload[1] - if received_data != expected[1]: - print(f'❌ Payload {i + 1}: Data mismatch') - print(f' Expected: {expected[1]}') - print(f' Got: {received_data}') - passed = False - else: - print(f'✅ Payload {i + 1}: Data correctly deserialized') - - # Final result - print('\n=== Test Result ===') - if passed: - print('✅ ALL TESTS PASSED') - sys.exit(0) - else: - print('❌ SOME TESTS FAILED') - sys.exit(1) - - except Exception as e: - print(f'❌ Test failed with error: {e}') - import traceback - traceback.print_exc() - sys.exit(1) - - -if __name__ == '__main__': - run_test() \ No newline at end of file diff --git a/test/test_mpy_binary_sender.py b/test/test_mpy_binary_sender.py deleted file mode 100644 index bd44fb8..0000000 --- a/test/test_mpy_binary_sender.py +++ /dev/null @@ -1,163 +0,0 @@ -""" -MicroPython Binary Sender Test -Tests the smartsend function with binary/image/audio/video payloads - -Note: This test is designed for both MicroPython and desktop Python -for compatibility testing. -""" - -import sys -import os -import base64 - -# Add parent directory to path -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from natsbridge_mpy import smartsend, DEFAULT_BROKER_URL, DEFAULT_FILESERVER_URL, DEFAULT_SIZE_THRESHOLD, MAX_PAYLOAD_SIZE - -TEST_SUBJECT = '/test/binary' -TEST_BROKER_URL = os.environ.get('NATS_URL', 'nats://localhost:4222') -TEST_FILESERVER_URL = os.environ.get('FILESERVER_URL', 'http://localhost:8080') - - -def run_test(): - print('=== MicroPython Binary Sender Test ===\n') - - from natsbridge_mpy import _generate_uuid - correlation_id = 'mpy-binary-test-' + _generate_uuid() - print(f'Correlation ID: {correlation_id}') - print(f'Subject: {TEST_SUBJECT}') - print(f'Broker URL: {TEST_BROKER_URL}') - print(f'Default Size Threshold: {DEFAULT_SIZE_THRESHOLD} bytes') - print(f'Max Payload Size: {MAX_PAYLOAD_SIZE} bytes\n') - - # Test data - binary data for different types - image_data = bytearray([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]) # PNG header - audio_data = bytearray([0x46, 0x4C, 0x41, 0x43, 0x00, 0x00, 0x00, 0x00]) # FLAC header - video_data = bytearray([0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70]) # MP4 header - generic_binary = bytearray([0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE]) - - test_data = [ - ('image', bytes(image_data), 'image'), - ('audio', bytes(audio_data), 'audio'), - ('video', bytes(video_data), 'video'), - ('binary', bytes(generic_binary), 'binary') - ] - - try: - # Send the message - print('Sending binary payloads...') - env, env_json_str = smartsend( - TEST_SUBJECT, - test_data, - broker_url=TEST_BROKER_URL, - fileserver_url=TEST_FILESERVER_URL, - correlation_id=correlation_id, - msg_purpose='test', - sender_name='mpy-binary-test', - is_publish=False - ) - - print('\n=== Envelope Created ===') - print(f'Correlation ID: {env["correlation_id"]}') - print(f'Message ID: {env["msg_id"]}') - print(f'Timestamp: {env["timestamp"]}') - print(f'Subject: {env["send_to"]}') - print(f'Purpose: {env["msg_purpose"]}') - print(f'Sender: {env["sender_name"]}') - print(f'Payloads: {len(env["payloads"])}\n') - - # Validate envelope structure - print('=== Validation ===') - passed = True - - if len(env['payloads']) != 4: - print(f'❌ Expected 4 payloads, got {len(env["payloads"])}') - passed = False - else: - print('✅ Correct number of payloads') - - # Test each payload - expected_datanames = ['image', 'audio', 'video', 'binary'] - expected_types = ['image', 'audio', 'video', 'binary'] - expected_data = [bytes(image_data), bytes(audio_data), bytes(video_data), bytes(generic_binary)] - - for i in range(len(env['payloads'])): - payload = env['payloads'][i] - - if payload['dataname'] != expected_datanames[i]: - print(f"❌ Payload {i + 1}: Expected dataname '{expected_datanames[i]}', got '{payload['dataname']}'") - passed = False - else: - print(f'✅ Payload {i + 1}: Correct dataname') - - if payload['payload_type'] != expected_types[i]: - print(f"❌ Payload {i + 1}: Expected type '{expected_types[i]}', got '{payload['payload_type']}'") - passed = False - else: - print(f'✅ Payload {i + 1}: Correct type') - - if payload['transport'] != 'direct': - print(f"❌ Payload {i + 1}: Expected transport 'direct', got '{payload['transport']}'") - passed = False - else: - print(f'✅ Payload {i + 1}: Correct transport') - - if payload['encoding'] != 'base64': - print(f"❌ Payload {i + 1}: Expected encoding 'base64', got '{payload['encoding']}'") - passed = False - else: - print(f'✅ Payload {i + 1}: Correct encoding') - - # Decode and verify the data - decoded_data = base64.b64decode(payload['data']) - original_data = expected_data[i] - - if decoded_data != original_data: - print(f'❌ Payload {i + 1}: Data integrity mismatch') - passed = False - else: - print(f'✅ Payload {i + 1}: Data integrity verified') - - print(f' Size: {payload["size"]} bytes\n') - - # Test with larger binary data - print('=== Large Binary Data Test ===') - large_data = bytes([0xFF] * 1000) # 1KB of binary data - large_test_data = [ - ('large_binary', large_data, 'binary') - ] - - large_env, _ = smartsend( - TEST_SUBJECT, - large_test_data, - broker_url=TEST_BROKER_URL, - fileserver_url=TEST_FILESERVER_URL, - correlation_id='large-' + correlation_id, - is_publish=False - ) - - if len(large_env['payloads']) == 1 and large_env['payloads'][0]['size'] == 1000: - print('✅ Large binary data handled correctly') - else: - print('❌ Large binary data handling failed') - passed = False - - # Final result - print('\n=== Test Result ===') - if passed: - print('✅ ALL TESTS PASSED') - sys.exit(0) - else: - print('❌ SOME TESTS FAILED') - sys.exit(1) - - except Exception as e: - print(f'❌ Test failed with error: {e}') - import traceback - traceback.print_exc() - sys.exit(1) - - -if __name__ == '__main__': - run_test() \ No newline at end of file diff --git a/test/test_mpy_dictionary_receiver.py b/test/test_mpy_dictionary_receiver.py deleted file mode 100644 index 0f3a804..0000000 --- a/test/test_mpy_dictionary_receiver.py +++ /dev/null @@ -1,224 +0,0 @@ -""" -MicroPython Dictionary Receiver Test -Tests the smartreceive function with dictionary payloads - -Note: This test is designed for both MicroPython and desktop Python -for compatibility testing. -""" - -import sys -import os -import json - -# Add parent directory to path -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from natsbridge_mpy import smartreceive, DEFAULT_BROKER_URL, DEFAULT_FILESERVER_URL - -TEST_BROKER_URL = os.environ.get('NATS_URL', 'nats://localhost:4222') -TEST_FILESERVER_URL = os.environ.get('FILESERVER_URL', 'http://localhost:8080') - - -def run_test(): - print('=== MicroPython Dictionary Receiver Test ===\n') - - from natsbridge_mpy import _generate_uuid - - # Create a mock NATS message with dictionary payloads - import base64 - - simple_dict = {'key1': 'value1', 'key2': 'value2'} - nested_dict = {'outer': {'inner': 'value', 'number': 42}} - array_dict = {'items': [1, 2, 3, 'four', 'five']} - mixed_dict = {'string': 'text', 'number': 123, 'boolean': True, 'null_val': None} - - test_data = { - 'correlation_id': 'mpy-receiver-dict-' + _generate_uuid(), - 'msg_id': _generate_uuid(), - 'timestamp': '2024-01-15T10:30:00Z', - 'send_to': '/test/dictionary', - 'msg_purpose': 'test', - 'sender_name': 'mpy-dict-test', - 'sender_id': _generate_uuid(), - 'receiver_name': 'mpy-receiver', - 'receiver_id': _generate_uuid(), - 'reply_to': '', - 'reply_to_msg_id': '', - 'broker_url': TEST_BROKER_URL, - 'metadata': {}, - 'payloads': [ - { - 'id': _generate_uuid(), - 'dataname': 'simple_dict', - 'payload_type': 'dictionary', - 'transport': 'direct', - 'encoding': 'base64', - 'size': len(json.dumps(simple_dict).encode('utf8')), - 'data': base64.b64encode(json.dumps(simple_dict).encode('utf8')).decode('ascii'), - 'metadata': {'payload_bytes': len(json.dumps(simple_dict).encode('utf8'))} - }, - { - 'id': _generate_uuid(), - 'dataname': 'nested_dict', - 'payload_type': 'dictionary', - 'transport': 'direct', - 'encoding': 'base64', - 'size': len(json.dumps(nested_dict).encode('utf8')), - 'data': base64.b64encode(json.dumps(nested_dict).encode('utf8')).decode('ascii'), - 'metadata': {'payload_bytes': len(json.dumps(nested_dict).encode('utf8'))} - }, - { - 'id': _generate_uuid(), - 'dataname': 'array_dict', - 'payload_type': 'dictionary', - 'transport': 'direct', - 'encoding': 'base64', - 'size': len(json.dumps(array_dict).encode('utf8')), - 'data': base64.b64encode(json.dumps(array_dict).encode('utf8')).decode('ascii'), - 'metadata': {'payload_bytes': len(json.dumps(array_dict).encode('utf8'))} - }, - { - 'id': _generate_uuid(), - 'dataname': 'mixed_dict', - 'payload_type': 'dictionary', - 'transport': 'direct', - 'encoding': 'base64', - 'size': len(json.dumps(mixed_dict).encode('utf8')), - 'data': base64.b64encode(json.dumps(mixed_dict).encode('utf8')).decode('ascii'), - 'metadata': {'payload_bytes': len(json.dumps(mixed_dict).encode('utf8'))} - } - ] - } - - mock_msg = { - 'payload': json.dumps(test_data) - } - - print('Mock Message Created:') - print(f' Correlation ID: {test_data["correlation_id"]}') - print(f' Payloads: {len(test_data["payloads"])}') - print(f' Payload types: {", ".join(p["payload_type"] for p in test_data["payloads"])}\n') - - try: - # Receive and process the message - print('Receiving and processing message...') - env = smartreceive( - mock_msg, - max_retries=3, - base_delay=100, - max_delay=1000 - ) - - print('\n=== Received Envelope ===') - print(f'Correlation ID: {env["correlation_id"]}') - print(f'Message ID: {env["msg_id"]}') - print(f'Timestamp: {env["timestamp"]}') - print(f'Subject: {env["send_to"]}') - print(f'Payloads: {len(env["payloads"])}\n') - - # Validate received data - print('=== Validation ===') - passed = True - - if not env.get('correlation_id'): - print('❌ correlation_id is missing') - passed = False - else: - print('✅ correlation_id present') - - if len(env['payloads']) != 4: - print(f'❌ Expected 4 payloads, got {len(env["payloads"])}') - passed = False - else: - print('✅ Correct number of payloads') - - # Expected data - expected_data = [ - ('simple_dict', simple_dict, 'dictionary'), - ('nested_dict', nested_dict, 'dictionary'), - ('array_dict', array_dict, 'dictionary'), - ('mixed_dict', mixed_dict, 'dictionary') - ] - - for i in range(len(env['payloads'])): - payload = env['payloads'][i] - expected = expected_data[i] - - if payload[0] != expected[0]: - print(f"❌ Payload {i + 1}: Expected dataname '{expected[0]}', got '{payload[0]}'") - passed = False - else: - print(f'✅ Payload {i + 1}: Correct dataname') - - if payload[2] != expected[2]: - print(f"❌ Payload {i + 1}: Expected type '{expected[2]}', got '{payload[2]}'") - passed = False - else: - print(f'✅ Payload {i + 1}: Correct type') - - data_match = json.dumps(payload[1], sort_keys=True) == json.dumps(expected[1], sort_keys=True) - if not data_match: - print(f'❌ Payload {i + 1}: Data mismatch') - print(f' Expected: {json.dumps(expected[1], sort_keys=True)}') - print(f' Got: {json.dumps(payload[1], sort_keys=True)}') - passed = False - else: - print(f'✅ Payload {i + 1}: Data correctly deserialized') - - # Test round-trip with receive - print('\n=== Round-trip Test ===') - round_trip_data = { - 'correlation_id': 'roundtrip-' + _generate_uuid(), - 'msg_id': _generate_uuid(), - 'timestamp': '2024-01-15T10:30:00Z', - 'send_to': '/test/dictionary', - 'msg_purpose': 'test', - 'sender_name': 'mpy-test', - 'sender_id': _generate_uuid(), - 'receiver_name': 'mpy-receiver', - 'receiver_id': _generate_uuid(), - 'reply_to': '', - 'reply_to_msg_id': '', - 'broker_url': TEST_BROKER_URL, - 'metadata': {}, - 'payloads': [ - { - 'id': _generate_uuid(), - 'dataname': 'roundtrip', - 'payload_type': 'dictionary', - 'transport': 'direct', - 'encoding': 'base64', - 'size': len(json.dumps({'test': 'data', 'nested': {'a': 1, 'b': 2}}).encode('utf8')), - 'data': base64.b64encode(json.dumps({'test': 'data', 'nested': {'a': 1, 'b': 2}}).encode('utf8')).decode('ascii'), - 'metadata': {'payload_bytes': len(json.dumps({'test': 'data', 'nested': {'a': 1, 'b': 2}}).encode('utf8'))} - } - ] - } - - mock_rt_msg = {'payload': json.dumps(round_trip_data)} - rt_env = smartreceive(mock_rt_msg) - - if rt_env['payloads'][0][0] == 'roundtrip' and rt_env['payloads'][0][2] == 'dictionary': - print('✅ Round-trip test successful') - else: - print('❌ Round-trip test failed') - passed = False - - # Final result - print('\n=== Test Result ===') - if passed: - print('✅ ALL TESTS PASSED') - sys.exit(0) - else: - print('❌ SOME TESTS FAILED') - sys.exit(1) - - except Exception as e: - print(f'❌ Test failed with error: {e}') - import traceback - traceback.print_exc() - sys.exit(1) - - -if __name__ == '__main__': - run_test() \ No newline at end of file diff --git a/test/test_mpy_dictionary_sender.py b/test/test_mpy_dictionary_sender.py deleted file mode 100644 index 54a190d..0000000 --- a/test/test_mpy_dictionary_sender.py +++ /dev/null @@ -1,177 +0,0 @@ -""" -MicroPython Dictionary Sender Test -Tests the smartsend function with dictionary payloads - -Note: This test is designed for both MicroPython and desktop Python -for compatibility testing. -""" - -import sys -import os -import json - -# Add parent directory to path -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from natsbridge_mpy import smartsend, DEFAULT_BROKER_URL, DEFAULT_FILESERVER_URL, DEFAULT_SIZE_THRESHOLD, MAX_PAYLOAD_SIZE - -TEST_SUBJECT = '/test/dictionary' -TEST_BROKER_URL = os.environ.get('NATS_URL', 'nats://localhost:4222') -TEST_FILESERVER_URL = os.environ.get('FILESERVER_URL', 'http://localhost:8080') - - -def run_test(): - print('=== MicroPython Dictionary Sender Test ===\n') - - from natsbridge_mpy import _generate_uuid - correlation_id = 'mpy-dict-test-' + _generate_uuid() - print(f'Correlation ID: {correlation_id}') - print(f'Subject: {TEST_SUBJECT}') - print(f'Broker URL: {TEST_BROKER_URL}') - print(f'Default Size Threshold: {DEFAULT_SIZE_THRESHOLD} bytes') - print(f'Max Payload Size: {MAX_PAYLOAD_SIZE} bytes\n') - - # Test data - various dictionary structures - test_data = [ - ('simple_dict', {'key1': 'value1', 'key2': 'value2'}, 'dictionary'), - ('nested_dict', {'outer': {'inner': 'value', 'number': 42}}, 'dictionary'), - ('array_dict', {'items': [1, 2, 3, 'four', 'five']}, 'dictionary'), - ('mixed_dict', {'string': 'text', 'number': 123, 'boolean': True, 'null_val': None}, 'dictionary') - ] - - try: - # Send the message - print('Sending dictionary payloads...') - env, env_json_str = smartsend( - TEST_SUBJECT, - test_data, - broker_url=TEST_BROKER_URL, - fileserver_url=TEST_FILESERVER_URL, - correlation_id=correlation_id, - msg_purpose='test', - sender_name='mpy-dict-test', - is_publish=False - ) - - print('\n=== Envelope Created ===') - print(f'Correlation ID: {env["correlation_id"]}') - print(f'Message ID: {env["msg_id"]}') - print(f'Timestamp: {env["timestamp"]}') - print(f'Subject: {env["send_to"]}') - print(f'Purpose: {env["msg_purpose"]}') - print(f'Sender: {env["sender_name"]}') - print(f'Payloads: {len(env["payloads"])}\n') - - # Validate envelope structure - print('=== Validation ===') - passed = True - - if len(env['payloads']) != 4: - print(f'❌ Expected 4 payloads, got {len(env["payloads"])}') - passed = False - else: - print('✅ Correct number of payloads') - - # Test each payload - expected_datanames = ['simple_dict', 'nested_dict', 'array_dict', 'mixed_dict'] - expected_types = ['dictionary', 'dictionary', 'dictionary', 'dictionary'] - - for i in range(len(env['payloads'])): - payload = env['payloads'][i] - - if payload['dataname'] != expected_datanames[i]: - print(f"❌ Payload {i + 1}: Expected dataname '{expected_datanames[i]}', got '{payload['dataname']}'") - passed = False - else: - print(f'✅ Payload {i + 1}: Correct dataname') - - if payload['payload_type'] != expected_types[i]: - print(f"❌ Payload {i + 1}: Expected type '{expected_types[i]}', got '{payload['payload_type']}'") - passed = False - else: - print(f'✅ Payload {i + 1}: Correct type') - - if payload['transport'] != 'direct': - print(f"❌ Payload {i + 1}: Expected transport 'direct', got '{payload['transport']}'") - passed = False - else: - print(f'✅ Payload {i + 1}: Correct transport') - - if payload['encoding'] != 'base64': - print(f"❌ Payload {i + 1}: Expected encoding 'base64', got '{payload['encoding']}'") - passed = False - else: - print(f'✅ Payload {i + 1}: Correct encoding') - - # Decode and verify the data - import base64 - decoded_data = json.loads(base64.b64decode(payload['data']).decode('ascii')) - original_data = test_data[i][1] - - # Normalize for comparison - if json.dumps(decoded_data, sort_keys=True) != json.dumps(original_data, sort_keys=True): - print(f'❌ Payload {i + 1}: Data integrity mismatch') - print(f' Expected: {json.dumps(original_data)}') - print(f' Got: {json.dumps(decoded_data)}') - passed = False - else: - print(f'✅ Payload {i + 1}: Data integrity verified') - - print(f' Size: {payload["size"]} bytes\n') - - # Test round-trip serialization - print('=== Round-trip Serialization Test ===') - round_trip_data = [ - ('roundtrip', {'test': 'data', 'numbers': [1, 2, 3], 'nested': {'a': 1, 'b': 2}}, 'dictionary') - ] - - rt_env, _ = smartsend( - TEST_SUBJECT, - round_trip_data, - broker_url=TEST_BROKER_URL, - fileserver_url=TEST_FILESERVER_URL, - correlation_id='roundtrip-' + correlation_id, - is_publish=False - ) - - rt_payload = rt_env['payloads'][0] - rt_decoded = json.loads(base64.b64decode(rt_payload['data']).decode('ascii')) - - if json.dumps(rt_decoded, sort_keys=True) == json.dumps(round_trip_data[0][1], sort_keys=True): - print('✅ Round-trip serialization successful') - else: - print('❌ Round-trip serialization failed') - passed = False - - # Test JSON string output - print('\n=== JSON String Output Test ===') - try: - parsed = json.loads(env_json_str) - if parsed['correlation_id'] == env['correlation_id'] and \ - len(parsed['payloads']) == len(env['payloads']): - print('✅ JSON string is valid and matches envelope') - else: - print('❌ JSON string does not match envelope') - passed = False - except json.JSONDecodeError as e: - print(f'❌ JSON string is invalid: {e}') - passed = False - - # Final result - print('\n=== Test Result ===') - if passed: - print('✅ ALL TESTS PASSED') - sys.exit(0) - else: - print('❌ SOME TESTS FAILED') - sys.exit(1) - - except Exception as e: - print(f'❌ Test failed with error: {e}') - import traceback - traceback.print_exc() - sys.exit(1) - - -if __name__ == '__main__': - run_test() \ No newline at end of file diff --git a/test/test_mpy_text_receiver.py b/test/test_mpy_text_receiver.py deleted file mode 100644 index c053a0b..0000000 --- a/test/test_mpy_text_receiver.py +++ /dev/null @@ -1,209 +0,0 @@ -""" -MicroPython Text Receiver Test -Tests the smartreceive function with text payloads - -Note: This test is designed for both MicroPython and desktop Python -for compatibility testing. -""" - -import sys -import os -import json - -# Add parent directory to path -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from natsbridge_mpy import smartreceive, DEFAULT_BROKER_URL, DEFAULT_FILESERVER_URL - -TEST_BROKER_URL = os.environ.get('NATS_URL', 'nats://localhost:4222') -TEST_FILESERVER_URL = os.environ.get('FILESERVER_URL', 'http://localhost:8080') - - -def run_test(): - print('=== MicroPython Text Receiver Test ===\n') - - from natsbridge_mpy import _generate_uuid - - # Create a mock NATS message with text payload - test_text = 'Hello, NATSBridge! This is a test message.' - import base64 - - test_data = { - 'correlation_id': 'mpy-receiver-test-' + _generate_uuid(), - 'msg_id': _generate_uuid(), - 'timestamp': '2024-01-15T10:30:00Z', - 'send_to': '/test/text', - 'msg_purpose': 'test', - 'sender_name': 'mpy-text-test', - 'sender_id': _generate_uuid(), - 'receiver_name': 'mpy-receiver', - 'receiver_id': _generate_uuid(), - 'reply_to': '', - 'reply_to_msg_id': '', - 'broker_url': TEST_BROKER_URL, - 'metadata': {}, - 'payloads': [ - { - 'id': _generate_uuid(), - 'dataname': 'message', - 'payload_type': 'text', - 'transport': 'direct', - 'encoding': 'base64', - 'size': len(test_text.encode('utf8')), - 'data': base64.b64encode(test_text.encode('utf8')).decode('ascii'), - 'metadata': {'payload_bytes': len(test_text.encode('utf8'))} - } - ] - } - - mock_msg = { - 'payload': json.dumps(test_data) - } - - print('Mock Message Created:') - print(f' Correlation ID: {test_data["correlation_id"]}') - print(f' Payloads: {len(test_data["payloads"])}') - print(f' Payload dataname: {test_data["payloads"][0]["dataname"]}') - print(f' Payload type: {test_data["payloads"][0]["payload_type"]}') - print(f' Transport: {test_data["payloads"][0]["transport"]}\n') - - try: - # Receive and process the message - print('Receiving and processing message...') - env = smartreceive( - mock_msg, - max_retries=3, - base_delay=100, - max_delay=1000 - ) - - print('\n=== Received Envelope ===') - print(f'Correlation ID: {env["correlation_id"]}') - print(f'Message ID: {env["msg_id"]}') - print(f'Timestamp: {env["timestamp"]}') - print(f'Subject: {env["send_to"]}') - print(f'Payloads: {len(env["payloads"])}\n') - - # Validate received data - print('=== Validation ===') - passed = True - - if not env.get('correlation_id'): - print('❌ correlation_id is missing') - passed = False - else: - print('✅ correlation_id present') - - if len(env['payloads']) != 1: - print(f'❌ Expected 1 payload, got {len(env["payloads"])}') - passed = False - else: - print('✅ Correct number of payloads') - - payload = env['payloads'][0] - if payload[0] != 'message': - print(f"❌ Expected dataname 'message', got '{payload[0]}'") - passed = False - else: - print('✅ Correct dataname') - - if payload[2] != 'text': - print(f"❌ Expected type 'text', got '{payload[2]}'") - passed = False - else: - print('✅ Correct type') - - if payload[1] != test_text: - print('❌ Data mismatch') - print(f' Expected: {test_text}') - print(f' Got: {payload[1]}') - passed = False - else: - print('✅ Data correctly deserialized') - - # Test with multiple text payloads - print('\n=== Multiple Text Payloads Test ===') - multi_test_data = { - 'correlation_id': 'multi-receiver-' + _generate_uuid(), - 'msg_id': _generate_uuid(), - 'timestamp': '2024-01-15T10:30:00Z', - 'send_to': '/test/text', - 'msg_purpose': 'test', - 'sender_name': 'mpy-text-test', - 'sender_id': _generate_uuid(), - 'receiver_name': 'mpy-receiver', - 'receiver_id': _generate_uuid(), - 'reply_to': '', - 'reply_to_msg_id': '', - 'broker_url': TEST_BROKER_URL, - 'metadata': {}, - 'payloads': [ - { - 'id': _generate_uuid(), - 'dataname': 'msg1', - 'payload_type': 'text', - 'transport': 'direct', - 'encoding': 'base64', - 'size': 16, - 'data': base64.b64encode(b'First message').decode('ascii'), - 'metadata': {'payload_bytes': 16} - }, - { - 'id': _generate_uuid(), - 'dataname': 'msg2', - 'payload_type': 'text', - 'transport': 'direct', - 'encoding': 'base64', - 'size': 16, - 'data': base64.b64encode(b'Second message').decode('ascii'), - 'metadata': {'payload_bytes': 16} - }, - { - 'id': _generate_uuid(), - 'dataname': 'msg3', - 'payload_type': 'text', - 'transport': 'direct', - 'encoding': 'base64', - 'size': 16, - 'data': base64.b64encode(b'Third message').decode('ascii'), - 'metadata': {'payload_bytes': 16} - } - ] - } - - mock_multi_msg = {'payload': json.dumps(multi_test_data)} - multi_env = smartreceive(mock_multi_msg) - - if len(multi_env['payloads']) == 3: - print('✅ Multiple payloads handled correctly') - - # Verify each payload - expected_messages = ['First message', 'Second message', 'Third message'] - for i in range(3): - if multi_env['payloads'][i][1] == expected_messages[i]: - print(f'✅ Payload {i + 1} correctly deserialized') - else: - print(f'❌ Payload {i + 1} mismatch') - passed = False - else: - print(f'❌ Expected 3 payloads, got {len(multi_env["payloads"])}') - passed = False - - # Final result - print('\n=== Test Result ===') - if passed: - print('✅ ALL TESTS PASSED') - sys.exit(0) - else: - print('❌ SOME TESTS FAILED') - sys.exit(1) - - except Exception as e: - print(f'❌ Test failed with error: {e}') - import traceback - traceback.print_exc() - sys.exit(1) - - -if __name__ == '__main__': - run_test() \ No newline at end of file diff --git a/test/test_mpy_text_sender.py b/test/test_mpy_text_sender.py deleted file mode 100644 index dff9ec8..0000000 --- a/test/test_mpy_text_sender.py +++ /dev/null @@ -1,205 +0,0 @@ -""" -MicroPython Text Sender Test -Tests the smartsend function with text payloads - -Note: This test is designed for both MicroPython and desktop Python -for compatibility testing. -""" - -import sys -import os - -# Add parent directory to path -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from natsbridge_mpy import smartsend, DEFAULT_BROKER_URL, DEFAULT_FILESERVER_URL, DEFAULT_SIZE_THRESHOLD, MAX_PAYLOAD_SIZE - -TEST_SUBJECT = '/test/text' -TEST_BROKER_URL = os.environ.get('NATS_URL', 'nats://localhost:4222') -TEST_FILESERVER_URL = os.environ.get('FILESERVER_URL', 'http://localhost:8080') - - -def run_test(): - print('=== MicroPython Text Sender Test ===\n') - - from natsbridge_mpy import _generate_uuid - correlation_id = 'mpy-text-test-' + _generate_uuid() - print(f'Correlation ID: {correlation_id}') - print(f'Subject: {TEST_SUBJECT}') - print(f'Broker URL: {TEST_BROKER_URL}') - print(f'Default Size Threshold: {DEFAULT_SIZE_THRESHOLD} bytes') - print(f'Max Payload Size: {MAX_PAYLOAD_SIZE} bytes\n') - - # Test data - text_data = 'Hello, NATSBridge! This is a test message.' - test_data = [ - ('message', text_data, 'text') - ] - - try: - # Send the message - print('Sending text payload...') - env, env_json_str = smartsend( - TEST_SUBJECT, - test_data, - broker_url=TEST_BROKER_URL, - fileserver_url=TEST_FILESERVER_URL, - correlation_id=correlation_id, - msg_purpose='test', - sender_name='mpy-text-test', - is_publish=False # Don't actually publish for this test - ) - - print('\n=== Envelope Created ===') - print(f'Correlation ID: {env["correlation_id"]}') - print(f'Message ID: {env["msg_id"]}') - print(f'Timestamp: {env["timestamp"]}') - print(f'Subject: {env["send_to"]}') - print(f'Purpose: {env["msg_purpose"]}') - print(f'Sender: {env["sender_name"]}') - print(f'Payloads: {len(env["payloads"])}\n') - - # Validate envelope structure - print('=== Validation ===') - passed = True - - if not env.get('correlation_id'): - print('❌ correlation_id is missing') - passed = False - else: - print('✅ correlation_id present') - - if not env.get('msg_id'): - print('❌ msg_id is missing') - passed = False - else: - print('✅ msg_id present') - - if not env.get('timestamp'): - print('❌ timestamp is missing') - passed = False - else: - print('✅ timestamp present') - - if len(env['payloads']) != 1: - print(f'❌ Expected 1 payload, got {len(env["payloads"])}') - passed = False - else: - print('✅ Correct number of payloads') - - payload = env['payloads'][0] - if payload['dataname'] != 'message': - print(f"❌ Expected dataname 'message', got '{payload['dataname']}'") - passed = False - else: - print('✅ Correct dataname') - - if payload['payload_type'] != 'text': - print(f"❌ Expected payload_type 'text', got '{payload['payload_type']}'") - passed = False - else: - print('✅ Correct payload_type') - - if payload['transport'] != 'direct': - print(f"❌ Expected transport 'direct', got '{payload['transport']}'") - passed = False - else: - print('✅ Correct transport') - - if payload['encoding'] != 'base64': - print(f"❌ Expected encoding 'base64', got '{payload['encoding']}'") - passed = False - else: - print('✅ Correct encoding') - - # Decode and verify the data - import base64 - decoded_data = base64.b64decode(payload['data']).decode('ascii') - if decoded_data != text_data: - print('❌ Decoded data mismatch') - print(f' Expected: {text_data}') - print(f' Got: {decoded_data}') - passed = False - else: - print('✅ Data integrity verified') - - print(f'\nPayload size: {payload["size"]} bytes') - print(f'Base64 data length: {len(payload["data"])} chars') - - # Test with multiple text payloads - print('\n=== Multiple Text Payloads Test ===') - multi_test_data = [ - ('msg1', 'First message', 'text'), - ('msg2', 'Second message', 'text'), - ('msg3', 'Third message', 'text') - ] - - multi_env, _ = smartsend( - TEST_SUBJECT, - multi_test_data, - broker_url=TEST_BROKER_URL, - fileserver_url=TEST_FILESERVER_URL, - correlation_id='multi-test-' + correlation_id, - is_publish=False - ) - - if len(multi_env['payloads']) == 3: - print('✅ Multiple payloads handled correctly') - else: - print(f'❌ Expected 3 payloads, got {len(multi_env["payloads"])}') - passed = False - - # Test size threshold enforcement - print('\n=== Size Threshold Test ===') - small_text = 'small' - large_text = 'x' * (DEFAULT_SIZE_THRESHOLD - 100) # Just under threshold - - small_env, _ = smartsend( - TEST_SUBJECT, - [('small', small_text, 'text')], - broker_url=TEST_BROKER_URL, - is_publish=False - ) - - if small_env['payloads'][0]['transport'] == 'direct': - print('✅ Small payload uses direct transport') - else: - print('❌ Small payload should use direct transport') - passed = False - - # Test that large text (> MAX_PAYLOAD_SIZE) raises error - print('\n=== Max Payload Size Test ===') - try: - too_large_text = 'x' * (MAX_PAYLOAD_SIZE + 1000) - large_env, _ = smartsend( - TEST_SUBJECT, - [('large', too_large_text, 'text')], - broker_url=TEST_BROKER_URL, - is_publish=False - ) - print('❌ Should have raised MemoryError for payload exceeding MAX_PAYLOAD_SIZE') - passed = False - except MemoryError as e: - print(f'✅ Correctly raised MemoryError: {e}') - except Exception as e: - print(f'❌ Unexpected error: {type(e).__name__}: {e}') - passed = False - - # Final result - print('\n=== Test Result ===') - if passed: - print('✅ ALL TESTS PASSED') - sys.exit(0) - else: - print('❌ SOME TESTS FAILED') - sys.exit(1) - - except Exception as e: - print(f'❌ Test failed with error: {e}') - import traceback - traceback.print_exc() - sys.exit(1) - - -if __name__ == '__main__': - run_test() \ No newline at end of file diff --git a/test/test_py_binary_receiver.py b/test/test_py_binary_receiver.py deleted file mode 100644 index eaeeb0c..0000000 --- a/test/test_py_binary_receiver.py +++ /dev/null @@ -1,184 +0,0 @@ -""" -Python Binary Receiver Test -Tests the smartreceive function with binary/image/audio/video/table payloads -""" - -import asyncio -import sys -import os -import json -import base64 - -# Add parent directory to path -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from natsbridge import smartreceive, DEFAULT_BROKER_URL, DEFAULT_FILESERVER_URL - -TEST_BROKER_URL = os.environ.get('NATS_URL', 'nats://localhost:4222') -TEST_FILESERVER_URL = os.environ.get('FILESERVER_URL', 'http://localhost:8080') - - -async def run_test(): - print('=== Python Binary Receiver Test ===\n') - - # Create mock NATS message with binary payloads - image_data = bytes([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]) # PNG header - audio_data = bytes([0x46, 0x4C, 0x41, 0x43, 0x00, 0x00, 0x00, 0x00]) # FLAC header - video_data = bytes([0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70]) # MP4 header - generic_binary = bytes([0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE]) - - test_data = { - 'correlation_id': 'py-binary-receiver-' + str(asyncio.get_event_loop().time() * 1000000), - 'msg_id': 'msg-' + str(asyncio.get_event_loop().time() * 1000000), - 'timestamp': asyncio.get_event_loop().time().isoformat(), - 'send_to': '/test/binary', - 'msg_purpose': 'test', - 'sender_name': 'py-binary-test', - 'sender_id': 'sender-' + str(asyncio.get_event_loop().time() * 1000000), - 'receiver_name': 'py-receiver', - 'receiver_id': 'receiver-' + str(asyncio.get_event_loop().time() * 1000000), - 'reply_to': '', - 'reply_to_msg_id': '', - 'broker_url': TEST_BROKER_URL, - 'metadata': {}, - 'payloads': [ - { - 'id': 'payload-1', - 'dataname': 'image', - 'payload_type': 'image', - 'transport': 'direct', - 'encoding': 'base64', - 'size': len(image_data), - 'data': base64.b64encode(image_data).decode('ascii'), - 'metadata': {'payload_bytes': len(image_data)} - }, - { - 'id': 'payload-2', - 'dataname': 'audio', - 'payload_type': 'audio', - 'transport': 'direct', - 'encoding': 'base64', - 'size': len(audio_data), - 'data': base64.b64encode(audio_data).decode('ascii'), - 'metadata': {'payload_bytes': len(audio_data)} - }, - { - 'id': 'payload-3', - 'dataname': 'video', - 'payload_type': 'video', - 'transport': 'direct', - 'encoding': 'base64', - 'size': len(video_data), - 'data': base64.b64encode(video_data).decode('ascii'), - 'metadata': {'payload_bytes': len(video_data)} - }, - { - 'id': 'payload-4', - 'dataname': 'binary', - 'payload_type': 'binary', - 'transport': 'direct', - 'encoding': 'base64', - 'size': len(generic_binary), - 'data': base64.b64encode(generic_binary).decode('ascii'), - 'metadata': {'payload_bytes': len(generic_binary)} - } - ] - } - - mock_msg = { - 'payload': json.dumps(test_data) - } - - print('Mock Message Created:') - print(f' Correlation ID: {test_data["correlation_id"]}') - print(f' Payloads: {len(test_data["payloads"])}') - print(f' Payload types: {", ".join(p["payload_type"] for p in test_data["payloads"])}\n') - - try: - # Receive and process the message - print('Receiving and processing message...') - env = await smartreceive( - mock_msg, - max_retries=3, - base_delay=100, - max_delay=1000 - ) - - print('\n=== Received Envelope ===') - print(f'Correlation ID: {env["correlation_id"]}') - print(f'Message ID: {env["msg_id"]}') - print(f'Timestamp: {env["timestamp"]}') - print(f'Subject: {env["send_to"]}') - print(f'Payloads: {len(env["payloads"])}\n') - - # Validate received data - print('=== Validation ===') - passed = True - - if not env.get('correlation_id'): - print('❌ correlation_id is missing') - passed = False - else: - print('✅ correlation_id present') - - if len(env['payloads']) != 4: - print(f'❌ Expected 4 payloads, got {len(env["payloads"])}') - passed = False - else: - print('✅ Correct number of payloads') - - # Expected data - expected_data = [ - ('image', image_data, 'image'), - ('audio', audio_data, 'audio'), - ('video', video_data, 'video'), - ('binary', generic_binary, 'binary') - ] - - for i in range(len(env['payloads'])): - payload = env['payloads'][i] - expected = expected_data[i] - - if payload[0] != expected[0]: - print(f"❌ Payload {i + 1}: Expected dataname '{expected[0]}', got '{payload[0]}'") - passed = False - else: - print(f'✅ Payload {i + 1}: Correct dataname') - - if payload[2] != expected[2]: - print(f"❌ Payload {i + 1}: Expected type '{expected[2]}', got '{payload[2]}'") - passed = False - else: - print(f'✅ Payload {i + 1}: Correct type') - - # Verify binary data integrity - received_data = payload[1] - if not isinstance(received_data, (bytes, bytearray)): - print(f'❌ Payload {i + 1}: Expected bytes/bytearray, got {type(received_data)}') - passed = False - elif received_data != expected[1]: - print(f'❌ Payload {i + 1}: Data mismatch') - print(f' Expected: {expected[1]}') - print(f' Got: {received_data}') - passed = False - else: - print(f'✅ Payload {i + 1}: Data correctly deserialized') - - # Final result - print('\n=== Test Result ===') - if passed: - print('✅ ALL TESTS PASSED') - sys.exit(0) - else: - print('❌ SOME TESTS FAILED') - sys.exit(1) - - except Exception as e: - print(f'❌ Test failed with error: {e}') - import traceback - traceback.print_exc() - sys.exit(1) - - -if __name__ == '__main__': - asyncio.run(run_test()) \ No newline at end of file diff --git a/test/test_py_binary_sender.py b/test/test_py_binary_sender.py deleted file mode 100644 index 74de656..0000000 --- a/test/test_py_binary_sender.py +++ /dev/null @@ -1,183 +0,0 @@ -""" -Python Binary Sender Test -Tests the smartsend function with binary/image/audio/video/table payloads -""" - -import asyncio -import sys -import os -import base64 - -# Add parent directory to path -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from natsbridge import smartsend, DEFAULT_BROKER_URL, DEFAULT_FILESERVER_URL - -TEST_SUBJECT = '/test/binary' -TEST_BROKER_URL = os.environ.get('NATS_URL', 'nats://localhost:4222') -TEST_FILESERVER_URL = os.environ.get('FILESERVER_URL', 'http://localhost:8080') - - -async def run_test(): - print('=== Python Binary Sender Test ===\n') - - correlation_id = 'py-binary-test-' + str(asyncio.get_event_loop().time() * 1000000) - print(f'Correlation ID: {correlation_id}') - print(f'Subject: {TEST_SUBJECT}') - print(f'Broker URL: {TEST_BROKER_URL}\n') - - # Test data - binary data for different types - image_data = bytes([0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]) # PNG header - audio_data = bytes([0x46, 0x4C, 0x41, 0x43, 0x00, 0x00, 0x00, 0x00]) # FLAC header - video_data = bytes([0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70]) # MP4 header - generic_binary = bytes([0xDE, 0xAD, 0xBE, 0xEF, 0xCA, 0xFE, 0xBA, 0xBE]) - - # Test table data - try: - import pandas as pd - table_data = pd.DataFrame({ - 'id': [1, 2, 3, 4, 5], - 'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'], - 'value': [10.5, 20.3, 30.1, 40.9, 50.7] - }) - table_available = True - except ImportError: - table_available = False - table_data = None - - test_data = [ - ('image', image_data, 'image'), - ('audio', audio_data, 'audio'), - ('video', video_data, 'video'), - ('binary', generic_binary, 'binary') - ] - - if table_available: - test_data.append(('table', table_data, 'table')) - - try: - # Send the message - print('Sending binary payloads...') - env, env_json_str = await smartsend( - TEST_SUBJECT, - test_data, - broker_url=TEST_BROKER_URL, - fileserver_url=TEST_FILESERVER_URL, - correlation_id=correlation_id, - msg_purpose='test', - sender_name='py-binary-test', - is_publish=False - ) - - print('\n=== Envelope Created ===') - print(f'Correlation ID: {env["correlation_id"]}') - print(f'Message ID: {env["msg_id"]}') - print(f'Timestamp: {env["timestamp"]}') - print(f'Subject: {env["send_to"]}') - print(f'Purpose: {env["msg_purpose"]}') - print(f'Sender: {env["sender_name"]}') - print(f'Payloads: {len(env["payloads"])}\n') - - # Validate envelope structure - print('=== Validation ===') - passed = True - - expected_count = 5 if table_available else 4 - if len(env['payloads']) != expected_count: - print(f'❌ Expected {expected_count} payloads, got {len(env["payloads"])}') - passed = False - else: - print('✅ Correct number of payloads') - - # Test each payload - expected_datanames = ['image', 'audio', 'video', 'binary'] - expected_types = ['image', 'audio', 'video', 'binary'] - expected_data = [image_data, audio_data, video_data, generic_binary] - - if table_available: - expected_datanames.append('table') - expected_types.append('table') - - for i in range(len(env['payloads'])): - payload = env['payloads'][i] - - if payload['dataname'] != expected_datanames[i]: - print(f"❌ Payload {i + 1}: Expected dataname '{expected_datanames[i]}', got '{payload['dataname']}'") - passed = False - else: - print(f'✅ Payload {i + 1}: Correct dataname') - - if payload['payload_type'] != expected_types[i]: - print(f"❌ Payload {i + 1}: Expected type '{expected_types[i]}', got '{payload['payload_type']}'") - passed = False - else: - print(f'✅ Payload {i + 1}: Correct type') - - if payload['transport'] != 'direct': - print(f"❌ Payload {i + 1}: Expected transport 'direct', got '{payload['transport']}'") - passed = False - else: - print(f'✅ Payload {i + 1}: Correct transport') - - if payload['encoding'] != 'base64': - print(f"❌ Payload {i + 1}: Expected encoding 'base64', got '{payload['encoding']}'") - passed = False - else: - print(f'✅ Payload {i + 1}: Correct encoding') - - # Decode and verify the data - decoded_data = base64.b64decode(payload['data']) - - if i < len(expected_data): - original_data = expected_data[i] - if decoded_data != original_data: - print(f'❌ Payload {i + 1}: Data integrity mismatch') - passed = False - else: - print(f'✅ Payload {i + 1}: Data integrity verified') - else: - # Table payload - just verify it's present - print(f'✅ Payload {i + 1}: Table data present (size: {payload["size"]} bytes)') - - print(f' Size: {payload["size"]} bytes\n') - - # Test with larger binary data - print('=== Large Binary Data Test ===') - large_data = bytes([0xFF] * 10000) # 10KB of binary data - large_test_data = [ - ('large_binary', large_data, 'binary') - ] - - large_env, _ = await smartsend( - TEST_SUBJECT, - large_test_data, - broker_url=TEST_BROKER_URL, - fileserver_url=TEST_FILESERVER_URL, - correlation_id='large-' + correlation_id, - is_publish=False - ) - - if len(large_env['payloads']) == 1 and large_env['payloads'][0]['size'] == 10000: - print('✅ Large binary data handled correctly') - else: - print('❌ Large binary data handling failed') - passed = False - - # Final result - print('\n=== Test Result ===') - if passed: - print('✅ ALL TESTS PASSED') - sys.exit(0) - else: - print('❌ SOME TESTS FAILED') - sys.exit(1) - - except Exception as e: - print(f'❌ Test failed with error: {e}') - import traceback - traceback.print_exc() - sys.exit(1) - - -if __name__ == '__main__': - asyncio.run(run_test()) \ No newline at end of file diff --git a/test/test_py_dictionary_receiver.py b/test/test_py_dictionary_receiver.py deleted file mode 100644 index 293b3cb..0000000 --- a/test/test_py_dictionary_receiver.py +++ /dev/null @@ -1,220 +0,0 @@ -""" -Python Dictionary Receiver Test -Tests the smartreceive function with dictionary payloads -""" - -import asyncio -import sys -import os -import json - -# Add parent directory to path -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from natsbridge import smartreceive, DEFAULT_BROKER_URL, DEFAULT_FILESERVER_URL - -TEST_BROKER_URL = os.environ.get('NATS_URL', 'nats://localhost:4222') -TEST_FILESERVER_URL = os.environ.get('FILESERVER_URL', 'http://localhost:8080') - - -async def run_test(): - print('=== Python Dictionary Receiver Test ===\n') - - # Create a mock NATS message with dictionary payloads - import base64 - - simple_dict = {'key1': 'value1', 'key2': 'value2'} - nested_dict = {'outer': {'inner': 'value', 'number': 42}} - array_dict = {'items': [1, 2, 3, 'four', 'five']} - mixed_dict = {'string': 'text', 'number': 123, 'boolean': True, 'null_val': None} - - test_data = { - 'correlation_id': 'py-receiver-dict-' + str(asyncio.get_event_loop().time() * 1000000), - 'msg_id': 'msg-' + str(asyncio.get_event_loop().time() * 1000000), - 'timestamp': asyncio.get_event_loop().time().isoformat(), - 'send_to': '/test/dictionary', - 'msg_purpose': 'test', - 'sender_name': 'py-dict-test', - 'sender_id': 'sender-' + str(asyncio.get_event_loop().time() * 1000000), - 'receiver_name': 'py-receiver', - 'receiver_id': 'receiver-' + str(asyncio.get_event_loop().time() * 1000000), - 'reply_to': '', - 'reply_to_msg_id': '', - 'broker_url': TEST_BROKER_URL, - 'metadata': {}, - 'payloads': [ - { - 'id': 'payload-1', - 'dataname': 'simple_dict', - 'payload_type': 'dictionary', - 'transport': 'direct', - 'encoding': 'base64', - 'size': len(json.dumps(simple_dict).encode('utf8')), - 'data': base64.b64encode(json.dumps(simple_dict).encode('utf8')).decode('ascii'), - 'metadata': {'payload_bytes': len(json.dumps(simple_dict).encode('utf8'))} - }, - { - 'id': 'payload-2', - 'dataname': 'nested_dict', - 'payload_type': 'dictionary', - 'transport': 'direct', - 'encoding': 'base64', - 'size': len(json.dumps(nested_dict).encode('utf8')), - 'data': base64.b64encode(json.dumps(nested_dict).encode('utf8')).decode('ascii'), - 'metadata': {'payload_bytes': len(json.dumps(nested_dict).encode('utf8'))} - }, - { - 'id': 'payload-3', - 'dataname': 'array_dict', - 'payload_type': 'dictionary', - 'transport': 'direct', - 'encoding': 'base64', - 'size': len(json.dumps(array_dict).encode('utf8')), - 'data': base64.b64encode(json.dumps(array_dict).encode('utf8')).decode('ascii'), - 'metadata': {'payload_bytes': len(json.dumps(array_dict).encode('utf8'))} - }, - { - 'id': 'payload-4', - 'dataname': 'mixed_dict', - 'payload_type': 'dictionary', - 'transport': 'direct', - 'encoding': 'base64', - 'size': len(json.dumps(mixed_dict).encode('utf8')), - 'data': base64.b64encode(json.dumps(mixed_dict).encode('utf8')).decode('ascii'), - 'metadata': {'payload_bytes': len(json.dumps(mixed_dict).encode('utf8'))} - } - ] - } - - mock_msg = { - 'payload': json.dumps(test_data) - } - - print('Mock Message Created:') - print(f' Correlation ID: {test_data["correlation_id"]}') - print(f' Payloads: {len(test_data["payloads"])}') - print(f' Payload types: {", ".join(p["payload_type"] for p in test_data["payloads"])}\n') - - try: - # Receive and process the message - print('Receiving and processing message...') - env = await smartreceive( - mock_msg, - max_retries=3, - base_delay=100, - max_delay=1000 - ) - - print('\n=== Received Envelope ===') - print(f'Correlation ID: {env["correlation_id"]}') - print(f'Message ID: {env["msg_id"]}') - print(f'Timestamp: {env["timestamp"]}') - print(f'Subject: {env["send_to"]}') - print(f'Payloads: {len(env["payloads"])}\n') - - # Validate received data - print('=== Validation ===') - passed = True - - if not env.get('correlation_id'): - print('❌ correlation_id is missing') - passed = False - else: - print('✅ correlation_id present') - - if len(env['payloads']) != 4: - print(f'❌ Expected 4 payloads, got {len(env["payloads"])}') - passed = False - else: - print('✅ Correct number of payloads') - - # Expected data - expected_data = [ - ('simple_dict', simple_dict, 'dictionary'), - ('nested_dict', nested_dict, 'dictionary'), - ('array_dict', array_dict, 'dictionary'), - ('mixed_dict', mixed_dict, 'dictionary') - ] - - for i in range(len(env['payloads'])): - payload = env['payloads'][i] - expected = expected_data[i] - - if payload[0] != expected[0]: - print(f"❌ Payload {i + 1}: Expected dataname '{expected[0]}', got '{payload[0]}'") - passed = False - else: - print(f'✅ Payload {i + 1}: Correct dataname') - - if payload[2] != expected[2]: - print(f"❌ Payload {i + 1}: Expected type '{expected[2]}', got '{payload[2]}'") - passed = False - else: - print(f'✅ Payload {i + 1}: Correct type') - - data_match = json.dumps(payload[1], sort_keys=True) == json.dumps(expected[1], sort_keys=True) - if not data_match: - print(f'❌ Payload {i + 1}: Data mismatch') - print(f' Expected: {json.dumps(expected[1], sort_keys=True)}') - print(f' Got: {json.dumps(payload[1], sort_keys=True)}') - passed = False - else: - print(f'✅ Payload {i + 1}: Data correctly deserialized') - - # Test round-trip with receive - print('\n=== Round-trip Test ===') - round_trip_data = { - 'correlation_id': 'roundtrip-' + str(asyncio.get_event_loop().time() * 1000000), - 'msg_id': 'msg-' + str(asyncio.get_event_loop().time() * 1000000), - 'timestamp': asyncio.get_event_loop().time().isoformat(), - 'send_to': '/test/dictionary', - 'msg_purpose': 'test', - 'sender_name': 'py-test', - 'sender_id': 'sender-' + str(asyncio.get_event_loop().time() * 1000000), - 'receiver_name': 'py-receiver', - 'receiver_id': 'receiver-' + str(asyncio.get_event_loop().time() * 1000000), - 'reply_to': '', - 'reply_to_msg_id': '', - 'broker_url': TEST_BROKER_URL, - 'metadata': {}, - 'payloads': [ - { - 'id': 'payload-rt', - 'dataname': 'roundtrip', - 'payload_type': 'dictionary', - 'transport': 'direct', - 'encoding': 'base64', - 'size': len(json.dumps({'test': 'data', 'nested': {'a': 1, 'b': 2}}).encode('utf8')), - 'data': base64.b64encode(json.dumps({'test': 'data', 'nested': {'a': 1, 'b': 2}}).encode('utf8')).decode('ascii'), - 'metadata': {'payload_bytes': len(json.dumps({'test': 'data', 'nested': {'a': 1, 'b': 2}}).encode('utf8'))} - } - ] - } - - mock_rt_msg = {'payload': json.dumps(round_trip_data)} - rt_env = await smartreceive(mock_rt_msg) - - if rt_env['payloads'][0][0] == 'roundtrip' and rt_env['payloads'][0][2] == 'dictionary': - print('✅ Round-trip test successful') - else: - print('❌ Round-trip test failed') - passed = False - - # Final result - print('\n=== Test Result ===') - if passed: - print('✅ ALL TESTS PASSED') - sys.exit(0) - else: - print('❌ SOME TESTS FAILED') - sys.exit(1) - - except Exception as e: - print(f'❌ Test failed with error: {e}') - import traceback - traceback.print_exc() - sys.exit(1) - - -if __name__ == '__main__': - asyncio.run(run_test()) \ No newline at end of file diff --git a/test/test_py_dictionary_sender.py b/test/test_py_dictionary_sender.py deleted file mode 100644 index b8027cc..0000000 --- a/test/test_py_dictionary_sender.py +++ /dev/null @@ -1,172 +0,0 @@ -""" -Python Dictionary Sender Test -Tests the smartsend function with dictionary payloads -""" - -import asyncio -import sys -import os -import json - -# Add parent directory to path -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from natsbridge import smartsend, DEFAULT_BROKER_URL, DEFAULT_FILESERVER_URL - -TEST_SUBJECT = '/test/dictionary' -TEST_BROKER_URL = os.environ.get('NATS_URL', 'nats://localhost:4222') -TEST_FILESERVER_URL = os.environ.get('FILESERVER_URL', 'http://localhost:8080') - - -async def run_test(): - print('=== Python Dictionary Sender Test ===\n') - - correlation_id = 'py-dict-test-' + str(asyncio.get_event_loop().time() * 1000000) - print(f'Correlation ID: {correlation_id}') - print(f'Subject: {TEST_SUBJECT}') - print(f'Broker URL: {TEST_BROKER_URL}\n') - - # Test data - various dictionary structures - test_data = [ - ('simple_dict', {'key1': 'value1', 'key2': 'value2'}, 'dictionary'), - ('nested_dict', {'outer': {'inner': 'value', 'number': 42}}, 'dictionary'), - ('array_dict', {'items': [1, 2, 3, 'four', 'five']}, 'dictionary'), - ('mixed_dict', {'string': 'text', 'number': 123, 'boolean': True, 'null_val': None}, 'dictionary') - ] - - try: - # Send the message - print('Sending dictionary payloads...') - env, env_json_str = await smartsend( - TEST_SUBJECT, - test_data, - broker_url=TEST_BROKER_URL, - fileserver_url=TEST_FILESERVER_URL, - correlation_id=correlation_id, - msg_purpose='test', - sender_name='py-dict-test', - is_publish=False - ) - - print('\n=== Envelope Created ===') - print(f'Correlation ID: {env["correlation_id"]}') - print(f'Message ID: {env["msg_id"]}') - print(f'Timestamp: {env["timestamp"]}') - print(f'Subject: {env["send_to"]}') - print(f'Purpose: {env["msg_purpose"]}') - print(f'Sender: {env["sender_name"]}') - print(f'Payloads: {len(env["payloads"])}\n') - - # Validate envelope structure - print('=== Validation ===') - passed = True - - if len(env['payloads']) != 4: - print(f'❌ Expected 4 payloads, got {len(env["payloads"])}') - passed = False - else: - print('✅ Correct number of payloads') - - # Test each payload - expected_datanames = ['simple_dict', 'nested_dict', 'array_dict', 'mixed_dict'] - expected_types = ['dictionary', 'dictionary', 'dictionary', 'dictionary'] - - for i in range(len(env['payloads'])): - payload = env['payloads'][i] - - if payload['dataname'] != expected_datanames[i]: - print(f"❌ Payload {i + 1}: Expected dataname '{expected_datanames[i]}', got '{payload['dataname']}'") - passed = False - else: - print(f'✅ Payload {i + 1}: Correct dataname') - - if payload['payload_type'] != expected_types[i]: - print(f"❌ Payload {i + 1}: Expected type '{expected_types[i]}', got '{payload['payload_type']}'") - passed = False - else: - print(f'✅ Payload {i + 1}: Correct type') - - if payload['transport'] != 'direct': - print(f"❌ Payload {i + 1}: Expected transport 'direct', got '{payload['transport']}'") - passed = False - else: - print(f'✅ Payload {i + 1}: Correct transport') - - if payload['encoding'] != 'base64': - print(f"❌ Payload {i + 1}: Expected encoding 'base64', got '{payload['encoding']}'") - passed = False - else: - print(f'✅ Payload {i + 1}: Correct encoding') - - # Decode and verify the data - import base64 - decoded_data = json.loads(base64.b64decode(payload['data']).decode('utf8')) - original_data = test_data[i][1] - - # Normalize for comparison (None vs null, True vs true, etc.) - if json.dumps(decoded_data, sort_keys=True) != json.dumps(original_data, sort_keys=True): - print(f'❌ Payload {i + 1}: Data integrity mismatch') - print(f' Expected: {json.dumps(original_data)}') - print(f' Got: {json.dumps(decoded_data)}') - passed = False - else: - print(f'✅ Payload {i + 1}: Data integrity verified') - - print(f' Size: {payload["size"]} bytes\n') - - # Test round-trip serialization - print('=== Round-trip Serialization Test ===') - round_trip_data = [ - ('roundtrip', {'test': 'data', 'numbers': [1, 2, 3], 'nested': {'a': 1, 'b': 2}}, 'dictionary') - ] - - rt_env, _ = await smartsend( - TEST_SUBJECT, - round_trip_data, - broker_url=TEST_BROKER_URL, - fileserver_url=TEST_FILESERVER_URL, - correlation_id='roundtrip-' + correlation_id, - is_publish=False - ) - - rt_payload = rt_env['payloads'][0] - rt_decoded = json.loads(base64.b64decode(rt_payload['data']).decode('utf8')) - - if json.dumps(rt_decoded, sort_keys=True) == json.dumps(round_trip_data[0][1], sort_keys=True): - print('✅ Round-trip serialization successful') - else: - print('❌ Round-trip serialization failed') - passed = False - - # Test JSON string output - print('\n=== JSON String Output Test ===') - try: - parsed = json.loads(env_json_str) - if parsed['correlation_id'] == env['correlation_id'] and \ - len(parsed['payloads']) == len(env['payloads']): - print('✅ JSON string is valid and matches envelope') - else: - print('❌ JSON string does not match envelope') - passed = False - except json.JSONDecodeError as e: - print(f'❌ JSON string is invalid: {e}') - passed = False - - # Final result - print('\n=== Test Result ===') - if passed: - print('✅ ALL TESTS PASSED') - sys.exit(0) - else: - print('❌ SOME TESTS FAILED') - sys.exit(1) - - except Exception as e: - print(f'❌ Test failed with error: {e}') - import traceback - traceback.print_exc() - sys.exit(1) - - -if __name__ == '__main__': - asyncio.run(run_test()) \ No newline at end of file diff --git a/test/test_py_table_sender.py b/test/test_py_table_sender.py deleted file mode 100644 index 724260b..0000000 --- a/test/test_py_table_sender.py +++ /dev/null @@ -1,167 +0,0 @@ -""" -Python Table Sender Test -Tests the smartsend function with table (Arrow IPC) payloads -""" - -import asyncio -import sys -import os - -# Add parent directory to path -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from natsbridge import smartsend, DEFAULT_BROKER_URL, DEFAULT_FILESERVER_URL - -TEST_SUBJECT = '/test/table' -TEST_BROKER_URL = os.environ.get('NATS_URL', 'nats://localhost:4222') -TEST_FILESERVER_URL = os.environ.get('FILESERVER_URL', 'http://localhost:8080') - - -async def run_test(): - print('=== Python Table Sender Test ===\n') - - correlation_id = 'py-table-test-' + str(asyncio.get_event_loop().time() * 1000000) - print(f'Correlation ID: {correlation_id}') - print(f'Subject: {TEST_SUBJECT}') - print(f'Broker URL: {TEST_BROKER_URL}\n') - - # Test data - pandas DataFrame - try: - import pandas as pd - table_data = pd.DataFrame({ - 'id': [1, 2, 3, 4, 5], - 'name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'], - 'age': [30, 25, 35, 28, 32], - 'active': [True, False, True, True, False] - }) - table_available = True - except ImportError: - print('❌ pandas not available - skipping table tests') - sys.exit(0) - - test_data = [ - ('users_table', table_data, 'table') - ] - - try: - # Send the message - print('Sending table payload...') - env, env_json_str = await smartsend( - TEST_SUBJECT, - test_data, - broker_url=TEST_BROKER_URL, - fileserver_url=TEST_FILESERVER_URL, - correlation_id=correlation_id, - msg_purpose='test', - sender_name='py-table-test', - is_publish=False - ) - - print('\n=== Envelope Created ===') - print(f'Correlation ID: {env["correlation_id"]}') - print(f'Message ID: {env["msg_id"]}') - print(f'Timestamp: {env["timestamp"]}') - print(f'Subject: {env["send_to"]}') - print(f'Purpose: {env["msg_purpose"]}') - print(f'Sender: {env["sender_name"]}') - print(f'Payloads: {len(env["payloads"])}\n') - - # Validate envelope structure - print('=== Validation ===') - passed = True - - if len(env['payloads']) != 1: - print(f'❌ Expected 1 payload, got {len(env["payloads"])}') - passed = False - else: - print('✅ Correct number of payloads') - - payload = env['payloads'][0] - if payload['dataname'] != 'users_table': - print(f"❌ Expected dataname 'users_table', got '{payload['dataname']}'") - passed = False - else: - print('✅ Correct dataname') - - if payload['payload_type'] != 'table': - print(f"❌ Expected payload_type 'table', got '{payload['payload_type']}'") - passed = False - else: - print('✅ Correct payload_type') - - if payload['transport'] != 'direct': - print(f"❌ Expected transport 'direct', got '{payload['transport']}'") - passed = False - else: - print('✅ Correct transport') - - if payload['encoding'] != 'base64': - print(f"❌ Expected encoding 'base64', got '{payload['encoding']}'") - passed = False - else: - print('✅ Correct encoding') - - print(f'\nPayload size: {payload["size"]} bytes') - - # Test with larger table - print('\n=== Larger Table Test ===') - large_table_data = pd.DataFrame({ - 'id': range(100), - 'name': [f'User{i}' for i in range(100)], - 'age': [20 + (i % 50) for i in range(100)], - 'active': [i % 2 == 0 for i in range(100)] - }) - - large_test_data = [ - ('large_table', large_table_data, 'table') - ] - - large_env, _ = await smartsend( - TEST_SUBJECT, - large_test_data, - broker_url=TEST_BROKER_URL, - fileserver_url=TEST_FILESERVER_URL, - correlation_id='large-' + correlation_id, - is_publish=False - ) - - if len(large_env['payloads']) == 1: - print('✅ Large table handled correctly') - print(f' Size: {large_env["payloads"][0]["size"]} bytes') - else: - print('❌ Large table handling failed') - passed = False - - # Test JSON string output - print('\n=== JSON String Output Test ===') - import json - try: - parsed = json.loads(env_json_str) - if parsed['correlation_id'] == env['correlation_id'] and \ - len(parsed['payloads']) == len(env['payloads']): - print('✅ JSON string is valid and matches envelope') - else: - print('❌ JSON string does not match envelope') - passed = False - except json.JSONDecodeError as e: - print(f'❌ JSON string is invalid: {e}') - passed = False - - # Final result - print('\n=== Test Result ===') - if passed: - print('✅ ALL TESTS PASSED') - sys.exit(0) - else: - print('❌ SOME TESTS FAILED') - sys.exit(1) - - except Exception as e: - print(f'❌ Test failed with error: {e}') - import traceback - traceback.print_exc() - sys.exit(1) - - -if __name__ == '__main__': - asyncio.run(run_test()) \ No newline at end of file diff --git a/test/test_py_text_receiver.py b/test/test_py_text_receiver.py deleted file mode 100644 index c22493f..0000000 --- a/test/test_py_text_receiver.py +++ /dev/null @@ -1,205 +0,0 @@ -""" -Python Text Receiver Test -Tests the smartreceive function with text payloads -""" - -import asyncio -import sys -import os -import json - -# Add parent directory to path -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from natsbridge import smartreceive, DEFAULT_BROKER_URL, DEFAULT_FILESERVER_URL - -TEST_BROKER_URL = os.environ.get('NATS_URL', 'nats://localhost:4222') -TEST_FILESERVER_URL = os.environ.get('FILESERVER_URL', 'http://localhost:8080') - - -async def run_test(): - print('=== Python Text Receiver Test ===\n') - - # Create a mock NATS message with text payload - test_text = 'Hello, NATSBridge! This is a test message.' - import base64 - - test_data = { - 'correlation_id': 'py-receiver-test-' + str(asyncio.get_event_loop().time() * 1000000), - 'msg_id': 'msg-' + str(asyncio.get_event_loop().time() * 1000000), - 'timestamp': asyncio.get_event_loop().time().isoformat(), - 'send_to': '/test/text', - 'msg_purpose': 'test', - 'sender_name': 'py-text-test', - 'sender_id': 'sender-' + str(asyncio.get_event_loop().time() * 1000000), - 'receiver_name': 'py-receiver', - 'receiver_id': 'receiver-' + str(asyncio.get_event_loop().time() * 1000000), - 'reply_to': '', - 'reply_to_msg_id': '', - 'broker_url': TEST_BROKER_URL, - 'metadata': {}, - 'payloads': [ - { - 'id': 'payload-' + str(asyncio.get_event_loop().time() * 1000000), - 'dataname': 'message', - 'payload_type': 'text', - 'transport': 'direct', - 'encoding': 'base64', - 'size': len(test_text.encode('utf8')), - 'data': base64.b64encode(test_text.encode('utf8')).decode('ascii'), - 'metadata': {'payload_bytes': len(test_text.encode('utf8'))} - } - ] - } - - mock_msg = { - 'payload': json.dumps(test_data) - } - - print('Mock Message Created:') - print(f' Correlation ID: {test_data["correlation_id"]}') - print(f' Payloads: {len(test_data["payloads"])}') - print(f' Payload dataname: {test_data["payloads"][0]["dataname"]}') - print(f' Payload type: {test_data["payloads"][0]["payload_type"]}') - print(f' Transport: {test_data["payloads"][0]["transport"]}\n') - - try: - # Receive and process the message - print('Receiving and processing message...') - env = await smartreceive( - mock_msg, - max_retries=3, - base_delay=100, - max_delay=1000 - ) - - print('\n=== Received Envelope ===') - print(f'Correlation ID: {env["correlation_id"]}') - print(f'Message ID: {env["msg_id"]}') - print(f'Timestamp: {env["timestamp"]}') - print(f'Subject: {env["send_to"]}') - print(f'Payloads: {len(env["payloads"])}\n') - - # Validate received data - print('=== Validation ===') - passed = True - - if not env.get('correlation_id'): - print('❌ correlation_id is missing') - passed = False - else: - print('✅ correlation_id present') - - if len(env['payloads']) != 1: - print(f'❌ Expected 1 payload, got {len(env["payloads"])}') - passed = False - else: - print('✅ Correct number of payloads') - - payload = env['payloads'][0] - if payload[0] != 'message': - print(f"❌ Expected dataname 'message', got '{payload[0]}'") - passed = False - else: - print('✅ Correct dataname') - - if payload[2] != 'text': - print(f"❌ Expected type 'text', got '{payload[2]}'") - passed = False - else: - print('✅ Correct type') - - if payload[1] != test_text: - print('❌ Data mismatch') - print(f' Expected: {test_text}') - print(f' Got: {payload[1]}') - passed = False - else: - print('✅ Data correctly deserialized') - - # Test with multiple text payloads - print('\n=== Multiple Text Payloads Test ===') - multi_test_data = { - 'correlation_id': 'multi-receiver-' + str(asyncio.get_event_loop().time() * 1000000), - 'msg_id': 'msg-' + str(asyncio.get_event_loop().time() * 1000000), - 'timestamp': asyncio.get_event_loop().time().isoformat(), - 'send_to': '/test/text', - 'msg_purpose': 'test', - 'sender_name': 'py-text-test', - 'sender_id': 'sender-' + str(asyncio.get_event_loop().time() * 1000000), - 'receiver_name': 'py-receiver', - 'receiver_id': 'receiver-' + str(asyncio.get_event_loop().time() * 1000000), - 'reply_to': '', - 'reply_to_msg_id': '', - 'broker_url': TEST_BROKER_URL, - 'metadata': {}, - 'payloads': [ - { - 'id': 'payload-1', - 'dataname': 'msg1', - 'payload_type': 'text', - 'transport': 'direct', - 'encoding': 'base64', - 'size': 16, - 'data': base64.b64encode(b'First message').decode('ascii'), - 'metadata': {'payload_bytes': 16} - }, - { - 'id': 'payload-2', - 'dataname': 'msg2', - 'payload_type': 'text', - 'transport': 'direct', - 'encoding': 'base64', - 'size': 16, - 'data': base64.b64encode(b'Second message').decode('ascii'), - 'metadata': {'payload_bytes': 16} - }, - { - 'id': 'payload-3', - 'dataname': 'msg3', - 'payload_type': 'text', - 'transport': 'direct', - 'encoding': 'base64', - 'size': 16, - 'data': base64.b64encode(b'Third message').decode('ascii'), - 'metadata': {'payload_bytes': 16} - } - ] - } - - mock_multi_msg = {'payload': json.dumps(multi_test_data)} - multi_env = await smartreceive(mock_multi_msg) - - if len(multi_env['payloads']) == 3: - print('✅ Multiple payloads handled correctly') - - # Verify each payload - expected_messages = ['First message', 'Second message', 'Third message'] - for i in range(3): - if multi_env['payloads'][i][1] == expected_messages[i]: - print(f'✅ Payload {i + 1} correctly deserialized') - else: - print(f'❌ Payload {i + 1} mismatch') - passed = False - else: - print(f"❌ Expected 3 payloads, got {len(multi_env['payloads'])}") - passed = False - - # Final result - print('\n=== Test Result ===') - if passed: - print('✅ ALL TESTS PASSED') - sys.exit(0) - else: - print('❌ SOME TESTS FAILED') - sys.exit(1) - - except Exception as e: - print(f'❌ Test failed with error: {e}') - import traceback - traceback.print_exc() - sys.exit(1) - - -if __name__ == '__main__': - asyncio.run(run_test()) \ No newline at end of file diff --git a/test/test_py_text_sender.py b/test/test_py_text_sender.py deleted file mode 100644 index ac86044..0000000 --- a/test/test_py_text_sender.py +++ /dev/null @@ -1,164 +0,0 @@ -""" -Python Text Sender Test -Tests the smartsend function with text payloads -""" - -import asyncio -import sys -import os - -# Add parent directory to path -sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) - -from natsbridge import smartsend, DEFAULT_BROKER_URL, DEFAULT_FILESERVER_URL - -TEST_SUBJECT = '/test/text' -TEST_BROKER_URL = os.environ.get('NATS_URL', 'nats://localhost:4222') -TEST_FILESERVER_URL = os.environ.get('FILESERVER_URL', 'http://localhost:8080') - - -async def run_test(): - print('=== Python Text Sender Test ===\n') - - correlation_id = 'py-text-test-' + str(asyncio.get_event_loop().time() * 1000000) - print(f'Correlation ID: {correlation_id}') - print(f'Subject: {TEST_SUBJECT}') - print(f'Broker URL: {TEST_BROKER_URL}\n') - - # Test data - text_data = 'Hello, NATSBridge! This is a test message.' - test_data = [ - ('message', text_data, 'text') - ] - - try: - # Send the message - print('Sending text payload...') - env, env_json_str = await smartsend( - TEST_SUBJECT, - test_data, - broker_url=TEST_BROKER_URL, - fileserver_url=TEST_FILESERVER_URL, - correlation_id=correlation_id, - msg_purpose='test', - sender_name='py-text-test', - is_publish=False # Don't actually publish for this test - ) - - print('\n=== Envelope Created ===') - print(f'Correlation ID: {env["correlation_id"]}') - print(f'Message ID: {env["msg_id"]}') - print(f'Timestamp: {env["timestamp"]}') - print(f'Subject: {env["send_to"]}') - print(f'Purpose: {env["msg_purpose"]}') - print(f'Sender: {env["sender_name"]}') - print(f'Payloads: {len(env["payloads"])}\n') - - # Validate envelope structure - print('=== Validation ===') - passed = True - - if not env.get('correlation_id'): - print('❌ correlation_id is missing') - passed = False - else: - print('✅ correlation_id present') - - if not env.get('msg_id'): - print('❌ msg_id is missing') - passed = False - else: - print('✅ msg_id present') - - if not env.get('timestamp'): - print('❌ timestamp is missing') - passed = False - else: - print('✅ timestamp present') - - if len(env['payloads']) != 1: - print(f'❌ Expected 1 payload, got {len(env["payloads"])}') - passed = False - else: - print('✅ Correct number of payloads') - - payload = env['payloads'][0] - if payload['dataname'] != 'message': - print(f"❌ Expected dataname 'message', got '{payload['dataname']}'") - passed = False - else: - print('✅ Correct dataname') - - if payload['payload_type'] != 'text': - print(f"❌ Expected payload_type 'text', got '{payload['payload_type']}'") - passed = False - else: - print('✅ Correct payload_type') - - if payload['transport'] != 'direct': - print(f"❌ Expected transport 'direct', got '{payload['transport']}'") - passed = False - else: - print('✅ Correct transport') - - if payload['encoding'] != 'base64': - print(f"❌ Expected encoding 'base64', got '{payload['encoding']}'") - passed = False - else: - print('✅ Correct encoding') - - # Decode and verify the data - import base64 - decoded_data = base64.b64decode(payload['data']).decode('utf8') - if decoded_data != text_data: - print('❌ Decoded data mismatch') - print(f' Expected: {text_data}') - print(f' Got: {decoded_data}') - passed = False - else: - print('✅ Data integrity verified') - - print(f"\nPayload size: {payload['size']} bytes") - print(f'Base64 data length: {len(payload["data"])} chars') - - # Test with multiple text payloads - print('\n=== Multiple Text Payloads Test ===') - multi_test_data = [ - ('msg1', 'First message', 'text'), - ('msg2', 'Second message', 'text'), - ('msg3', 'Third message', 'text') - ] - - multi_env, _ = await smartsend( - TEST_SUBJECT, - multi_test_data, - broker_url=TEST_BROKER_URL, - fileserver_url=TEST_FILESERVER_URL, - correlation_id='multi-test-' + correlation_id, - is_publish=False - ) - - if len(multi_env['payloads']) == 3: - print('✅ Multiple payloads handled correctly') - else: - print(f"❌ Expected 3 payloads, got {len(multi_env['payloads'])}") - passed = False - - # Final result - print('\n=== Test Result ===') - if passed: - print('✅ ALL TESTS PASSED') - sys.exit(0) - else: - print('❌ SOME TESTS FAILED') - sys.exit(1) - - except Exception as e: - print(f'❌ Test failed with error: {e}') - import traceback - traceback.print_exc() - sys.exit(1) - - -if __name__ == '__main__': - asyncio.run(run_test()) \ No newline at end of file