refactor(search): address PR #750 round 12 review feedback
Six review items raised; four required code changes (#3, #4, #5, #6) and two were resolved without code changes (#1 audit-only, #2 informational). * search/verification.py — clarify the granularity asymmetry between the whole-batch fail-open (structural API failure) and the per-item fail-open (single bad stored doc_id). Future readers no longer need to derive why the two paths have different blast radii from the code alone. * models/semantic.py — `dropped_document_count` description now explicitly notes that subtracting it from `verified_chunk_count` is not a meaningful operation, since the two fields count different units (documents vs chunks). Surfaces the unit mismatch where MCP clients actually see it. * server/semantic.py — clarify the per-doc_type over-fetch comment so the N×2 pre-merge Qdrant cost (vs the cross-app branch's 1×2) is explicit rather than implied by "same 2× over-fetch budget". * tests/unit/search/test_verification.py — add four new 429 unit tests (notes/news/files/deck) mirroring the existing 5xx-keeps pattern. Locks in that `_is_definitive_404_or_403` returns False for 429 so a future refactor cannot accidentally treat rate-limit responses as permanent revocations. Audit confirmation for review item #1: all four `WebDAVClient.get_file_info` call sites already handle the new `HTTPStatusError`-on-404 contract (verification.py:156, tests/integration/test_rag.py:139, tests/unit/client/test_webdav.py:153/190). No silent breakage internal to this repo. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
1ed8362f78
commit
104bbd390d
@@ -125,6 +125,24 @@ async def test_verify_notes_transient_5xx_keeps(mocker):
|
||||
assert result == {42}
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
async def test_verify_notes_429_keeps_as_transient(mocker):
|
||||
"""HTTP 429 (rate-limit) is transient, NOT a definitive 403/404 drop.
|
||||
|
||||
Locks in that ``_is_definitive_404_or_403`` returns False for 429 so a
|
||||
future refactor cannot accidentally treat rate-limit responses as
|
||||
permanent revocations and shrink result pages on every Nextcloud hiccup.
|
||||
"""
|
||||
notes_client = SimpleNamespace(
|
||||
get_note=mocker.AsyncMock(side_effect=_http_error(429))
|
||||
)
|
||||
client = SimpleNamespace(notes=notes_client, username="alice")
|
||||
|
||||
result = await _verify_notes(client, [_make_result(42)], _sem())
|
||||
|
||||
assert result == {42}
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
async def test_verify_notes_unexpected_exception_keeps(mocker):
|
||||
notes_client = SimpleNamespace(
|
||||
@@ -289,6 +307,27 @@ async def test_verify_news_items_transient_keeps_all(mocker):
|
||||
assert result == {1, 2, 3}
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
async def test_verify_news_items_429_keeps_as_transient(mocker):
|
||||
"""HTTP 429 from get_items must NOT collapse the batch (transient)."""
|
||||
news_client = SimpleNamespace(
|
||||
get_items=mocker.AsyncMock(side_effect=_http_error(429))
|
||||
)
|
||||
client = SimpleNamespace(news=news_client, username="alice")
|
||||
|
||||
result = await _verify_news_items(
|
||||
client,
|
||||
[
|
||||
_make_result(1, doc_type="news_item"),
|
||||
_make_result(2, doc_type="news_item"),
|
||||
_make_result(3, doc_type="news_item"),
|
||||
],
|
||||
_sem(),
|
||||
)
|
||||
|
||||
assert result == {1, 2, 3}
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
async def test_verify_news_items_unexpected_exception_keeps_all(mocker):
|
||||
"""A non-HTTP exception from get_items must keep all results (fail open).
|
||||
@@ -505,6 +544,23 @@ async def test_verify_files_transient_5xx_keeps(mocker):
|
||||
assert result == {7}
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
async def test_verify_files_429_keeps_as_transient(mocker):
|
||||
"""HTTP 429 from get_file_info must NOT silently drop file results."""
|
||||
webdav_client = SimpleNamespace(
|
||||
get_file_info=mocker.AsyncMock(side_effect=_http_error(429))
|
||||
)
|
||||
client = SimpleNamespace(webdav=webdav_client, username="alice")
|
||||
|
||||
result = await _verify_files(
|
||||
client,
|
||||
[_make_result(7, doc_type="file", metadata={"path": "x.txt"})],
|
||||
_sem(),
|
||||
)
|
||||
|
||||
assert result == {7}
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
async def test_verify_files_unexpected_exception_keeps(mocker):
|
||||
"""A non-HTTP exception from get_file_info must not drop the result.
|
||||
@@ -623,6 +679,29 @@ async def test_verify_deck_cards_transient_5xx_keeps(mocker):
|
||||
assert result == {42}
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
async def test_verify_deck_cards_429_keeps_as_transient(mocker):
|
||||
"""HTTP 429 from get_card must NOT silently shrink result pages."""
|
||||
deck_client = SimpleNamespace(
|
||||
get_card=mocker.AsyncMock(side_effect=_http_error(429))
|
||||
)
|
||||
client = SimpleNamespace(deck=deck_client, username="alice")
|
||||
|
||||
result = await _verify_deck_cards(
|
||||
client,
|
||||
[
|
||||
_make_result(
|
||||
42,
|
||||
doc_type="deck_card",
|
||||
metadata={"board_id": 1, "stack_id": 2},
|
||||
)
|
||||
],
|
||||
_sem(),
|
||||
)
|
||||
|
||||
assert result == {42}
|
||||
|
||||
|
||||
@pytest.mark.unit
|
||||
async def test_verify_deck_cards_unexpected_exception_keeps(mocker):
|
||||
"""Non-HTTP exception from get_card → fail-open, keep result."""
|
||||
|
||||
Reference in New Issue
Block a user