fix(search): address PR #813 review (viz verify-on-read, owners cache, docs)
- viz_routes: run verify_search_results before returning results. After the accessible_owners expansion the viz can surface OTHER users' shared docs, so it must drop ones the caller can no longer access (revoked share) — same as the nc_semantic_search tool path. (Blocking review item.) - access_filter: cache list_accessible_owners per user for 30s to keep the OCS shares round-trip off the search hot path (failures aren't cached); document the single-page OCS limitation; add a clear_accessible_owners_cache() test helper. Comment the empty-accessible_owners MatchAny([]) edge case. - verification: comment why cross-user eviction is a deliberate no-op (eviction is scoped to the querying user's id, so a recipient's revoked access never deletes the owner's points; the recipient self-heals via accessible_owners). - algorithms: declare SearchResult.original_score (set by the viz route) so the now-precisely-typed result list type-checks. - tests: cross-user eviction-no-op safety test; autouse owners-cache reset in the access_filter + shared-search tests; replace async-no-await qdrant fakes with AsyncMock (clears SonarCloud S7503). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
6206f4a634
commit
b1fac2d7a8
@@ -15,6 +15,8 @@ real-Nextcloud flow (share + verify-on-read) lives in
|
||||
``test_acl_shared_search.py``.
|
||||
"""
|
||||
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
import pytest
|
||||
from qdrant_client import AsyncQdrantClient
|
||||
from qdrant_client.models import Distance, PointStruct, VectorParams
|
||||
@@ -82,12 +84,9 @@ async def seeded_collection(monkeypatch):
|
||||
await client.upsert(collection_name=collection, points=points, wait=True)
|
||||
|
||||
# Point the algorithm at the in-memory client + deterministic embeddings.
|
||||
async def _fake_get_qdrant_client():
|
||||
return client
|
||||
|
||||
monkeypatch.setattr(
|
||||
"nextcloud_mcp_server.search.semantic.get_qdrant_client",
|
||||
_fake_get_qdrant_client,
|
||||
AsyncMock(return_value=client),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"nextcloud_mcp_server.search.semantic.get_embedding_service",
|
||||
|
||||
@@ -23,6 +23,7 @@ proves the real share → accessible_owners → filter → verify chain.
|
||||
|
||||
import os
|
||||
import uuid
|
||||
from unittest.mock import AsyncMock
|
||||
|
||||
import pytest
|
||||
from httpx import BasicAuth
|
||||
@@ -32,12 +33,25 @@ from qdrant_client.models import Distance, PointStruct, VectorParams
|
||||
from nextcloud_mcp_server.client import NextcloudClient
|
||||
from nextcloud_mcp_server.config import get_settings
|
||||
from nextcloud_mcp_server.embedding import SimpleEmbeddingProvider
|
||||
from nextcloud_mcp_server.search.access_filter import list_accessible_owners
|
||||
from nextcloud_mcp_server.search.access_filter import (
|
||||
clear_accessible_owners_cache,
|
||||
list_accessible_owners,
|
||||
)
|
||||
from nextcloud_mcp_server.search.semantic import SemanticSearchAlgorithm
|
||||
from nextcloud_mcp_server.search.verification import verify_search_results
|
||||
|
||||
pytestmark = pytest.mark.integration
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _reset_owners_cache():
|
||||
"""Reset the process-global accessible-owners cache around each test so a
|
||||
real OCS share created in a fixture isn't masked by a stale cached entry."""
|
||||
clear_accessible_owners_cache()
|
||||
yield
|
||||
clear_accessible_owners_cache()
|
||||
|
||||
|
||||
_DOC_TEXT = "Confidential quarterly infrastructure budget and capacity plan"
|
||||
|
||||
|
||||
@@ -127,12 +141,9 @@ async def seeded_semantic(monkeypatch, shared_file):
|
||||
wait=True,
|
||||
)
|
||||
|
||||
async def _fake_get_qdrant_client():
|
||||
return client
|
||||
|
||||
monkeypatch.setattr(
|
||||
"nextcloud_mcp_server.search.semantic.get_qdrant_client",
|
||||
_fake_get_qdrant_client,
|
||||
AsyncMock(return_value=client),
|
||||
)
|
||||
monkeypatch.setattr(
|
||||
"nextcloud_mcp_server.search.semantic.get_embedding_service",
|
||||
|
||||
Reference in New Issue
Block a user