chorus/bot/tests/test_ping.py
pyr0ball 75ee9f8117 fix: prevent Discord embed field 400s from empty/oversized values
C1: build_ping_embed set sender_id field value="" whenever sender_id was
None, which manual_share.py always passes -- Discord rejects empty embed
field values (min length 1), so every manual-share ping failed to send.
Fixed by using a zero-width space sentinel that round-trips back to ""
(raw_content) or None (sender_id) in parse_ping_embed. Added regression
tests asserting no field value is ever empty after build_ping_embed runs.

C2: raw_content over 1024 chars (Discord's embed field cap) caused
inbox.send() to raise before the UID was marked seen, so oversized emails
were retried forever and never ingested. Added a defensive hard truncation
in build_ping_embed itself, plus an explicit truncation in email_ingest.py
before the embed is built (lossy for very long emails, acceptable for MVP).
2026-07-13 15:01:23 -07:00

92 lines
3.6 KiB
Python

from datetime import datetime, timezone
from chorus_bot import ping
def test_build_and_parse_round_trip():
captured_at = datetime(2026, 7, 13, 7, 0, 0, tzinfo=timezone.utc)
embed = ping.build_ping_embed(
modality="bh_email",
sender_id="jane@example.com",
snippet="I have some books to donate...",
raw_content="I have some books to donate. Full text goes here.",
captured_at=captured_at,
)
assert "bh_email" in embed.title or "bh_email" in str(embed.fields)
assert "I have some books to donate..." in (embed.description or "")
parsed = ping.parse_ping_embed(embed)
assert parsed["modality"] == "bh_email"
assert parsed["sender_id"] == "jane@example.com"
assert parsed["raw_content"] == "I have some books to donate. Full text goes here."
assert parsed["captured_at"] == captured_at
def test_build_ping_embed_handles_missing_sender():
embed = ping.build_ping_embed(
modality="voice",
sender_id=None,
snippet="call about donation",
raw_content="call about donation",
captured_at=datetime(2026, 7, 13, 8, 0, 0, tzinfo=timezone.utc),
)
parsed = ping.parse_ping_embed(embed)
assert parsed["sender_id"] is None
def test_build_and_parse_empty_raw_content():
"""Verify empty raw_content round-trips correctly without being converted to '(empty)'."""
captured_at = datetime(2026, 7, 13, 9, 0, 0, tzinfo=timezone.utc)
embed = ping.build_ping_embed(
modality="bh_email",
sender_id="test@example.com",
snippet="Empty message",
raw_content="",
captured_at=captured_at,
)
parsed = ping.parse_ping_embed(embed)
assert parsed["raw_content"] == "", "Empty raw_content should round-trip as empty string, not '(empty)'"
def test_build_ping_embed_never_produces_empty_field_value_for_empty_raw_content():
"""Regression guard for C1: Discord's API rejects embed field values with
length 0 (HTTP 400). Every field value must have len() >= 1 after
build_ping_embed runs, even when raw_content is ''."""
embed = ping.build_ping_embed(
modality="bh_email",
sender_id="test@example.com",
snippet="Empty message",
raw_content="",
captured_at=datetime(2026, 7, 13, 9, 0, 0, tzinfo=timezone.utc),
)
for field in embed.fields:
assert len(field.value) >= 1, f"field {field.name!r} has an empty value"
def test_build_ping_embed_never_produces_empty_field_value_for_sender_id_none():
"""Regression guard for C1: manual_share.py always calls build_ping_embed
with sender_id=None. Every field value must still have len() >= 1."""
embed = ping.build_ping_embed(
modality="voice",
sender_id=None,
snippet="call about donation",
raw_content="call about donation",
captured_at=datetime(2026, 7, 13, 8, 0, 0, tzinfo=timezone.utc),
)
for field in embed.fields:
assert len(field.value) >= 1, f"field {field.name!r} has an empty value"
def test_build_ping_embed_truncates_long_raw_content():
"""Regression guard for C2: embed field values over 1024 chars are
rejected by Discord. build_ping_embed must truncate defensively."""
long_body = "x" * 2000
embed = ping.build_ping_embed(
modality="bh_email",
sender_id="jane@example.com",
snippet="long email",
raw_content=long_body,
captured_at=datetime(2026, 7, 13, 9, 0, 0, tzinfo=timezone.utc),
)
raw_content_field = next(f for f in embed.fields if f.name == "raw_content")
assert len(raw_content_field.value) <= 1024
assert "truncated" in raw_content_field.value