mirror of
https://github.com/smittix/intercept.git
synced 2026-06-09 14:41:55 -07:00
e5c5afb158
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
182 lines
5.3 KiB
Python
182 lines
5.3 KiB
Python
"""Integration tests: mock meshcore library at its boundary, test full flow."""
|
|
|
|
import sys
|
|
from datetime import datetime, timezone
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def mock_meshcore_lib():
|
|
"""Isolate tests from the optional meshcore library."""
|
|
fake = MagicMock()
|
|
with patch.dict(sys.modules, {"meshcore": fake, "meshcore.events": fake}):
|
|
yield
|
|
|
|
|
|
class TestMessageRoundTrip:
|
|
"""Connect → receive event → appears in message store and SSE queue."""
|
|
|
|
def test_message_stored_on_receipt(self):
|
|
from utils.meshcore import MeshcoreClient, MeshcoreMessage
|
|
|
|
client = MeshcoreClient()
|
|
|
|
msg = MeshcoreMessage(
|
|
id="t1",
|
|
sender_id="A",
|
|
recipient_id="BROADCAST",
|
|
text="test",
|
|
timestamp=datetime.now(timezone.utc),
|
|
hop_count=1,
|
|
snr=-10.0,
|
|
is_direct=False,
|
|
)
|
|
client.on_message(msg)
|
|
|
|
msgs = client.get_messages()
|
|
assert len(msgs) == 1
|
|
assert msgs[0]["text"] == "test"
|
|
|
|
def test_message_pushed_to_queue(self):
|
|
from utils.meshcore import MeshcoreClient, MeshcoreMessage
|
|
|
|
client = MeshcoreClient()
|
|
|
|
msg = MeshcoreMessage(
|
|
id="t2",
|
|
sender_id="B",
|
|
recipient_id="BROADCAST",
|
|
text="queued",
|
|
timestamp=datetime.now(timezone.utc),
|
|
hop_count=0,
|
|
snr=None,
|
|
is_direct=True,
|
|
)
|
|
client.on_message(msg)
|
|
|
|
event = client.get_queue().get_nowait()
|
|
assert event["type"] == "message"
|
|
assert event["data"]["text"] == "queued"
|
|
|
|
def test_message_history_capped_at_500(self):
|
|
from utils.meshcore import MeshcoreClient, MeshcoreMessage
|
|
|
|
client = MeshcoreClient()
|
|
|
|
for i in range(510):
|
|
client.on_message(
|
|
MeshcoreMessage(
|
|
id=str(i),
|
|
sender_id="X",
|
|
recipient_id="BROADCAST",
|
|
text=f"msg{i}",
|
|
timestamp=datetime.now(timezone.utc),
|
|
hop_count=0,
|
|
snr=None,
|
|
is_direct=False,
|
|
)
|
|
)
|
|
|
|
assert len(client.get_messages()) == 500
|
|
|
|
|
|
class TestNodeAndRepeater:
|
|
def test_repeater_appears_in_repeaters_only(self):
|
|
from utils.meshcore import MeshcoreClient, MeshcoreNode
|
|
|
|
client = MeshcoreClient()
|
|
|
|
client.on_node(
|
|
MeshcoreNode(
|
|
node_id="R1",
|
|
name="Roof",
|
|
is_repeater=True,
|
|
lat=51.5,
|
|
lon=-0.1,
|
|
battery_pct=100,
|
|
last_seen=datetime.now(timezone.utc),
|
|
snr=-3.0,
|
|
hops_away=1,
|
|
)
|
|
)
|
|
client.on_node(
|
|
MeshcoreNode(
|
|
node_id="C1",
|
|
name="Client",
|
|
is_repeater=False,
|
|
lat=51.6,
|
|
lon=-0.2,
|
|
battery_pct=72,
|
|
last_seen=datetime.now(timezone.utc),
|
|
snr=-8.0,
|
|
hops_away=2,
|
|
)
|
|
)
|
|
|
|
assert len(client.get_repeaters()) == 1
|
|
assert client.get_repeaters()[0]["node_id"] == "R1"
|
|
assert len(client.get_nodes()) == 2
|
|
|
|
|
|
class TestTracerouteRoundTrip:
|
|
def test_traceroute_pushed_to_queue(self):
|
|
from utils.meshcore import MeshcoreClient, MeshcoreTraceroute
|
|
|
|
client = MeshcoreClient()
|
|
|
|
tr = MeshcoreTraceroute(
|
|
origin_id="A",
|
|
destination_id="B",
|
|
hops=["A", "R1", "B"],
|
|
snr_per_hop=[-5.0, -8.0],
|
|
timestamp=datetime.now(timezone.utc),
|
|
)
|
|
client.on_traceroute(tr)
|
|
|
|
q = client.get_queue()
|
|
event = q.get_nowait()
|
|
assert event["type"] == "traceroute"
|
|
assert event["data"]["hops"] == ["A", "R1", "B"]
|
|
|
|
|
|
class TestBLEDockerBlock:
|
|
def test_ble_blocked_in_docker(self, monkeypatch):
|
|
monkeypatch.setenv("INTERCEPT_DOCKER", "1")
|
|
from utils.meshcore import BLEConfig, MeshcoreClient
|
|
|
|
client = MeshcoreClient()
|
|
client.connect(BLEConfig())
|
|
|
|
events = []
|
|
while not client.get_queue().empty():
|
|
events.append(client.get_queue().get_nowait())
|
|
error_events = [e for e in events if e.get("data", {}).get("state") == "error"]
|
|
assert error_events
|
|
assert "Docker" in error_events[0]["data"]["message"]
|
|
|
|
|
|
class TestConnectionStateTransitions:
|
|
def test_on_connected_pushes_status_event(self):
|
|
from utils.meshcore import ConnectionState, MeshcoreClient
|
|
|
|
client = MeshcoreClient()
|
|
client.on_connected(transport="serial", device="/dev/ttyUSB0")
|
|
|
|
assert client.get_state() == ConnectionState.CONNECTED
|
|
event = client.get_queue().get_nowait()
|
|
assert event["type"] == "status"
|
|
assert event["data"]["state"] == "connected"
|
|
|
|
def test_on_error_pushes_status_event(self):
|
|
from utils.meshcore import ConnectionState, MeshcoreClient
|
|
|
|
client = MeshcoreClient()
|
|
client.on_error("timeout")
|
|
|
|
assert client.get_state() == ConnectionState.ERROR
|
|
event = client.get_queue().get_nowait()
|
|
assert event["data"]["state"] == "error"
|
|
assert event["data"].get("message") == "timeout"
|