No blockers raised; residual cleanup: - processor.py: guard int(doc_task.doc_id) with is_valid_nextcloud_doc_id in the mail_message branch (consistent with search/context.py + the verifier). - mail metadata symmetry: store `bcc` in file_metadata and the Qdrant payload alongside cc (build_mail_content already emits a Bcc: line). - server/mail.py: extract _cap_attachment_content helper (byte-accurate cap) and unit-test it (small/None/oversized/multibyte). - client/mail.py: give the synthetic OCS-error Response an explicit empty body; add a test that a traversal-style attachment_id is percent-encoded. - models/mail.py: clarify ListMessagesResponse.total_count is the page count, not the mailbox total. Deferred (Deck #376): _potentially_deleted doc_type-in-key. It's pre-existing and spans ~30 sites across the notes/news/deck/file/mail scanners (whose deletion paths have no unit coverage), so it belongs in its own focused PR rather than expanding this mail PR's blast radius into other doc types. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
39 lines
1.3 KiB
Python
39 lines
1.3 KiB
Python
"""Unit tests for server/mail.py helper logic."""
|
|
|
|
import pytest
|
|
|
|
from nextcloud_mcp_server.server.mail import (
|
|
MAX_ATTACHMENT_CONTENT_BYTES,
|
|
_cap_attachment_content,
|
|
)
|
|
|
|
pytestmark = pytest.mark.unit
|
|
|
|
|
|
def test_small_content_passes_through():
|
|
assert _cap_attachment_content("hello") == "hello"
|
|
|
|
|
|
def test_none_content_passes_through():
|
|
assert _cap_attachment_content(None) is None
|
|
|
|
|
|
def test_oversized_content_replaced_with_byte_sentinel():
|
|
oversized = "a" * (MAX_ATTACHMENT_CONTENT_BYTES + 1)
|
|
result = _cap_attachment_content(oversized)
|
|
assert result != oversized
|
|
assert "too large to inline" in result
|
|
# Reports the actual UTF-8 byte count, not character count.
|
|
assert f"{MAX_ATTACHMENT_CONTENT_BYTES + 1} bytes" in result
|
|
|
|
|
|
def test_multibyte_counted_in_bytes_not_chars():
|
|
# Each "€" is 3 UTF-8 bytes; a string just under the byte cap in characters
|
|
# can still exceed it in bytes.
|
|
char_count = (MAX_ATTACHMENT_CONTENT_BYTES // 3) + 1
|
|
content = "€" * char_count
|
|
# Under the cap by character count, over it by byte count -> capped.
|
|
assert len(content) <= MAX_ATTACHMENT_CONTENT_BYTES
|
|
assert len(content.encode("utf-8")) > MAX_ATTACHMENT_CONTENT_BYTES
|
|
assert "too large to inline" in _cap_attachment_content(content)
|