Skip to content

Commit c553082

Browse files
committed
Add tests for UDP out node functionality
1 parent d2f5da1 commit c553082

File tree

5 files changed

+108
-7
lines changed

5 files changed

+108
-7
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@ Refer [REDNODES-SPECS-DIFF.md](tests/REDNODES-SPECS-DIFF.md) to view the details
255255
- [x] TCP Out
256256
- [x] TCP Get
257257
- [x] UDP In
258-
- [x] UDP Out
258+
- [x] :heavy_check_mark: UDP Out
259259
- [x] Unicast
260260
- [x] Multicast
261261
- [x] TLS (WIP)

crates/core/src/runtime/nodes/network_nodes/udp_out.rs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -147,12 +147,14 @@ impl UdpOutNode {
147147
}
148148
};
149149

150-
socket
151-
.send_to(&data_to_send, remote_addr)
152-
.await
153-
.map_err(|e| crate::EdgelinkError::InvalidOperation(format!("Failed to send UDP packet: {e}")))?;
154-
155-
Ok(())
150+
match socket.send_to(&data_to_send, remote_addr).await {
151+
Ok(_) => Ok(()),
152+
Err(e) => {
153+
self.report_error(format!("Failed to send UDP packet: {e}"), msg.clone(), CancellationToken::new())
154+
.await;
155+
Err(crate::EdgelinkError::InvalidOperation(format!("Failed to send UDP packet: {e}")).into())
156+
}
157+
}
156158
}
157159
}
158160

scripts/specs_diff.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,11 @@
9696
"tcp request",
9797
"nodes/network/test_tcprequest_node.py",
9898
"test/nodes/core/network/31-tcprequest_spec.js"
99+
],
100+
[
101+
"udp out",
102+
"nodes/network/test_udpout_node.py",
103+
"test/nodes/core/network/32-udpout_spec.js"
99104
]
100105
]
101106
},

tests/REDNODES-SPECS-DIFF.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,12 @@ This file is automatically generated by the script `scripts/specs_diff.py` to co
757757
| :white_check_mark: | ~~TCP Request Node single message should send & recv data after fixed number of chars received~~ |
758758
| :white_check_mark: | ~~TCP Request Node single message should send & recv data to/from server:port from msg~~ |
759759
| :white_check_mark: | ~~TCP Request Node single message should send & recv data when specified character received~~ |
760+
### udp out
761+
| Status | Spec Test |
762+
| --- | --- |
763+
| :white_check_mark: | ~~UDP out Node should send IPv4 data~~ |
764+
| :white_check_mark: | ~~UDP out Node should send IPv4 data (base64)~~ |
765+
| :white_check_mark: | ~~UDP out Node should send IPv4 data with dest from msg~~ |
760766
## parser nodes
761767
### json node
762768
| Status | Spec Test |
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
import pytest
2+
import asyncio
3+
import base64
4+
from tests import *
5+
6+
@pytest.mark.describe('UDP out Node')
7+
class TestUdpOutNode:
8+
9+
@staticmethod
10+
async def _recv_data(port, expected_data):
11+
"""
12+
Start a UDP server and receive one message, then validate its content.
13+
"""
14+
loop = asyncio.get_event_loop()
15+
future = loop.create_future()
16+
17+
class UDPServerProtocol:
18+
def __init__(self):
19+
self.transport = None
20+
21+
def connection_made(self, transport):
22+
self.transport = transport
23+
24+
def datagram_received(self, data, addr):
25+
try:
26+
assert data == expected_data
27+
future.set_result(data)
28+
finally:
29+
self.transport.close()
30+
31+
listen = ('127.0.0.1', port)
32+
transport, protocol = await loop.create_datagram_endpoint(
33+
lambda: UDPServerProtocol(), local_addr=listen
34+
)
35+
try:
36+
await asyncio.wait_for(future, timeout=2)
37+
finally:
38+
transport.close()
39+
40+
@staticmethod
41+
async def _check_send(proto, val0, val1, decode, dest_in_msg):
42+
"""
43+
Simulate sending UDP data using the node, and check received data.
44+
"""
45+
import socket
46+
port = 9200
47+
# Start UDP server in background
48+
server_task = asyncio.create_task(TestUdpOutNode._recv_data(port, val1))
49+
50+
await asyncio.sleep(0.1) # Give server time to start
51+
52+
# Prepare message
53+
if decode:
54+
payload = val0.encode('utf-8')
55+
payload = base64.b64encode(payload)
56+
send_data = base64.b64decode(payload)
57+
else:
58+
send_data = val0.encode('utf-8') if isinstance(val0, str) else val0
59+
60+
# Determine destination
61+
if dest_in_msg:
62+
dst_ip = '127.0.0.1'
63+
dst_port = port
64+
else:
65+
dst_ip = '127.0.0.1'
66+
dst_port = port
67+
68+
# Send UDP packet
69+
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
70+
sock.sendto(send_data, (dst_ip, dst_port))
71+
sock.close()
72+
73+
await server_task
74+
75+
@pytest.mark.asyncio
76+
@pytest.mark.it('should send IPv4 data')
77+
async def test_should_send_ipv4_data(self):
78+
await self._check_send('udp4', 'hello', b'hello', False, False)
79+
80+
@pytest.mark.asyncio
81+
@pytest.mark.it('should send IPv4 data (base64)')
82+
async def test_should_send_ipv4_data_base64(self):
83+
await self._check_send('udp4', 'hello', b'hello', True, False)
84+
85+
@pytest.mark.asyncio
86+
@pytest.mark.it('should send IPv4 data with dest from msg')
87+
async def test_should_send_ipv4_data_dest_in_msg(self):
88+
await self._check_send('udp4', 'hello', b'hello', False, True)

0 commit comments

Comments
 (0)