mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-03-28 17:43:05 +01:00
First wave of work
This commit is contained in:
@@ -127,6 +127,7 @@ export function deleteContact(publicKey: string): Promise<{ status: string }> {
|
||||
export interface MessagePath {
|
||||
path: string;
|
||||
received_at: number;
|
||||
path_len?: number;
|
||||
}
|
||||
|
||||
export interface Message {
|
||||
|
||||
@@ -19,6 +19,7 @@ from app.decoder import (
|
||||
decrypt_group_text,
|
||||
derive_public_key,
|
||||
derive_shared_secret,
|
||||
extract_payload,
|
||||
parse_packet,
|
||||
try_decrypt_dm,
|
||||
try_decrypt_packet_with_channel_key,
|
||||
@@ -81,8 +82,31 @@ class TestPacketParsing:
|
||||
assert result.route_type == RouteType.DIRECT
|
||||
assert result.payload_type == PayloadType.TEXT_MESSAGE
|
||||
assert result.path_length == 3
|
||||
assert result.path_hash_size == 1
|
||||
assert result.path_byte_length == 3
|
||||
assert result.payload == b"msg"
|
||||
|
||||
def test_parse_packet_with_two_byte_hops(self):
|
||||
"""Packets with multi-byte hop identifiers decode hop count separately from byte length."""
|
||||
packet = bytes([0x0A, 0x42, 0x01, 0x02, 0x03, 0x04]) + b"msg"
|
||||
|
||||
result = parse_packet(packet)
|
||||
|
||||
assert result is not None
|
||||
assert result.route_type == RouteType.DIRECT
|
||||
assert result.payload_type == PayloadType.TEXT_MESSAGE
|
||||
assert result.path_length == 2
|
||||
assert result.path_hash_size == 2
|
||||
assert result.path_byte_length == 4
|
||||
assert result.path == bytes([0x01, 0x02, 0x03, 0x04])
|
||||
assert result.payload == b"msg"
|
||||
|
||||
def test_extract_payload_with_two_byte_hops(self):
|
||||
"""Payload extraction skips the full path byte length for multi-byte hops."""
|
||||
packet = bytes([0x15, 0x42, 0xAA, 0xBB, 0xCC, 0xDD]) + b"payload_data"
|
||||
|
||||
assert extract_payload(packet) == b"payload_data"
|
||||
|
||||
def test_parse_transport_flood_skips_transport_code(self):
|
||||
"""TRANSPORT_FLOOD packets have 4-byte transport code to skip."""
|
||||
# Header: route_type=TRANSPORT_FLOOD(0), payload_type=GROUP_TEXT(5)
|
||||
|
||||
@@ -682,7 +682,7 @@ class TestDirectMessageDirectionDetection:
|
||||
message_broadcasts = [b for b in broadcasts if b["type"] == "message"]
|
||||
assert len(message_broadcasts) == 1
|
||||
assert message_broadcasts[0]["data"]["paths"] == [
|
||||
{"path": "", "received_at": SENDER_TIMESTAMP}
|
||||
{"path": "", "received_at": SENDER_TIMESTAMP, "path_len": 0}
|
||||
]
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
||||
@@ -652,6 +652,21 @@ class TestAppriseFormatBody:
|
||||
assert "`20`" in body
|
||||
assert "`27`" in body
|
||||
|
||||
def test_dm_with_multi_byte_path(self):
|
||||
from app.fanout.apprise_mod import _format_body
|
||||
|
||||
body = _format_body(
|
||||
{
|
||||
"type": "PRIV",
|
||||
"text": "hi",
|
||||
"sender_name": "Alice",
|
||||
"paths": [{"path": "20273031", "path_len": 2}],
|
||||
},
|
||||
include_path=True,
|
||||
)
|
||||
assert "`2027`" in body
|
||||
assert "`3031`" in body
|
||||
|
||||
def test_dm_no_path_shows_direct(self):
|
||||
from app.fanout.apprise_mod import _format_body
|
||||
|
||||
|
||||
@@ -678,6 +678,7 @@ class TestMessageBroadcastStructure:
|
||||
assert broadcast["paths"] is not None
|
||||
assert len(broadcast["paths"]) == 1
|
||||
assert broadcast["paths"][0]["path"] == "" # Empty string = direct/flood
|
||||
assert broadcast["paths"][0]["path_len"] == 0
|
||||
|
||||
|
||||
class TestRawPacketStorage:
|
||||
@@ -927,6 +928,7 @@ class TestCreateDMMessageFromDecrypted:
|
||||
our_public_key=self.FACE12_PUB,
|
||||
received_at=1700000001,
|
||||
path="aabbcc", # Path through 3 repeaters
|
||||
path_len=3,
|
||||
outgoing=False,
|
||||
)
|
||||
|
||||
@@ -937,6 +939,7 @@ class TestCreateDMMessageFromDecrypted:
|
||||
assert broadcast["paths"] is not None
|
||||
assert len(broadcast["paths"]) == 1
|
||||
assert broadcast["paths"][0]["path"] == "aabbcc"
|
||||
assert broadcast["paths"][0]["path_len"] == 3
|
||||
assert broadcast["paths"][0]["received_at"] == 1700000001
|
||||
|
||||
|
||||
|
||||
@@ -36,12 +36,13 @@ class TestMessageRepositoryAddPath:
|
||||
msg_id = await _create_message(test_db)
|
||||
|
||||
result = await MessageRepository.add_path(
|
||||
message_id=msg_id, path="1A2B", received_at=1700000000
|
||||
message_id=msg_id, path="1A2B", received_at=1700000000, path_len=1
|
||||
)
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0].path == "1A2B"
|
||||
assert result[0].received_at == 1700000000
|
||||
assert result[0].path_len == 1
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_add_path_to_message_with_existing_paths(self, test_db):
|
||||
|
||||
@@ -125,6 +125,39 @@ class TestOutgoingDMBroadcast:
|
||||
assert exc_info.value.status_code == 409
|
||||
assert "ambiguous" in exc_info.value.detail.lower()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_dm_add_contact_preserves_out_path_hash_mode(self, test_db):
|
||||
"""Direct-send contact export includes the inferred path hash mode for multi-byte routes."""
|
||||
mc = _make_mc()
|
||||
pub_key = "cd" * 32
|
||||
await ContactRepository.upsert(
|
||||
{
|
||||
"public_key": pub_key,
|
||||
"name": "Bob",
|
||||
"type": 0,
|
||||
"flags": 0,
|
||||
"last_path": "11223344",
|
||||
"last_path_len": 2,
|
||||
"last_advert": None,
|
||||
"lat": None,
|
||||
"lon": None,
|
||||
"last_seen": None,
|
||||
"on_radio": False,
|
||||
"last_contacted": None,
|
||||
}
|
||||
)
|
||||
|
||||
with (
|
||||
patch("app.routers.messages.require_connected", return_value=mc),
|
||||
patch.object(radio_manager, "_meshcore", mc),
|
||||
):
|
||||
await send_direct_message(SendDirectMessageRequest(destination=pub_key, text="hi"))
|
||||
|
||||
add_contact_arg = mc.commands.add_contact.await_args.args[0]
|
||||
assert add_contact_arg["out_path"] == "11223344"
|
||||
assert add_contact_arg["out_path_len"] == 2
|
||||
assert add_contact_arg["out_path_hash_mode"] == 1
|
||||
|
||||
|
||||
class TestOutgoingChannelBroadcast:
|
||||
"""Test that outgoing channel messages are broadcast via broadcast_event for fanout dispatch."""
|
||||
|
||||
Reference in New Issue
Block a user