Files
mcp-nextcloud/tests/unit/vector/test_scanner_app_gating.py
T
Chris CoutinhoandClaude Opus 4.8 b11d1b17a3 test(vector): address PR #873 round-1 review
- Extract `_app_enabled` to a module-level helper so the gate predicate is
  unit-tested directly instead of via an inline copy that could drift.
- Move `import logging` to module scope in test_scanner_app_gating.py.
- Harden `get_enabled_apps` OCS-envelope parsing (`X or {}` / `or []`) so a
  present-but-null `ocs`/`data` coerces to empty instead of raising on
  `None.get`; add parametrized malformed-envelope tests.
- Use https:// in the test request URL (SonarCloud S5332 hotspot).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 20:04:00 +02:00

63 lines
2.1 KiB
Python

"""Unit tests for the vector scanner's enabled-app gating helpers.
``scan_user_documents`` skips polling apps the user doesn't have enabled (those
polls 404 and flood tenant logs). ``_get_enabled_apps_or_none`` resolves the
enabled-app set, returning ``None`` on any failure so the caller falls back to
scanning every app (the prior behaviour) rather than silently halting indexing;
``_app_enabled`` is the gate predicate applied per app.
"""
import logging
from unittest.mock import AsyncMock
import pytest
from httpx import HTTPStatusError, Request, Response
from nextcloud_mcp_server.vector.scanner import (
_app_enabled,
_get_enabled_apps_or_none,
)
pytestmark = pytest.mark.unit
async def test_returns_enabled_set_on_success():
nc_client = AsyncMock()
nc_client.get_enabled_apps = AsyncMock(return_value={"files", "notes"})
result = await _get_enabled_apps_or_none(nc_client, "alice", scan_id=1234)
assert result == {"files", "notes"}
async def test_returns_none_when_detection_raises(caplog):
nc_client = AsyncMock()
request = Request("GET", "https://nc.test/ocs/v2.php/core/navigation/apps")
nc_client.get_enabled_apps = AsyncMock(
side_effect=HTTPStatusError(
"boom", request=request, response=Response(503, request=request)
)
)
caplog.set_level(logging.WARNING, logger="nextcloud_mcp_server.vector.scanner")
result = await _get_enabled_apps_or_none(nc_client, "alice", scan_id=1234)
# None signals scan-all fallback; _app_enabled treats `None` as
# "every app enabled" so indexing never silently stops.
assert result is None
assert "scanning all apps" in caplog.text
def test_none_set_enables_every_app():
"""A None set means detection failed, so every app must be scanned."""
assert _app_enabled("news", None) is True
assert _app_enabled("deck", None) is True
def test_concrete_set_gates_precisely():
"""A resolved set scans only the apps it contains."""
enabled = {"notes", "files"}
assert _app_enabled("notes", enabled) is True
assert _app_enabled("news", enabled) is False
assert _app_enabled("deck", enabled) is False