From 97bf1e47f4e45b0e286d49a3bc683ac118bc8fb8 Mon Sep 17 00:00:00 2001 From: narawat Date: Mon, 23 Feb 2026 06:58:16 +0700 Subject: [PATCH] update --- examples/walkthrough.md | 24 +- test/test_micropython_basic.py | 341 ++++++++++++------------ test/test_micropython_dict_receiver.py | 70 +++++ test/test_micropython_dict_sender.py | 99 +++++++ test/test_micropython_file_receiver.py | 65 +++++ test/test_micropython_file_sender.py | 79 ++++++ test/test_micropython_mixed_receiver.py | 97 +++++++ test/test_micropython_mixed_sender.py | 93 +++++++ test/test_micropython_text_receiver.py | 69 +++++ test/test_micropython_text_sender.py | 81 ++++++ 10 files changed, 829 insertions(+), 189 deletions(-) create mode 100644 test/test_micropython_dict_receiver.py create mode 100644 test/test_micropython_dict_sender.py create mode 100644 test/test_micropython_file_receiver.py create mode 100644 test/test_micropython_file_sender.py create mode 100644 test/test_micropython_mixed_receiver.py create mode 100644 test/test_micropython_mixed_sender.py create mode 100644 test/test_micropython_text_receiver.py create mode 100644 test/test_micropython_text_sender.py diff --git a/examples/walkthrough.md b/examples/walkthrough.md index 6897238..0e42bbb 100644 --- a/examples/walkthrough.md +++ b/examples/walkthrough.md @@ -38,22 +38,22 @@ Each section builds on the previous one, gradually increasing in complexity. ┌─────────────────────────────────────────────────────────────────┐ │ NATSBridge Architecture │ ├─────────────────────────────────────────────────────────────────┤ -│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ -│ │ Julia │ │ JavaScript │ │ Python/Micr │ │ -│ │ (NATS.jl) │◄──►│ (nats.js) │◄──►│ opython │ │ -│ └──────────────┘ └──────────────┘ └──────────────┘ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ Julia │ │ JavaScript │ │ Python/Micr │ │ +│ │ (NATS.jl) │◄──►│ (nats.js) │◄──►│ opython │ │ +│ └──────────────┘ └──────────────┘ └──────────────┘ │ │ │ │ │ │ │ ▼ ▼ ▼ │ -│ ┌──────────────────────────────────────────────┐ │ -│ │ NATS │ │ -│ │ (Message Broker) │ │ -│ └──────────────────────────────────────────────┘ │ +│ ┌──────────────────────────────────────────────┐ │ +│ │ NATS │ │ +│ │ (Message Broker) │ │ +│ └──────────────────────────────────────────────┘ │ │ │ │ │ ▼ │ -│ ┌──────────────────┐ │ -│ │ File Server │ │ -│ │ (HTTP Upload) │ │ -│ └──────────────────┘ │ +│ ┌──────────────────┐ │ +│ │ File Server │ │ +│ │ (HTTP Upload) │ │ +│ └──────────────────┘ │ └─────────────────────────────────────────────────────────────────┘ ``` diff --git a/test/test_micropython_basic.py b/test/test_micropython_basic.py index c5e6584..31884cb 100644 --- a/test/test_micropython_basic.py +++ b/test/test_micropython_basic.py @@ -1,220 +1,207 @@ +#!/usr/bin/env python3 """ -Micropython NATS Bridge - Basic Test Examples - -This module demonstrates basic usage of the NATSBridge for Micropython. +Basic functionality test for nats_bridge.py +Tests the core classes and functions without NATS connection """ import sys -sys.path.insert(0, "../src") +import os -from nats_bridge import MessageEnvelope, MessagePayload, smartsend, smartreceive, log_trace +# Add src to path for import +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) + +from nats_bridge import ( + MessagePayload, + MessageEnvelope, + smartsend, + smartreceive, + log_trace, + generate_uuid, + get_timestamp, + _serialize_data, + _deserialize_data +) import json -# ============================================= 100 ============================================== # - -def test_text_message(): - """Test sending and receiving text messages.""" - print("\n=== Test 1: Text Message ===") +def test_message_payload(): + """Test MessagePayload class""" + print("\n=== Testing MessagePayload ===") - # Send text message - data = [ - ("message", "Hello World", "text"), - ("greeting", "Good morning!", "text") - ] - - env = smartsend( - "/test/text", - data, - nats_url="nats://localhost:4222", - size_threshold=1000000 + # Test direct transport with text + payload1 = MessagePayload( + data="Hello World", + msg_type="text", + id="test-id-1", + dataname="message", + transport="direct", + encoding="base64", + size=11 ) - print("Sent envelope:") - print(" Subject: {}".format(env.send_to)) - print(" Correlation ID: {}".format(env.correlation_id)) - print(" Payloads: {}".format(len(env.payloads))) + assert payload1.id == "test-id-1" + assert payload1.dataname == "message" + assert payload1.type == "text" + assert payload1.transport == "direct" + assert payload1.encoding == "base64" + assert payload1.size == 11 + print(" [PASS] MessagePayload with text data") - # Expected output on receiver: - # envelope = smartreceive(msg) - # for dataname, data, type in envelope["payloads"]: - # print("Received {}: {}".format(dataname, data)) - - -def test_dictionary_message(): - """Test sending and receiving dictionary messages.""" - print("\n=== Test 2: Dictionary Message ===") - - # Send dictionary message - config = { - "step_size": 0.01, - "iterations": 1000, - "threshold": 0.5 - } - - data = [ - ("config", config, "dictionary") - ] - - env = smartsend( - "/test/dictionary", - data, - nats_url="nats://localhost:4222", - size_threshold=1000000 + # Test link transport with URL + payload2 = MessagePayload( + data="http://example.com/file.txt", + msg_type="binary", + id="test-id-2", + dataname="file", + transport="link", + encoding="none", + size=1000 ) - print("Sent envelope:") - print(" Subject: {}".format(env.send_to)) - print(" Payloads: {}".format(len(env.payloads))) + assert payload2.transport == "link" + assert payload2.data == "http://example.com/file.txt" + print(" [PASS] MessagePayload with link transport") - # Expected output on receiver: - # envelope = smartreceive(msg) - # for dataname, data, type in envelope["payloads"]: - # if type == "dictionary": - # print("Config: {}".format(data)) + # Test to_dict method + payload_dict = payload1.to_dict() + assert "id" in payload_dict + assert "dataname" in payload_dict + assert "type" in payload_dict + assert "transport" in payload_dict + assert "data" in payload_dict + print(" [PASS] MessagePayload.to_dict() method") -def test_mixed_payloads(): - """Test sending mixed payload types in a single message.""" - print("\n=== Test 3: Mixed Payloads ===") +def test_message_envelope(): + """Test MessageEnvelope class""" + print("\n=== Testing MessageEnvelope ===") - # Mixed content: text, dictionary, and binary - image_data = b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR" # PNG header (example) + # Create payloads + payload1 = MessagePayload("Hello", "text", id="p1", dataname="msg1") + payload2 = MessagePayload("http://example.com/file", "binary", id="p2", dataname="file", transport="link") - data = [ - ("message_text", "Hello!", "text"), - ("user_config", {"theme": "dark", "volume": 80}, "dictionary"), - ("user_image", image_data, "binary") - ] - - env = smartsend( - "/test/mixed", - data, - nats_url="nats://localhost:4222", - size_threshold=1000000 + # Create envelope + env = MessageEnvelope( + send_to="/test/subject", + payloads=[payload1, payload2], + correlation_id="test-correlation-id", + msg_id="test-msg-id", + msg_purpose="chat", + sender_name="test_sender", + receiver_name="test_receiver", + reply_to="/test/reply" ) - print("Sent envelope:") - print(" Subject: {}".format(env.send_to)) - print(" Payloads: {}".format(len(env.payloads))) + assert env.send_to == "/test/subject" + assert env.correlation_id == "test-correlation-id" + assert env.msg_id == "test-msg-id" + assert env.msg_purpose == "chat" + assert len(env.payloads) == 2 + print(" [PASS] MessageEnvelope creation") - # Expected output on receiver: - # envelope = smartreceive(msg) - # for dataname, data, type in envelope["payloads"]: - # print("Received {}: {} (type: {})".format(dataname, data if type != "binary" else len(data), type)) + # Test to_json method + json_str = env.to_json() + json_data = json.loads(json_str) + assert json_data["sendTo"] == "/test/subject" + assert json_data["correlationId"] == "test-correlation-id" + assert json_data["msgPurpose"] == "chat" + assert len(json_data["payloads"]) == 2 + print(" [PASS] MessageEnvelope.to_json() method") -def test_large_payload(): - """Test sending large payloads that require fileserver upload.""" - print("\n=== Test 4: Large Payload (Link Transport) ===") +def test_serialize_data(): + """Test _serialize_data function""" + print("\n=== Testing _serialize_data ===") - # Create large data (> 1MB would trigger link transport) - # For testing, we'll use a smaller size but configure threshold lower - large_data = b"A" * 100000 # 100KB + # Test text serialization + text_bytes = _serialize_data("Hello", "text") + assert isinstance(text_bytes, bytes) + assert text_bytes == b"Hello" + print(" [PASS] Text serialization") - data = [ - ("large_data", large_data, "binary") - ] + # Test dictionary serialization + dict_data = {"key": "value", "number": 42} + dict_bytes = _serialize_data(dict_data, "dictionary") + assert isinstance(dict_bytes, bytes) + parsed = json.loads(dict_bytes.decode('utf-8')) + assert parsed["key"] == "value" + print(" [PASS] Dictionary serialization") - # Use a lower threshold for testing - env = smartsend( - "/test/large", - data, - nats_url="nats://localhost:4222", - fileserver_url="http://localhost:8080", - size_threshold=50000 # 50KB threshold for testing - ) + # Test binary serialization + binary_data = b"\x00\x01\x02" + binary_bytes = _serialize_data(binary_data, "binary") + assert binary_bytes == b"\x00\x01\x02" + print(" [PASS] Binary serialization") - print("Sent envelope:") - print(" Subject: {}".format(env.send_to)) - print(" Payloads: {}".format(len(env.payloads))) - for p in env.payloads: - print(" - Transport: {}, Type: {}".format(p.transport, p.type)) + # Test image serialization + image_data = bytes([1, 2, 3, 4, 5]) + image_bytes = _serialize_data(image_data, "image") + assert image_bytes == image_data + print(" [PASS] Image serialization") -def test_reply_to(): - """Test sending messages with reply-to functionality.""" - print("\n=== Test 5: Reply To ===") +def test_deserialize_data(): + """Test _deserialize_data function""" + print("\n=== Testing _deserialize_data ===") - data = [ - ("command", {"action": "start"}, "dictionary") - ] + # Test text deserialization + text_bytes = b"Hello" + text_data = _deserialize_data(text_bytes, "text", "test-correlation-id") + assert text_data == "Hello" + print(" [PASS] Text deserialization") - env = smartsend( - "/test/command", - data, - nats_url="nats://localhost:4222", - reply_to="/test/response", - reply_to_msg_id="reply-123", - msg_purpose="command" - ) + # Test dictionary deserialization + dict_bytes = b'{"key": "value"}' + dict_data = _deserialize_data(dict_bytes, "dictionary", "test-correlation-id") + assert dict_data == {"key": "value"} + print(" [PASS] Dictionary deserialization") - print("Sent envelope:") - print(" Subject: {}".format(env.send_to)) - print(" Reply To: {}".format(env.reply_to)) - print(" Reply To Msg ID: {}".format(env.reply_to_msg_id)) + # Test binary deserialization + binary_data = b"\x00\x01\x02" + binary_result = _deserialize_data(binary_data, "binary", "test-correlation-id") + assert binary_result == b"\x00\x01\x02" + print(" [PASS] Binary deserialization") -def test_correlation_id(): - """Test using custom correlation IDs for tracing.""" - print("\n=== Test 6: Custom Correlation ID ===") +def test_utilities(): + """Test utility functions""" + print("\n=== Testing Utility Functions ===") - custom_cid = "trace-abc123" - data = [ - ("message", "Test with correlation ID", "text") - ] + # Test generate_uuid + uuid1 = generate_uuid() + uuid2 = generate_uuid() + assert uuid1 != uuid2 + print(f" [PASS] generate_uuid() - generated: {uuid1}") - env = smartsend( - "/test/correlation", - data, - nats_url="nats://localhost:4222", - correlation_id=custom_cid - ) - - print("Sent envelope with correlation ID: {}".format(env.correlation_id)) - print("This ID can be used to trace the message flow.") + # Test get_timestamp + timestamp = get_timestamp() + assert "T" in timestamp + print(f" [PASS] get_timestamp() - generated: {timestamp}") -def test_multiple_payloads(): - """Test sending multiple payloads in one message.""" - print("\n=== Test 7: Multiple Payloads ===") +def main(): + """Run all tests""" + print("=" * 60) + print("NATSBridge Python/Micropython - Basic Functionality Tests") + print("=" * 60) - data = [ - ("text_message", "Hello", "text"), - ("json_data", {"key": "value", "number": 42}, "dictionary"), - ("table_data", b"\x01\x02\x03\x04", "binary"), - ("audio_data", b"\x00\x01\x02\x03", "binary") - ] - - env = smartsend( - "/test/multiple", - data, - nats_url="nats://localhost:4222", - size_threshold=1000000 - ) - - print("Sent {} payloads in one message".format(len(env.payloads))) + try: + test_message_payload() + test_message_envelope() + test_serialize_data() + test_deserialize_data() + test_utilities() + + print("\n" + "=" * 60) + print("ALL TESTS PASSED!") + print("=" * 60) + + except Exception as e: + print(f"\n[FAIL] Test failed with error: {e}") + import traceback + traceback.print_exc() + sys.exit(1) if __name__ == "__main__": - print("Micropython NATS Bridge Test Suite") - print("==================================") - print() - - # Run tests - test_text_message() - test_dictionary_message() - test_mixed_payloads() - test_large_payload() - test_reply_to() - test_correlation_id() - test_multiple_payloads() - - print("\n=== All tests completed ===") - print() - print("Note: These tests require:") - print(" 1. A running NATS server at nats://localhost:4222") - print(" 2. An HTTP file server at http://localhost:8080 (for large payloads)") - print() - print("To run the tests:") - print(" python test_micropython_basic.py") \ No newline at end of file + main() \ No newline at end of file diff --git a/test/test_micropython_dict_receiver.py b/test/test_micropython_dict_receiver.py new file mode 100644 index 0000000..ae6df9f --- /dev/null +++ b/test/test_micropython_dict_receiver.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 +""" +Test script for dictionary transport testing - Receiver +Tests receiving dictionary messages via NATS using nats_bridge.py smartreceive +""" + +import sys +import os +import json + +# Add src to path for import +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) + +from nats_bridge import smartreceive, log_trace +import nats +import asyncio + +# Configuration +SUBJECT = "/NATSBridge_dict_test" +NATS_URL = "nats://nats.yiem.cc:4222" + + +async def main(): + log_trace("", f"Starting dictionary transport receiver test...") + log_trace("", f"Note: This receiver will wait for messages from the sender.") + log_trace("", f"Run test_micropython_dict_sender.py first to send test data.") + + # Connect to NATS + nc = await nats.connect(NATS_URL) + log_trace("", f"Connected to NATS at {NATS_URL}") + + # Subscribe to the subject + async def message_handler(msg): + log_trace("", f"Received message on {msg.subject}") + + # Use smartreceive to handle the data + result = smartreceive(msg.data) + + # Result is an envelope dictionary with payloads field containing list of (dataname, data, data_type) tuples + for dataname, data, data_type in result["payloads"]: + if isinstance(data, dict): + log_trace(result.get("correlationId", ""), f"Received dictionary '{dataname}' of type {data_type}") + log_trace(result.get("correlationId", ""), f" Keys: {list(data.keys())}") + + # Display first few items for small dicts + if isinstance(data, dict) and len(data) <= 10: + log_trace(result.get("correlationId", ""), f" Content: {json.dumps(data, indent=2)}") + else: + # For large dicts, show summary + log_trace(result.get("correlationId", ""), f" Summary: {json.dumps(data, default=str)[:200]}...") + + # Save to file + output_path = f"./received_{dataname}.json" + with open(output_path, 'w') as f: + json.dump(data, f, indent=2) + log_trace(result.get("correlationId", ""), f"Saved dictionary to {output_path}") + else: + log_trace(result.get("correlationId", ""), f"Received unexpected data type for '{dataname}': {type(data)}") + + sid = await nc.subscribe(SUBJECT, cb=message_handler) + log_trace("", f"Subscribed to {SUBJECT} with subscription ID: {sid}") + + # Keep listening for 120 seconds + await asyncio.sleep(120) + await nc.close() + log_trace("", "Test completed.") + + +if __name__ == "__main__": + asyncio.run(main()) \ No newline at end of file diff --git a/test/test_micropython_dict_sender.py b/test/test_micropython_dict_sender.py new file mode 100644 index 0000000..3d63106 --- /dev/null +++ b/test/test_micropython_dict_sender.py @@ -0,0 +1,99 @@ +#!/usr/bin/env python3 +""" +Test script for dictionary transport testing - Micropython +Tests sending dictionary messages via NATS using nats_bridge.py smartsend +""" + +import sys +import os + +# Add src to path for import +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) + +from nats_bridge import smartsend, log_trace +import uuid + +# Configuration +SUBJECT = "/NATSBridge_dict_test" +NATS_URL = "nats://nats.yiem.cc:4222" +FILESERVER_URL = "http://192.168.88.104:8080" +SIZE_THRESHOLD = 1_000_000 # 1MB + +# Create correlation ID for tracing +correlation_id = str(uuid.uuid4()) + + +def main(): + # Create a small dictionary (will use direct transport) + small_dict = { + "name": "test", + "value": 42, + "enabled": True, + "metadata": { + "version": "1.0.0", + "timestamp": "2026-02-22T12:00:00Z" + } + } + + # Create a large dictionary (will use link transport if > 1MB) + # Generate a larger dictionary (~2MB to ensure link transport) + large_dict = { + "id": str(uuid.uuid4()), + "items": [ + { + "index": i, + "name": f"item_{i}", + "value": i * 1.5, + "data": "x" * 10000 # Large string per item + } + for i in range(200) + ], + "metadata": { + "count": 200, + "created": "2026-02-22T12:00:00Z" + } + } + + # Test data 1: small dictionary + data1 = ("small_dict", small_dict, "dictionary") + + # Test data 2: large dictionary + data2 = ("large_dict", large_dict, "dictionary") + + log_trace(correlation_id, f"Starting smartsend for subject: {SUBJECT}") + log_trace(correlation_id, f"Correlation ID: {correlation_id}") + + # Use smartsend with dictionary type + env = smartsend( + SUBJECT, + [data1, data2], # List of (dataname, data, type) tuples + nats_url=NATS_URL, + fileserver_url=FILESERVER_URL, + size_threshold=SIZE_THRESHOLD, + correlation_id=correlation_id, + msg_purpose="chat", + sender_name="dict_sender", + receiver_name="", + receiver_id="", + reply_to="", + reply_to_msg_id="" + ) + + log_trace(correlation_id, f"Sent message with {len(env.payloads)} payloads") + + # Log transport type for each payload + for i, payload in enumerate(env.payloads): + log_trace(correlation_id, f"Payload {i+1} ('{payload.dataname}'):") + log_trace(correlation_id, f" Transport: {payload.transport}") + log_trace(correlation_id, f" Type: {payload.type}") + log_trace(correlation_id, f" Size: {payload.size} bytes") + log_trace(correlation_id, f" Encoding: {payload.encoding}") + + if payload.transport == "link": + log_trace(correlation_id, f" URL: {payload.data}") + + print(f"Test completed. Correlation ID: {correlation_id}") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/test/test_micropython_file_receiver.py b/test/test_micropython_file_receiver.py new file mode 100644 index 0000000..98a6ebc --- /dev/null +++ b/test/test_micropython_file_receiver.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 +""" +Test script for file transport testing - Receiver +Tests receiving binary files via NATS using nats_bridge.py smartreceive +""" + +import sys +import os + +# Add src to path for import +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) + +from nats_bridge import smartreceive, log_trace +import nats +import asyncio + +# Configuration +SUBJECT = "/NATSBridge_file_test" +NATS_URL = "nats://nats.yiem.cc:4222" + + +async def main(): + log_trace("", f"Starting file transport receiver test...") + log_trace("", f"Note: This receiver will wait for messages from the sender.") + log_trace("", f"Run test_micropython_file_sender.py first to send test data.") + + # Connect to NATS + nc = await nats.connect(NATS_URL) + log_trace("", f"Connected to NATS at {NATS_URL}") + + # Subscribe to the subject + async def message_handler(msg): + log_trace("", f"Received message on {msg.subject}") + + # Use smartreceive to handle the data + result = smartreceive(msg.data) + + # Result is an envelope dictionary with payloads field containing list of (dataname, data, data_type) tuples + for dataname, data, data_type in result["payloads"]: + if isinstance(data, bytes): + log_trace(result.get("correlationId", ""), f"Received binary '{dataname}' of type {data_type}") + log_trace(result.get("correlationId", ""), f" Size: {len(data)} bytes") + + # Display first 100 bytes as hex + log_trace(result.get("correlationId", ""), f" First 100 bytes (hex): {data[:100].hex()}") + + # Save to file + output_path = f"./received_{dataname}.bin" + with open(output_path, 'wb') as f: + f.write(data) + log_trace(result.get("correlationId", ""), f"Saved binary to {output_path}") + else: + log_trace(result.get("correlationId", ""), f"Received unexpected data type for '{dataname}': {type(data)}") + + sid = await nc.subscribe(SUBJECT, cb=message_handler) + log_trace("", f"Subscribed to {SUBJECT} with subscription ID: {sid}") + + # Keep listening for 120 seconds + await asyncio.sleep(120) + await nc.close() + log_trace("", "Test completed.") + + +if __name__ == "__main__": + asyncio.run(main()) \ No newline at end of file diff --git a/test/test_micropython_file_sender.py b/test/test_micropython_file_sender.py new file mode 100644 index 0000000..9219c0f --- /dev/null +++ b/test/test_micropython_file_sender.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python3 +""" +Test script for file transport testing - Micropython +Tests sending binary files via NATS using nats_bridge.py smartsend +""" + +import sys +import os + +# Add src to path for import +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) + +from nats_bridge import smartsend, log_trace +import uuid + +# Configuration +SUBJECT = "/NATSBridge_file_test" +NATS_URL = "nats://nats.yiem.cc:4222" +FILESERVER_URL = "http://192.168.88.104:8080" +SIZE_THRESHOLD = 1_000_000 # 1MB + +# Create correlation ID for tracing +correlation_id = str(uuid.uuid4()) + + +def main(): + # Create small binary data (will use direct transport) + small_binary = b"This is small binary data for testing direct transport." + small_binary += b"\x00" * 100 # Add some null bytes + + # Create large binary data (will use link transport if > 1MB) + # Generate a larger binary (~2MB to ensure link transport) + large_binary = bytes([ + (i * 7) % 256 for i in range(2_000_000) + ]) + + # Test data 1: small binary (direct transport) + data1 = ("small_binary", small_binary, "binary") + + # Test data 2: large binary (link transport) + data2 = ("large_binary", large_binary, "binary") + + log_trace(correlation_id, f"Starting smartsend for subject: {SUBJECT}") + log_trace(correlation_id, f"Correlation ID: {correlation_id}") + + # Use smartsend with binary type + env = smartsend( + SUBJECT, + [data1, data2], # List of (dataname, data, type) tuples + nats_url=NATS_URL, + fileserver_url=FILESERVER_URL, + size_threshold=SIZE_THRESHOLD, + correlation_id=correlation_id, + msg_purpose="chat", + sender_name="file_sender", + receiver_name="", + receiver_id="", + reply_to="", + reply_to_msg_id="" + ) + + log_trace(correlation_id, f"Sent message with {len(env.payloads)} payloads") + + # Log transport type for each payload + for i, payload in enumerate(env.payloads): + log_trace(correlation_id, f"Payload {i+1} ('{payload.dataname}'):") + log_trace(correlation_id, f" Transport: {payload.transport}") + log_trace(correlation_id, f" Type: {payload.type}") + log_trace(correlation_id, f" Size: {payload.size} bytes") + log_trace(correlation_id, f" Encoding: {payload.encoding}") + + if payload.transport == "link": + log_trace(correlation_id, f" URL: {payload.data}") + + print(f"Test completed. Correlation ID: {correlation_id}") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/test/test_micropython_mixed_receiver.py b/test/test_micropython_mixed_receiver.py new file mode 100644 index 0000000..d28722d --- /dev/null +++ b/test/test_micropython_mixed_receiver.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +""" +Test script for mixed payload testing - Receiver +Tests receiving mixed payload types via NATS using nats_bridge.py smartreceive +""" + +import sys +import os +import json + +# Add src to path for import +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) + +from nats_bridge import smartreceive, log_trace +import nats +import asyncio + +# Configuration +SUBJECT = "/NATSBridge_mixed_test" +NATS_URL = "nats://nats.yiem.cc:4222" + + +async def main(): + log_trace("", f"Starting mixed payload receiver test...") + log_trace("", f"Note: This receiver will wait for messages from the sender.") + log_trace("", f"Run test_micropython_mixed_sender.py first to send test data.") + + # Connect to NATS + nc = await nats.connect(NATS_URL) + log_trace("", f"Connected to NATS at {NATS_URL}") + + # Subscribe to the subject + async def message_handler(msg): + log_trace("", f"Received message on {msg.subject}") + + # Use smartreceive to handle the data + result = smartreceive(msg.data) + + log_trace(result.get("correlationId", ""), f"Received envelope with {len(result['payloads'])} payloads") + + # Result is an envelope dictionary with payloads field containing list of (dataname, data, data_type) tuples + for dataname, data, data_type in result["payloads"]: + log_trace(result.get("correlationId", ""), f"\n--- Payload: {dataname} (type: {data_type}) ---") + + if isinstance(data, str): + log_trace(result.get("correlationId", ""), f" Type: text/string") + log_trace(result.get("correlationId", ""), f" Length: {len(data)} characters") + if len(data) <= 100: + log_trace(result.get("correlationId", ""), f" Content: {data}") + else: + log_trace(result.get("correlationId", ""), f" First 100 chars: {data[:100]}...") + # Save to file + output_path = f"./received_{dataname}.txt" + with open(output_path, 'w') as f: + f.write(data) + log_trace(result.get("correlationId", ""), f" Saved to: {output_path}") + + elif isinstance(data, dict): + log_trace(result.get("correlationId", ""), f" Type: dictionary") + log_trace(result.get("correlationId", ""), f" Keys: {list(data.keys())}") + log_trace(result.get("correlationId", ""), f" Content: {json.dumps(data, indent=2)}") + # Save to file + output_path = f"./received_{dataname}.json" + with open(output_path, 'w') as f: + json.dump(data, f, indent=2) + log_trace(result.get("correlationId", ""), f" Saved to: {output_path}") + + elif isinstance(data, bytes): + log_trace(result.get("correlationId", ""), f" Type: binary") + log_trace(result.get("correlationId", ""), f" Size: {len(data)} bytes") + log_trace(result.get("correlationId", ""), f" First 100 bytes (hex): {data[:100].hex()}") + # Save to file + output_path = f"./received_{dataname}.bin" + with open(output_path, 'wb') as f: + f.write(data) + log_trace(result.get("correlationId", ""), f" Saved to: {output_path}") + else: + log_trace(result.get("correlationId", ""), f" Received unexpected data type: {type(data)}") + + # Log envelope metadata + log_trace(result.get("correlationId", ""), f"\n--- Envelope Metadata ---") + log_trace(result.get("correlationId", ""), f" Correlation ID: {result.get('correlationId', 'N/A')}") + log_trace(result.get("correlationId", ""), f" Message ID: {result.get('msgId', 'N/A')}") + log_trace(result.get("correlationId", ""), f" Sender: {result.get('senderName', 'N/A')}") + log_trace(result.get("correlationId", ""), f" Purpose: {result.get('msgPurpose', 'N/A')}") + + sid = await nc.subscribe(SUBJECT, cb=message_handler) + log_trace("", f"Subscribed to {SUBJECT} with subscription ID: {sid}") + + # Keep listening for 120 seconds + await asyncio.sleep(120) + await nc.close() + log_trace("", "Test completed.") + + +if __name__ == "__main__": + asyncio.run(main()) \ No newline at end of file diff --git a/test/test_micropython_mixed_sender.py b/test/test_micropython_mixed_sender.py new file mode 100644 index 0000000..00e24d3 --- /dev/null +++ b/test/test_micropython_mixed_sender.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python3 +""" +Test script for mixed payload testing - Micropython +Tests sending mixed payload types via NATS using nats_bridge.py smartsend +""" + +import sys +import os + +# Add src to path for import +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) + +from nats_bridge import smartsend, log_trace +import uuid + +# Configuration +SUBJECT = "/NATSBridge_mixed_test" +NATS_URL = "nats://nats.yiem.cc:4222" +FILESERVER_URL = "http://192.168.88.104:8080" +SIZE_THRESHOLD = 1_000_000 # 1MB + +# Create correlation ID for tracing +correlation_id = str(uuid.uuid4()) + + +def main(): + # Create payloads for mixed content test + + # 1. Small text (direct transport) + text_data = "Hello, this is a text message for testing mixed payloads!" + + # 2. Small dictionary (direct transport) + dict_data = { + "status": "ok", + "code": 200, + "message": "Test successful", + "items": [1, 2, 3] + } + + # 3. Small binary (direct transport) + binary_data = b"\x00\x01\x02\x03\x04\x05" + b"\xff" * 100 + + # 4. Large text (link transport - will use fileserver) + large_text = "\n".join([ + f"Line {i}: This is a large text payload for link transport testing. " * 50 + for i in range(100) + ]) + + # Test data list - mixed payload types + data = [ + ("message_text", text_data, "text"), + ("config_dict", dict_data, "dictionary"), + ("small_binary", binary_data, "binary"), + ("large_text", large_text, "text"), + ] + + log_trace(correlation_id, f"Starting smartsend for subject: {SUBJECT}") + log_trace(correlation_id, f"Correlation ID: {correlation_id}") + + # Use smartsend with mixed types + env = smartsend( + SUBJECT, + data, # List of (dataname, data, type) tuples + nats_url=NATS_URL, + fileserver_url=FILESERVER_URL, + size_threshold=SIZE_THRESHOLD, + correlation_id=correlation_id, + msg_purpose="chat", + sender_name="mixed_sender", + receiver_name="", + receiver_id="", + reply_to="", + reply_to_msg_id="" + ) + + log_trace(correlation_id, f"Sent message with {len(env.payloads)} payloads") + + # Log transport type for each payload + for i, payload in enumerate(env.payloads): + log_trace(correlation_id, f"Payload {i+1} ('{payload.dataname}'):") + log_trace(correlation_id, f" Transport: {payload.transport}") + log_trace(correlation_id, f" Type: {payload.type}") + log_trace(correlation_id, f" Size: {payload.size} bytes") + log_trace(correlation_id, f" Encoding: {payload.encoding}") + + if payload.transport == "link": + log_trace(correlation_id, f" URL: {payload.data}") + + print(f"Test completed. Correlation ID: {correlation_id}") + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/test/test_micropython_text_receiver.py b/test/test_micropython_text_receiver.py new file mode 100644 index 0000000..ee585d3 --- /dev/null +++ b/test/test_micropython_text_receiver.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +""" +Test script for text transport testing - Receiver +Tests receiving text messages via NATS using nats_bridge.py smartreceive +""" + +import sys +import os +import json + +# Add src to path for import +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) + +from nats_bridge import smartreceive, log_trace +import nats +import asyncio + +# Configuration +SUBJECT = "/NATSBridge_text_test" +NATS_URL = "nats://nats.yiem.cc:4222" + + +async def main(): + log_trace("", f"Starting text transport receiver test...") + log_trace("", f"Note: This receiver will wait for messages from the sender.") + log_trace("", f"Run test_micropython_text_sender.py first to send test data.") + + # Connect to NATS + nc = await nats.connect(NATS_URL) + log_trace("", f"Connected to NATS at {NATS_URL}") + + # Subscribe to the subject + async def message_handler(msg): + log_trace("", f"Received message on {msg.subject}") + + # Use smartreceive to handle the data + result = smartreceive(msg.data) + + # Result is an envelope dictionary with payloads field containing list of (dataname, data, data_type) tuples + for dataname, data, data_type in result["payloads"]: + if isinstance(data, str): + log_trace(result.get("correlationId", ""), f"Received text '{dataname}' of type {data_type}") + log_trace(result.get("correlationId", ""), f" Length: {len(data)} characters") + + # Display first 100 characters + if len(data) > 100: + log_trace(result.get("correlationId", ""), f" First 100 characters: {data[:100]}...") + else: + log_trace(result.get("correlationId", ""), f" Content: {data}") + + # Save to file + output_path = f"./received_{dataname}.txt" + with open(output_path, 'w') as f: + f.write(data) + log_trace(result.get("correlationId", ""), f"Saved text to {output_path}") + else: + log_trace(result.get("correlationId", ""), f"Received unexpected data type for '{dataname}': {type(data)}") + + sid = await nc.subscribe(SUBJECT, cb=message_handler) + log_trace("", f"Subscribed to {SUBJECT} with subscription ID: {sid}") + + # Keep listening for 120 seconds + await asyncio.sleep(120) + await nc.close() + log_trace("", "Test completed.") + + +if __name__ == "__main__": + asyncio.run(main()) \ No newline at end of file diff --git a/test/test_micropython_text_sender.py b/test/test_micropython_text_sender.py new file mode 100644 index 0000000..26200ed --- /dev/null +++ b/test/test_micropython_text_sender.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 +""" +Test script for text transport testing - Micropython +Tests sending text messages via NATS using nats_bridge.py smartsend +""" + +import sys +import os + +# Add src to path for import +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) + +from nats_bridge import smartsend, log_trace +import uuid + +# Configuration +SUBJECT = "/NATSBridge_text_test" +NATS_URL = "nats://nats.yiem.cc:4222" +FILESERVER_URL = "http://192.168.88.104:8080" +SIZE_THRESHOLD = 1_000_000 # 1MB + +# Create correlation ID for tracing +correlation_id = str(uuid.uuid4()) + + +def main(): + # Create a small text (will use direct transport) + small_text = "Hello, this is a small text message. Testing direct transport via NATS." + + # Create a large text (will use link transport if > 1MB) + # Generate a larger text (~2MB to ensure link transport) + large_text = "\n".join([ + f"Line {i}: This is a sample text line with some content to pad the size. " * 100 + for i in range(500) + ]) + + # Test data 1: small text + data1 = ("small_text", small_text, "text") + + # Test data 2: large text + data2 = ("large_text", large_text, "text") + + log_trace(correlation_id, f"Starting smartsend for subject: {SUBJECT}") + log_trace(correlation_id, f"Correlation ID: {correlation_id}") + + # Use smartsend with text type + # For small text: will use direct transport (Base64 encoded UTF-8) + # For large text: will use link transport (uploaded to fileserver) + env = smartsend( + SUBJECT, + [data1, data2], # List of (dataname, data, type) tuples + nats_url=NATS_URL, + fileserver_url=FILESERVER_URL, + size_threshold=SIZE_THRESHOLD, + correlation_id=correlation_id, + msg_purpose="chat", + sender_name="text_sender", + receiver_name="", + receiver_id="", + reply_to="", + reply_to_msg_id="" + ) + + log_trace(correlation_id, f"Sent message with {len(env.payloads)} payloads") + + # Log transport type for each payload + for i, payload in enumerate(env.payloads): + log_trace(correlation_id, f"Payload {i+1} ('{payload.dataname}'):") + log_trace(correlation_id, f" Transport: {payload.transport}") + log_trace(correlation_id, f" Type: {payload.type}") + log_trace(correlation_id, f" Size: {payload.size} bytes") + log_trace(correlation_id, f" Encoding: {payload.encoding}") + + if payload.transport == "link": + log_trace(correlation_id, f" URL: {payload.data}") + + print(f"Test completed. Correlation ID: {correlation_id}") + + +if __name__ == "__main__": + main() \ No newline at end of file