diff --git a/app/routers/contacts.py b/app/routers/contacts.py index c6df141..abb5efb 100644 --- a/app/routers/contacts.py +++ b/app/routers/contacts.py @@ -193,9 +193,11 @@ async def sync_contacts_from_radio() -> dict: count = 0 for public_key, contact_data in contacts.items(): - await ContactRepository.upsert( - Contact.from_radio_dict(public_key, contact_data, on_radio=True) - ) + lower_key = public_key.lower() + await ContactRepository.upsert(Contact.from_radio_dict(lower_key, contact_data, on_radio=True)) + claimed = await MessageRepository.claim_prefix_messages(lower_key) + if claimed > 0: + logger.info("Claimed %d prefix DM message(s) for contact %s", claimed, public_key[:12]) count += 1 logger.info("Synced %d contacts from radio", count) diff --git a/tests/test_contacts_router.py b/tests/test_contacts_router.py index 62f1496..76c6073 100644 --- a/tests/test_contacts_router.py +++ b/tests/test_contacts_router.py @@ -14,7 +14,7 @@ import pytest from meshcore import EventType from app.database import Database -from app.repository import ContactRepository +from app.repository import ContactRepository, MessageRepository # Sample 64-char hex public keys for testing KEY_A = "aa" * 32 # aaaa...aa @@ -301,6 +301,36 @@ class TestSyncContacts: assert response.status_code == 503 + @pytest.mark.asyncio + async def test_sync_claims_prefix_messages(self, test_db, client): + """Syncing contacts promotes prefix-stored DM messages to the full key.""" + await MessageRepository.create( + msg_type="PRIV", + text="hello from prefix", + received_at=1700000000, + conversation_key=KEY_A[:12], + sender_timestamp=1700000000, + ) + + mock_mc = MagicMock() + mock_result = MagicMock() + mock_result.type = EventType.OK + mock_result.payload = {KEY_A: {"adv_name": "Alice", "type": 1, "flags": 0}} + mock_mc.commands.get_contacts = AsyncMock(return_value=mock_result) + + with patch("app.dependencies.radio_manager") as mock_dep_rm: + mock_dep_rm.is_connected = True + mock_dep_rm.meshcore = mock_mc + + response = await client.post("/api/contacts/sync") + + assert response.status_code == 200 + assert response.json()["synced"] == 1 + + messages = await MessageRepository.get_all(conversation_key=KEY_A) + assert len(messages) == 1 + assert messages[0].conversation_key == KEY_A.lower() + class TestAddRemoveRadio: """Test add-to-radio and remove-from-radio endpoints."""