Files
ai-bot/tests/test_route_ttl.py

96 lines
3.0 KiB
Python

"""Tests for orac.meshcore route table TTL and SNR upgrade rule."""
from __future__ import annotations
import time
from unittest.mock import patch
import orac.meshcore as mc
def _clear_route_table() -> None:
with mc._route_lock:
mc._route_table.clear()
def test_learn_and_get_roundtrip() -> None:
_clear_route_table()
mc.learn_route(0xAB, [b"\x11\x22"], hash_size=2, snr=10.0)
got = mc.get_route(0xAB)
assert got is not None
hops, hs = got
# path stored reversed
assert hops == [b"\x11\x22"]
assert hs == 2
def test_ttl_expiry_and_lazy_eviction() -> None:
_clear_route_table()
mc.learn_route(0xCD, [b"\x01\x02"], hash_size=2, snr=5.0)
# Fast-forward monotonic time by patching time.monotonic to return now+TTL+1
fake_future = time.monotonic() + mc.ROUTE_TTL_S + 1.0
with patch("orac.meshcore.time.monotonic", return_value=fake_future):
assert mc.get_route(0xCD) is None
# Entry should have been lazy-evicted.
assert mc.get_route(0xCD) is None
def test_forget_route_removes_entry() -> None:
_clear_route_table()
mc.learn_route(0x55, [b"\xaa\xbb"], hash_size=2)
assert mc.forget_route(0x55) is True
assert mc.get_route(0x55) is None
# Double-forget is a no-op.
assert mc.forget_route(0x55) is False
def test_snr_upgrade_rule_rejects_bad_path() -> None:
_clear_route_table()
mc.learn_route(0x77, [b"\x01\x02", b"\x03\x04"], hash_size=2, snr=10.0)
# New path arrives with much worse SNR → existing path kept.
mc.learn_route(0x77, [b"\xff\xff"], hash_size=2, snr=-5.0)
got = mc.get_route(0x77)
assert got is not None
hops, _ = got
assert hops == [b"\x03\x04", b"\x01\x02"] # original (reversed) hops
def test_snr_upgrade_rule_accepts_comparable_path() -> None:
_clear_route_table()
mc.learn_route(0x88, [b"\x01\x02", b"\x03\x04"], hash_size=2, snr=5.0)
# New path with slightly worse SNR (within 3 dB tolerance) → upgrade (refreshes learned_at).
mc.learn_route(0x88, [b"\xaa\xbb"], hash_size=2, snr=3.0)
got = mc.get_route(0x88)
assert got is not None
hops, _ = got
assert hops == [b"\xaa\xbb"]
def test_hash_size_downgrade_rejected() -> None:
_clear_route_table()
mc.learn_route(0x99, [b"\x01\x02"], hash_size=2, snr=10.0)
# Smaller hash size should NOT replace a larger one.
mc.learn_route(0x99, [b"\xff"], hash_size=1, snr=10.0)
got = mc.get_route(0x99)
assert got is not None
_, hs = got
assert hs == 2
def test_one_byte_hash_paths_are_never_learned() -> None:
"""Policy: we refuse to learn routes with 1-byte hashes (MIN_HASH_SIZE=2)."""
_clear_route_table()
mc.learn_route(0xAA, [b"\xff"], hash_size=1, snr=10.0)
assert mc.get_route(0xAA) is None
def test_build_path_return_rejects_one_byte_hashes() -> None:
"""Policy: refuse to echo 1-byte PATH returns."""
import pytest
peer_pk = b"\x55" * 32
with pytest.raises(ValueError):
mc.build_path_return_packet(peer_pk, [b"\xff"], hash_size=1)