Merge pull request #275 from agessaman/fix/binary-persist-1150

This commit is contained in:
Lloyd
2026-05-31 08:22:57 +01:00
committed by GitHub
2 changed files with 75 additions and 2 deletions
+21 -2
View File
@@ -18,6 +18,24 @@ from pymc_core.companion import CompanionBridge
logger = logging.getLogger("RepeaterCompanionBridge")
def _prefs_bytes_from_json(value: Any) -> bytes:
"""Restore a ``bytes`` NodePrefs field from JSON (hex string from :func:`_to_json_safe`)."""
if value is None:
return b""
if isinstance(value, (bytes, bytearray)):
return bytes(value)
if isinstance(value, str):
s = value.strip()
if not s:
return b""
try:
return bytes.fromhex(s)
except ValueError:
logger.debug("Invalid hex for prefs bytes field (prefix %r)", s[:32])
return b""
return b""
def _to_json_safe(value: Any) -> Any:
"""Convert a value to a JSON-serializable form (avoids TypeError from enums, bytes, etc.)."""
if value is None or isinstance(value, (bool, int, float, str)):
@@ -70,8 +88,6 @@ class RepeaterCompanionBridge(CompanionBridge):
authenticate_callback=authenticate_callback,
initial_contacts=initial_contacts,
)
# Load persisted prefs (e.g. node_name) from SQLite so matching uses last-saved name
self._load_prefs()
def _save_prefs(self) -> None:
"""Persist full NodePrefs as JSON to SQLite."""
@@ -104,6 +120,9 @@ class RepeaterCompanionBridge(CompanionBridge):
try:
if value is None:
continue
if isinstance(current, bytes):
setattr(self.prefs, key, _prefs_bytes_from_json(value))
continue
if isinstance(current, bool):
setattr(self.prefs, key, bool(value))
elif isinstance(current, int):
+54
View File
@@ -0,0 +1,54 @@
"""Tests for RepeaterCompanionBridge prefs JSON round-trip (bytes fields)."""
import pytest
from pymc_core import LocalIdentity
from repeater.companion.bridge import RepeaterCompanionBridge, _prefs_bytes_from_json
@pytest.fixture
def identity():
return LocalIdentity()
def test_prefs_bytes_from_json_round_trip():
assert _prefs_bytes_from_json("") == b""
assert _prefs_bytes_from_json("00") == b"\x00"
key = bytes(range(16))
assert _prefs_bytes_from_json(key.hex()) == key
assert _prefs_bytes_from_json(bytearray(key)) == key
assert _prefs_bytes_from_json(key) == key
assert _prefs_bytes_from_json("not-hex") == b""
def test_load_prefs_restores_default_scope_key_as_bytes(identity):
"""Hex strings from SQLite JSON must become bytes (not str) on NodePrefs."""
class FakeSqlite:
def companion_load_prefs(self, companion_hash: str):
return {
"default_scope_name": "region1",
"default_scope_key": bytes(range(16)).hex(),
}
def companion_save_prefs(self, companion_hash: str, prefs: dict) -> bool:
return True
async def inject(pkt, wait_for_ack=False):
return True
bridge = RepeaterCompanionBridge(
identity,
inject,
sqlite_handler=FakeSqlite(),
companion_hash="testhash",
node_name="bootname",
)
assert bridge.prefs.default_scope_name == "region1"
assert isinstance(bridge.prefs.default_scope_key, bytes)
assert bridge.prefs.default_scope_key == bytes(range(16))
scope = bridge.get_default_flood_scope()
assert scope is not None
assert scope[0] == "region1"
assert scope[1] == bytes(range(16))