Phase 1C: Frontend unified contact UI
This commit is contained in:
@@ -167,7 +167,7 @@ async def test_ac1_search_returns_results(
|
||||
client, _ = search_authed_client
|
||||
mock_hybrid_search.return_value = [
|
||||
{
|
||||
"entity_type": "company",
|
||||
"entity_type": "contact",
|
||||
"entity_id": str(uuid.uuid4()),
|
||||
"title": "Company Alpha",
|
||||
"snippet": "IT company",
|
||||
@@ -186,7 +186,7 @@ async def test_ac1_search_returns_results(
|
||||
assert "results" in data
|
||||
assert isinstance(data["results"], list)
|
||||
assert len(data["results"]) > 0
|
||||
assert data["results"][0]["entity_type"] == "company"
|
||||
assert data["results"][0]["entity_type"] == "contact"
|
||||
assert data["results"][0]["title"] == "Company Alpha"
|
||||
assert "score" in data["results"][0]
|
||||
assert "query" in data
|
||||
@@ -209,13 +209,13 @@ async def test_ac2_search_entity_types_filter(
|
||||
|
||||
resp = await client.post(
|
||||
"/api/v1/search",
|
||||
json={"query": "test", "entity_types": ["company"]},
|
||||
json={"query": "test", "entity_types": ["contact"]},
|
||||
headers=ORIGIN_HEADER,
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
# Verify hybrid_search was called with entity_types=["company"]
|
||||
# Verify hybrid_search was called with entity_types=["contact"]
|
||||
call_kwargs = mock_hybrid_search.call_args
|
||||
assert call_kwargs.kwargs.get("entity_types") == ["company"]
|
||||
assert call_kwargs.kwargs.get("entity_types") == ["contact"]
|
||||
|
||||
|
||||
# ─── AC3: No auth → 401 ───
|
||||
@@ -322,7 +322,7 @@ async def test_ac8_similar_returns_results(
|
||||
resp = await client.post(
|
||||
"/api/v1/search/similar",
|
||||
json={
|
||||
"entity_type": "company",
|
||||
"entity_type": "contact",
|
||||
"entity_id": str(seed["company_a"].id),
|
||||
"limit": 5,
|
||||
},
|
||||
@@ -347,7 +347,7 @@ async def test_ac9_similar_tenant_isolation(
|
||||
resp = await client.post(
|
||||
"/api/v1/search/similar",
|
||||
json={
|
||||
"entity_type": "company",
|
||||
"entity_type": "contact",
|
||||
"entity_id": str(seed["company_b"].id),
|
||||
"limit": 5,
|
||||
},
|
||||
@@ -398,7 +398,7 @@ async def test_ac11_reindex_admin_200(
|
||||
client, _ = search_authed_client
|
||||
resp = await client.post(
|
||||
"/api/v1/search/reindex",
|
||||
json={"entity_types": ["company"]},
|
||||
json={"entity_types": ["contact"]},
|
||||
headers=ORIGIN_HEADER,
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
@@ -420,7 +420,7 @@ async def test_ac12_reindex_viewer_403(
|
||||
|
||||
resp = await search_client.post(
|
||||
"/api/v1/search/reindex",
|
||||
json={"entity_types": ["company"]},
|
||||
json={"entity_types": ["contact"]},
|
||||
headers=ORIGIN_HEADER,
|
||||
)
|
||||
assert resp.status_code == 403
|
||||
@@ -440,7 +440,7 @@ def test_rrf_fusion_combines_fts_and_vector_scores():
|
||||
{"id": "3", "title": "Result C", "score": 0.7},
|
||||
]
|
||||
|
||||
fused = rrf_fusion(fts_results, vec_results, "company")
|
||||
fused = rrf_fusion(fts_results, vec_results, "contact")
|
||||
|
||||
# All 3 unique IDs should be present
|
||||
ids = [str(r.get("id", "")) for r in fused]
|
||||
@@ -457,7 +457,7 @@ def test_rrf_fusion_combines_fts_and_vector_scores():
|
||||
|
||||
def test_rrf_fusion_empty_inputs():
|
||||
"""RRF fusion with empty inputs returns empty list."""
|
||||
fused = rrf_fusion([], [], "company")
|
||||
fused = rrf_fusion([], [], "contact")
|
||||
assert fused == []
|
||||
|
||||
|
||||
@@ -467,7 +467,7 @@ def test_rrf_fusion_fts_only():
|
||||
{"id": "1", "title": "A"},
|
||||
{"id": "2", "title": "B"},
|
||||
]
|
||||
fused = rrf_fusion(fts_results, [], "company")
|
||||
fused = rrf_fusion(fts_results, [], "contact")
|
||||
assert len(fused) == 2
|
||||
# First result (rank 0) should have higher score
|
||||
assert fused[0]["_score"] > fused[1]["_score"]
|
||||
@@ -547,7 +547,7 @@ def test_provider_get_all():
|
||||
p1 = MagicMock()
|
||||
p1.entity_type = "contact"
|
||||
p2 = MagicMock()
|
||||
p2.entity_type = "company"
|
||||
p2.entity_type = "contact"
|
||||
|
||||
registry.register(p1)
|
||||
registry.register(p2)
|
||||
@@ -556,7 +556,6 @@ def test_provider_get_all():
|
||||
assert len(all_providers) == 2
|
||||
entity_types = [p.entity_type for p in all_providers]
|
||||
assert "contact" in entity_types
|
||||
assert "company" in entity_types
|
||||
|
||||
|
||||
def test_provider_clear():
|
||||
@@ -577,13 +576,12 @@ def test_provider_get_entity_types():
|
||||
p1 = MagicMock()
|
||||
p1.entity_type = "contact"
|
||||
p2 = MagicMock()
|
||||
p2.entity_type = "company"
|
||||
p2.entity_type = "contact"
|
||||
registry.register(p1)
|
||||
registry.register(p2)
|
||||
|
||||
types = registry.get_entity_types()
|
||||
assert "contact" in types
|
||||
assert "company" in types
|
||||
assert len(types) == 2
|
||||
|
||||
|
||||
@@ -745,12 +743,12 @@ async def test_llm_aggregate_results_success():
|
||||
mock_agg_resp.choices = [MagicMock()]
|
||||
mock_agg_resp.choices[0].message.content = json.dumps({
|
||||
"summary": "2 Ergebnisse gefunden",
|
||||
"facets": {"types": {"company": 1, "contact": 1}},
|
||||
"suggestions": ["Filter by company"],
|
||||
"facets": {"types": {"contact": 2}},
|
||||
"suggestions": ["Filter by contact"],
|
||||
})
|
||||
with patch("litellm.acompletion", new_callable=AsyncMock, return_value=mock_agg_resp):
|
||||
results = [
|
||||
{"entity_type": "company", "title": "Company Alpha"},
|
||||
{"entity_type": "contact", "title": "Company Alpha"},
|
||||
{"entity_type": "contact", "title": "John Doe"},
|
||||
]
|
||||
result = await llm_aggregate_results(results, "test")
|
||||
@@ -997,7 +995,7 @@ async def test_index_contact(mock_index_entity, mock_factory, db_session: AsyncS
|
||||
@patch("app.plugins.builtins.unified_search.embedding.index_entity", new_callable=AsyncMock, return_value=True)
|
||||
async def test_index_company(mock_index_entity, mock_factory, db_session: AsyncSession):
|
||||
"""index_company calls index_entity."""
|
||||
from app.models.company import Company
|
||||
from app.models.contact import Contact as Company
|
||||
from app.models.tenant import Tenant
|
||||
from app.models.user import User
|
||||
from app.core.auth import hash_password
|
||||
@@ -1038,7 +1036,7 @@ async def test_index_company(mock_index_entity, mock_factory, db_session: AsyncS
|
||||
@patch("app.plugins.builtins.unified_search.embedding.index_entity", new_callable=AsyncMock, return_value=True)
|
||||
async def test_reindex(mock_index_entity, mock_factory, db_session: AsyncSession):
|
||||
"""reindex iterates over all entities of a type."""
|
||||
from app.models.company import Company
|
||||
from app.models.contact import Contact as Company
|
||||
from app.models.tenant import Tenant
|
||||
from app.models.user import User
|
||||
from app.core.auth import hash_password
|
||||
@@ -1070,7 +1068,7 @@ async def test_reindex(mock_index_entity, mock_factory, db_session: AsyncSession
|
||||
sf = async_sessionmaker(bind=db_session.bind, expire_on_commit=False, class_=AsyncSession)
|
||||
mock_factory.return_value = sf
|
||||
|
||||
await reindex({}, "company")
|
||||
await reindex({}, "contact")
|
||||
mock_index_entity.assert_called()
|
||||
|
||||
|
||||
@@ -1079,7 +1077,7 @@ async def test_reindex(mock_index_entity, mock_factory, db_session: AsyncSession
|
||||
@patch("app.plugins.builtins.unified_search.embedding.index_entity", new_callable=AsyncMock, return_value=True)
|
||||
async def test_embedding_batch(mock_index_entity, mock_factory, db_session: AsyncSession):
|
||||
"""embedding_batch finds entities without embedding."""
|
||||
from app.models.company import Company
|
||||
from app.models.contact import Contact as Company
|
||||
from app.models.tenant import Tenant
|
||||
from app.models.user import User
|
||||
from app.core.auth import hash_password
|
||||
@@ -1169,7 +1167,7 @@ def test_company_provider_get_embedding_text():
|
||||
"""CompanyProvider get_embedding_text is a callable method."""
|
||||
provider = CompanySearchProvider()
|
||||
assert hasattr(provider, "get_embedding_text")
|
||||
assert provider.entity_type == "company"
|
||||
assert provider.entity_type == "contact"
|
||||
|
||||
|
||||
def test_mail_provider_get_embedding_text():
|
||||
@@ -1210,7 +1208,7 @@ def test_company_provider_to_search_result():
|
||||
provider = CompanySearchProvider()
|
||||
entity = {"id": "456", "name": "Acme Corp", "description": "IT company"}
|
||||
result = provider.to_search_result(entity)
|
||||
assert result["entity_type"] == "company"
|
||||
assert result["entity_type"] == "contact"
|
||||
assert result["entity_id"] == "456"
|
||||
assert result["title"] == "Acme Corp"
|
||||
assert "IT company" in result["snippet"]
|
||||
|
||||
Reference in New Issue
Block a user