Two ingest-robustness fixes from card 309 (OHR-Bench smoke-test triage). WebDAV paths flowed through the client already URL-decoded (unquote on the PROPFIND/REPORT <d:href>, or raw MCP-tool input), so a '#' reached httpx as a URL fragment and silently truncated the request -> spurious 404 on otherwise valid files (e.g. law filenames with '#', commas, double/trailing spaces). Route every caller-path builder through a new _webdav_path helper that percent-encodes the path once (preserving separators); the MOVE/COPY Destination header is encoded too. Vector-sync runs inside anyio task groups, so a child-task failure surfaced as a BaseExceptionGroup whose str() is the useless "unhandled errors in a TaskGroup (N sub-exception)" -- hiding the real ConnectError operators need. Add format_exception_group to flatten the group to its leaf exceptions and use it at the broad catch/log sites in processor.py and oauth_sync.py. Refs: Deck board 12 card 309 (AC #4 filename handling, AC #2 observability). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
42 lines
1.3 KiB
Python
42 lines
1.3 KiB
Python
"""Unit tests for vector-sync error formatting (card 309)."""
|
|
|
|
import httpx
|
|
import pytest
|
|
|
|
from nextcloud_mcp_server.vector._errors import format_exception_group
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_format_plain_exception_returns_repr():
|
|
exc = httpx.ConnectError("Connection error")
|
|
assert format_exception_group(exc) == repr(exc)
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_format_exception_group_names_leaf_cause():
|
|
"""A single-child group must surface the real ConnectError, not the group's
|
|
useless 'unhandled errors in a TaskGroup' default message."""
|
|
leaf = httpx.ConnectError("Connection error")
|
|
group = BaseExceptionGroup("unhandled errors in a TaskGroup", [leaf])
|
|
|
|
formatted = format_exception_group(group)
|
|
|
|
assert "ConnectError" in formatted
|
|
assert "unhandled errors in a TaskGroup" not in formatted
|
|
assert "1 sub-exception" in formatted
|
|
|
|
|
|
@pytest.mark.unit
|
|
def test_format_nested_exception_group_flattens_all_leaves():
|
|
inner = BaseExceptionGroup(
|
|
"inner", [ValueError("bad value"), httpx.ConnectError("conn")]
|
|
)
|
|
outer = BaseExceptionGroup("outer", [inner, RuntimeError("boom")])
|
|
|
|
formatted = format_exception_group(outer)
|
|
|
|
assert "ValueError" in formatted
|
|
assert "ConnectError" in formatted
|
|
assert "RuntimeError" in formatted
|
|
assert "3 sub-exception(s)" in formatted
|