SonarCloud: - Resolve 6 S5332 hotspots (http→https in test fixture URLs). - S6418: hoist the unauthenticated AsyncOpenAI placeholder to a named constant + NOSONAR (genuine non-secret; gateway ignores it when unauthenticated). - Fix two reliability bugs: None-index guard in the gateway token-cache test (S2259) and float `> 0.0` instead of `!= 0.0` in the sentinel test (S1244). - status.py idle path sleeps 0.1s instead of sleep(0) (S7491); NOSONAR on the protocol-required async no-await aclose() stubs (S7503). Claude review: - Remove three leftover debug print() calls in app.py (logger.info already covers them). - payload_backfill: drop parsed_at from the backfilled-keys docstring (it is per-document state, not a deployment scalar); add a clean 404 precondition for BasicAuth deployments without an OAuth token verifier. - status.py: task_status typed TaskStatus | None (drop type: ignore). - nats.py: TODO to thread etags for file/deck/news; note etag default → None. - factory: warn on unknown INGEST_BUS_URL scheme; raise ValueError instead of assert for the external-mode preconditions. - docs/configuration.md: document the decomposition hook-point env vars + that nats-py ships core (lazy-imported) and external+bus uses two NATS connections. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
92 lines
3.1 KiB
Python
92 lines
3.1 KiB
Python
"""Collection metadata source + sentinel (design §10.1)."""
|
|
|
|
from types import SimpleNamespace
|
|
|
|
import httpx
|
|
|
|
from nextcloud_mcp_server.config import Settings
|
|
from nextcloud_mcp_server.vector import collection_metadata as cm
|
|
from nextcloud_mcp_server.vector.payload_keys import EMBEDDING_IDENTITY
|
|
|
|
|
|
async def test_qdrant_read_hit(mocker):
|
|
client = mocker.AsyncMock()
|
|
client.retrieve.return_value = [
|
|
SimpleNamespace(
|
|
payload={
|
|
EMBEDDING_IDENTITY: "mistral-embed",
|
|
cm.CHUNKING_CONFIG: {"chunk_size": 1024, "chunk_overlap": 100},
|
|
cm.IS_SENTINEL: True,
|
|
}
|
|
)
|
|
]
|
|
meta = await cm.read_collection_metadata(client, "col", Settings())
|
|
assert meta["embedding_identity"] == "mistral-embed"
|
|
assert meta["chunking_config"]["chunk_size"] == 1024
|
|
|
|
|
|
async def test_qdrant_miss_falls_back_to_env(mocker):
|
|
client = mocker.AsyncMock()
|
|
client.retrieve.return_value = [] # no sentinel
|
|
settings = Settings(document_chunk_size=2048, document_chunk_overlap=200)
|
|
meta = await cm.read_collection_metadata(client, "col", settings)
|
|
assert meta == cm.env_default_metadata(settings)
|
|
assert meta["chunking_config"]["chunk_size"] == 2048
|
|
|
|
|
|
async def test_qdrant_error_falls_back_to_env(mocker):
|
|
client = mocker.AsyncMock()
|
|
client.retrieve.side_effect = RuntimeError("qdrant down")
|
|
settings = Settings()
|
|
meta = await cm.read_collection_metadata(client, "col", settings)
|
|
assert meta == cm.env_default_metadata(settings)
|
|
|
|
|
|
async def test_api_source(mocker):
|
|
settings = Settings(
|
|
collection_metadata_source="api",
|
|
collection_metadata_api_url="https://cp",
|
|
)
|
|
|
|
def handler(request: httpx.Request) -> httpx.Response:
|
|
assert request.url.path == "/v1/qdrant-collections/col/metadata"
|
|
return httpx.Response(
|
|
200,
|
|
json={
|
|
"embedding_identity": "amazon.titan-embed-text-v2:0",
|
|
"chunking_config": {"chunk_size": 512, "chunk_overlap": 50},
|
|
},
|
|
)
|
|
|
|
transport = httpx.MockTransport(handler)
|
|
orig = httpx.AsyncClient
|
|
mocker.patch.object(
|
|
httpx,
|
|
"AsyncClient",
|
|
lambda *a, **k: orig(*a, **{**k, "transport": transport}),
|
|
)
|
|
|
|
meta = await cm.read_collection_metadata(mocker.AsyncMock(), "col", settings)
|
|
assert meta["embedding_identity"] == "amazon.titan-embed-text-v2:0"
|
|
|
|
|
|
async def test_upsert_sentinel_builds_point(mocker):
|
|
client = mocker.AsyncMock()
|
|
await cm.upsert_sentinel(
|
|
client,
|
|
"col",
|
|
embedding_identity="mistral-embed",
|
|
chunking_config={"chunk_size": 2048, "chunk_overlap": 200},
|
|
dimension=4,
|
|
)
|
|
client.upsert.assert_awaited_once()
|
|
kwargs = client.upsert.await_args.kwargs
|
|
assert kwargs["collection_name"] == "col"
|
|
point = kwargs["points"][0]
|
|
assert str(point.id) == cm.SENTINEL_POINT_ID
|
|
# Non-zero dense (cosine-safe), empty sparse.
|
|
assert point.vector["dense"][0] > 0.0
|
|
assert len(point.vector["dense"]) == 4
|
|
assert point.payload[EMBEDDING_IDENTITY] == "mistral-embed"
|
|
assert point.payload[cm.IS_SENTINEL] is True
|