mirror of
https://github.com/pyMC-dev/pyMC_Repeater.git
synced 2026-06-12 17:24:48 +02:00
Merge pull request #275 from agessaman/fix/binary-persist-1150
This commit is contained in:
@@ -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):
|
||||
|
||||
@@ -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))
|
||||
Reference in New Issue
Block a user