144 lines
5.8 KiB
Python
144 lines
5.8 KiB
Python
from datetime import datetime, timezone
|
|
import discord
|
|
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
|
|
|
|
|
|
def test_build_and_parse_round_trip_includes_direction():
|
|
captured_at = datetime(2026, 7, 13, 7, 0, 0, tzinfo=timezone.utc)
|
|
embed = ping.build_ping_embed(
|
|
modality="lead_found", sender_id=None, snippet="found post",
|
|
raw_content="Someone posted about donating books on Nextdoor",
|
|
captured_at=captured_at, direction="outbound",
|
|
)
|
|
parsed = ping.parse_ping_embed(embed)
|
|
assert parsed["direction"] == "outbound"
|
|
|
|
|
|
def test_build_ping_embed_direction_defaults_to_inbound():
|
|
embed = ping.build_ping_embed(
|
|
modality="bh_email", sender_id="jane@example.com", snippet="hi",
|
|
raw_content="hi", captured_at=datetime(2026, 7, 13, 7, 0, 0, tzinfo=timezone.utc),
|
|
)
|
|
parsed = ping.parse_ping_embed(embed)
|
|
assert parsed["direction"] == "inbound"
|
|
|
|
|
|
def test_parse_ping_embed_defaults_direction_when_field_missing():
|
|
"""Regression guard: embeds built before this change have no direction
|
|
field at all -- parsing one must not raise and must default to inbound."""
|
|
embed = discord.Embed(title="bh_email", description="hi")
|
|
embed.add_field(name="modality", value="bh_email", inline=True)
|
|
embed.add_field(name="sender_id", value="jane@example.com", inline=True)
|
|
embed.add_field(name="captured_at", value="2026-07-13T07:00:00+00:00", inline=True)
|
|
embed.add_field(name="raw_content", value="hi", inline=False)
|
|
|
|
parsed = ping.parse_ping_embed(embed)
|
|
assert parsed["direction"] == "inbound"
|
|
|
|
|
|
def test_build_ping_embed_sets_image_when_url_given():
|
|
embed = ping.build_ping_embed(
|
|
modality="lead_found", sender_id=None, snippet="found post",
|
|
raw_content="Found on Nextdoor",
|
|
captured_at=datetime(2026, 7, 13, 9, 0, 0, tzinfo=timezone.utc),
|
|
image_url="https://cdn.discordapp.com/attachments/1/2/screenshot.png",
|
|
)
|
|
assert embed.image.url == "https://cdn.discordapp.com/attachments/1/2/screenshot.png"
|
|
|
|
|
|
def test_build_ping_embed_no_image_by_default():
|
|
embed = ping.build_ping_embed(
|
|
modality="bh_email", sender_id="jane@example.com", snippet="hi",
|
|
raw_content="hi", captured_at=datetime(2026, 7, 13, 9, 0, 0, tzinfo=timezone.utc),
|
|
)
|
|
assert embed.image.url is None
|