mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-08 13:12:08 +00:00
fix(matrix): reset _device_id_unverified at start of connect()
Per review feedback on #53997 from @teknium1: the flag was set True on failed device_id resolution but never reset, so a same-adapter reconnect that successfully resolves a real device_id would keep skipping server-side key verification indefinitely. Reset now happens at the top of connect(), before resolution runs, so every connect() attempt starts clean. A repeat failure re-sets the flag (unchanged behavior); a recovery correctly clears it. Adds TestDeviceIdRecoveryOnReconnect to cover the transition.
This commit is contained in:
parent
9048457eab
commit
09dbe76955
2 changed files with 120 additions and 0 deletions
|
|
@ -1152,6 +1152,7 @@ class MatrixAdapter(BasePlatformAdapter):
|
|||
|
||||
async def connect(self, *, is_reconnect: bool = False) -> bool:
|
||||
"""Connect to the Matrix homeserver and start syncing."""
|
||||
self._device_id_unverified = False
|
||||
if self._client is not None:
|
||||
try:
|
||||
await self.disconnect()
|
||||
|
|
|
|||
|
|
@ -5131,3 +5131,122 @@ class TestMatrixReconnectDisconnect:
|
|||
await adapter.connect()
|
||||
|
||||
adapter.disconnect.assert_awaited_once()
|
||||
|
||||
|
||||
class TestDeviceIdRecoveryOnReconnect:
|
||||
"""_device_id_unverified must reset on every connect() call so a
|
||||
recovery after a failed resolution clears the stuck-true flag."""
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_flag_clears_when_second_connect_resolves_device_id(self):
|
||||
"""Same adapter, first connect fails to resolve, second succeeds. Flag
|
||||
must be False afterward and server verification must run on the second
|
||||
call."""
|
||||
from plugins.platforms.matrix.adapter import MatrixAdapter
|
||||
|
||||
config = PlatformConfig(
|
||||
enabled=True,
|
||||
token="syt_test_access_token",
|
||||
extra={
|
||||
"homeserver": "https://matrix.example.org",
|
||||
"user_id": "@bot:example.org",
|
||||
"encryption": True,
|
||||
},
|
||||
)
|
||||
adapter = MatrixAdapter(config)
|
||||
|
||||
fake_mautrix_mods = _make_fake_mautrix()
|
||||
|
||||
# --- first connect: whoami returns no device_id, query_keys returns
|
||||
# zero devices → flag set to True ---
|
||||
mock_client1 = MagicMock()
|
||||
mock_client1.mxid = "@bot:example.org"
|
||||
mock_client1.device_id = None
|
||||
mock_client1.state_store = MagicMock()
|
||||
mock_client1.sync_store = MagicMock()
|
||||
mock_client1.crypto = None
|
||||
mock_client1.whoami = AsyncMock(return_value=MagicMock(
|
||||
user_id="@bot:example.org", device_id=None,
|
||||
))
|
||||
resolve_resp = MagicMock()
|
||||
resolve_resp.device_keys = {"@bot:example.org": {}}
|
||||
mock_client1.query_keys = AsyncMock(return_value=resolve_resp)
|
||||
mock_client1.sync = AsyncMock(return_value={"rooms": {"join": {"!room:server": {}}}})
|
||||
mock_client1.add_event_handler = MagicMock()
|
||||
mock_client1.handle_sync = MagicMock(return_value=[])
|
||||
mock_client1.api = MagicMock()
|
||||
mock_client1.api.token = "syt_test_access_token"
|
||||
mock_client1.api.session = MagicMock()
|
||||
mock_client1.api.session.close = AsyncMock()
|
||||
|
||||
mock_olm1 = MagicMock()
|
||||
mock_olm1.load = AsyncMock()
|
||||
mock_olm1.share_keys = AsyncMock()
|
||||
mock_olm1.share_keys_min_trust = None
|
||||
mock_olm1.send_keys_min_trust = None
|
||||
mock_olm1.account = MagicMock()
|
||||
mock_olm1.account.identity_keys = {"ed25519": "fake_key"}
|
||||
|
||||
fake_mautrix_mods["mautrix.client"].Client = MagicMock(return_value=mock_client1)
|
||||
fake_mautrix_mods["mautrix.crypto"].OlmMachine = MagicMock(return_value=mock_olm1)
|
||||
|
||||
import plugins.platforms.matrix.adapter as matrix_mod
|
||||
with patch.object(matrix_mod, "_check_e2ee_deps", return_value=True):
|
||||
with patch.dict("sys.modules", fake_mautrix_mods):
|
||||
with patch.object(adapter, "_refresh_dm_cache", AsyncMock()):
|
||||
with patch.object(adapter, "_sync_loop", AsyncMock(return_value=None)):
|
||||
await adapter.connect()
|
||||
|
||||
assert adapter._device_id_unverified is True
|
||||
await adapter.disconnect()
|
||||
|
||||
# --- second connect (same adapter, re-attaching): whoami returns a
|
||||
# real device_id this time → flag must be False ---
|
||||
mock_client2 = MagicMock()
|
||||
mock_client2.mxid = "@bot:example.org"
|
||||
mock_client2.device_id = None
|
||||
mock_client2.state_store = MagicMock()
|
||||
mock_client2.sync_store = MagicMock()
|
||||
mock_client2.crypto = None
|
||||
mock_client2.whoami = AsyncMock(return_value=MagicMock(
|
||||
user_id="@bot:example.org", device_id=None,
|
||||
))
|
||||
resolve_resp2 = MagicMock()
|
||||
resolve_dev = MagicMock()
|
||||
resolve_dev.keys = {"ed25519:DEV2": "fake_ed25519_key2"}
|
||||
resolve_resp2.device_keys = {"@bot:example.org": {"DEV2": resolve_dev}}
|
||||
verify_resp = MagicMock()
|
||||
verify_dev = MagicMock()
|
||||
verify_dev.keys = {"ed25519:DEV2": "fake_ed25519_key2"}
|
||||
verify_resp.device_keys = {"@bot:example.org": {"DEV2": verify_dev}}
|
||||
mock_client2.query_keys = AsyncMock(side_effect=[resolve_resp2, verify_resp])
|
||||
mock_client2.sync = AsyncMock(return_value={"rooms": {"join": {"!room:server": {}}}})
|
||||
mock_client2.add_event_handler = MagicMock()
|
||||
mock_client2.handle_sync = MagicMock(return_value=[])
|
||||
mock_client2.api = MagicMock()
|
||||
mock_client2.api.token = "syt_test_access_token"
|
||||
mock_client2.api.session = MagicMock()
|
||||
mock_client2.api.session.close = AsyncMock()
|
||||
|
||||
mock_olm2 = MagicMock()
|
||||
mock_olm2.load = AsyncMock()
|
||||
mock_olm2.share_keys = AsyncMock()
|
||||
mock_olm2.share_keys_min_trust = None
|
||||
mock_olm2.send_keys_min_trust = None
|
||||
mock_olm2.account = MagicMock()
|
||||
mock_olm2.account.identity_keys = {"ed25519": "fake_ed25519_key2"}
|
||||
|
||||
fake_mautrix_mods["mautrix.client"].Client = MagicMock(return_value=mock_client2)
|
||||
fake_mautrix_mods["mautrix.crypto"].OlmMachine = MagicMock(return_value=mock_olm2)
|
||||
|
||||
with patch.object(matrix_mod, "_check_e2ee_deps", return_value=True):
|
||||
with patch.dict("sys.modules", fake_mautrix_mods):
|
||||
with patch.object(adapter, "_refresh_dm_cache", AsyncMock()):
|
||||
with patch.object(adapter, "_sync_loop", AsyncMock(return_value=None)):
|
||||
result = await adapter.connect()
|
||||
|
||||
assert result is True
|
||||
assert adapter._device_id_unverified is False
|
||||
mock_client2.query_keys.assert_awaited()
|
||||
|
||||
await adapter.disconnect()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue