Don't use prefix matching if we can help it

This commit is contained in:
Jack Kingsman
2026-02-10 22:05:59 -08:00
parent bfdccc4a94
commit 1aa26c05d0
13 changed files with 227 additions and 55 deletions
+27 -3
View File
@@ -90,7 +90,7 @@ class TestCreateContact:
with (
patch(
"app.routers.contacts.ContactRepository.get_by_key_or_prefix",
"app.routers.contacts.ContactRepository.get_by_key",
new_callable=AsyncMock,
return_value=None,
),
@@ -123,7 +123,7 @@ class TestCreateContact:
from fastapi.testclient import TestClient
with patch(
"app.routers.contacts.ContactRepository.get_by_key_or_prefix",
"app.routers.contacts.ContactRepository.get_by_key",
new_callable=AsyncMock,
return_value=None,
):
@@ -160,7 +160,7 @@ class TestCreateContact:
with (
patch(
"app.routers.contacts.ContactRepository.get_by_key_or_prefix",
"app.routers.contacts.ContactRepository.get_by_key",
new_callable=AsyncMock,
return_value=existing,
),
@@ -220,6 +220,30 @@ class TestGetContact:
assert response.status_code == 404
def test_get_ambiguous_prefix_returns_409(self):
from fastapi.testclient import TestClient
from app.repository import AmbiguousPublicKeyPrefixError
with patch(
"app.routers.contacts.ContactRepository.get_by_key_or_prefix",
new_callable=AsyncMock,
side_effect=AmbiguousPublicKeyPrefixError(
"abcd12",
[
"abcd120000000000000000000000000000000000000000000000000000000000",
"abcd12ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
],
),
):
from app.main import app
client = TestClient(app)
response = client.get("/api/contacts/abcd12")
assert response.status_code == 409
assert "ambiguous" in response.json()["detail"].lower()
class TestMarkRead:
"""Test POST /api/contacts/{public_key}/mark-read."""
+41 -1
View File
@@ -3,7 +3,7 @@
import pytest
from app.database import Database
from app.repository import ContactRepository, MessageRepository
from app.repository import AmbiguousPublicKeyPrefixError, ContactRepository, MessageRepository
@pytest.fixture
@@ -117,3 +117,43 @@ async def test_duplicate_with_same_text_and_null_timestamp_rejected(test_db):
received_at=received_at,
)
assert msg_id2 is None # duplicate rejected
@pytest.mark.asyncio
async def test_get_by_key_prefix_returns_none_when_ambiguous(test_db):
"""Ambiguous prefixes should not resolve to an arbitrary contact."""
key1 = "abc1230000000000000000000000000000000000000000000000000000000000"
key2 = "abc123ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
await ContactRepository.upsert({"public_key": key1, "name": "A"})
await ContactRepository.upsert({"public_key": key2, "name": "B"})
contact = await ContactRepository.get_by_key_prefix("abc123")
assert contact is None
@pytest.mark.asyncio
async def test_get_by_key_or_prefix_raises_on_ambiguous_prefix(test_db):
"""Prefix lookup should raise when multiple contacts match."""
key1 = "abc1230000000000000000000000000000000000000000000000000000000000"
key2 = "abc123ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
await ContactRepository.upsert({"public_key": key1, "name": "A"})
await ContactRepository.upsert({"public_key": key2, "name": "B"})
with pytest.raises(AmbiguousPublicKeyPrefixError):
await ContactRepository.get_by_key_or_prefix("abc123")
@pytest.mark.asyncio
async def test_get_by_key_or_prefix_prefers_exact_full_key(test_db):
"""Exact key lookup works even when the shorter prefix is ambiguous."""
key1 = "abc1230000000000000000000000000000000000000000000000000000000000"
key2 = "abc123ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
await ContactRepository.upsert({"public_key": key1, "name": "A"})
await ContactRepository.upsert({"public_key": key2, "name": "B"})
contact = await ContactRepository.get_by_key_or_prefix(key2.upper())
assert contact is not None
assert contact.public_key == key2
+30
View File
@@ -4,6 +4,7 @@ import asyncio
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from fastapi import HTTPException
from meshcore import EventType
from app.models import (
@@ -13,6 +14,7 @@ from app.models import (
SendChannelMessageRequest,
SendDirectMessageRequest,
)
from app.repository import AmbiguousPublicKeyPrefixError
from app.routers.messages import send_channel_message, send_direct_message
@@ -123,6 +125,34 @@ class TestOutgoingDMBotTrigger:
call_kwargs = mock_bot.call_args[1]
assert call_kwargs["sender_name"] is None
@pytest.mark.asyncio
async def test_send_dm_ambiguous_prefix_returns_409(self):
"""Ambiguous destination prefix should fail instead of selecting a random contact."""
mc = _make_mc()
with (
patch("app.routers.messages.require_connected", return_value=mc),
patch(
"app.repository.ContactRepository.get_by_key_or_prefix",
new=AsyncMock(
side_effect=AmbiguousPublicKeyPrefixError(
"abc123",
[
"abc1230000000000000000000000000000000000000000000000000000000000",
"abc123ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
],
)
),
),
):
with pytest.raises(HTTPException) as exc_info:
await send_direct_message(
SendDirectMessageRequest(destination="abc123", text="Hello")
)
assert exc_info.value.status_code == 409
assert "ambiguous" in exc_info.value.detail.lower()
class TestOutgoingChannelBotTrigger:
"""Test that sending a channel message triggers bots with is_outgoing=True."""