Files
lukasandClaude Opus 4.8 a5ec0d21cb Initial commit: webmail client (M0/M1)
Three-pane webmail client.
- Backend: FastAPI + async SQLAlchemy + arq worker (Postgres/Redis), IMAP/SMTP
  sync, threading, search, drafts/outbox with undo, attachments, 2FA.
- Frontend: React 18 + Vite + TypeScript + TanStack Query + Tailwind; three-pane
  UI with rich-text composer, light/dark themes, resizable panels.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 23:20:34 +02:00

56 lines
2.0 KiB
Python

"""Message-ID and subject normalization (SPEC §7.1, §7.3)."""
from __future__ import annotations
from app.threading_.normalize import (
is_synthetic,
normalize_message_id,
normalize_reference_list,
normalize_subject,
synthesize_message_id,
)
def test_normalize_message_id_strips_brackets_and_lowercases_domain() -> None:
assert normalize_message_id("<Abc.123@Example.COM>") == "Abc.123@example.com"
assert normalize_message_id(" <x@Y.Z> ") == "x@y.z"
# local part case is preserved, domain lowercased
assert normalize_message_id("<CaseSensitive@Domain.Com>") == "CaseSensitive@domain.com"
def test_normalize_message_id_rejects_invalid() -> None:
assert normalize_message_id(None) is None
assert normalize_message_id("no-at-sign") is None
assert normalize_message_id("<>") is None
assert normalize_message_id("@nolocal.com") is None
def test_normalize_reference_list_order_and_dedupe() -> None:
raw = "<a@x.com> <b@y.com> <a@X.com>"
assert normalize_reference_list(raw) == ["a@x.com", "b@y.com"]
assert normalize_reference_list("") == []
assert normalize_reference_list(None) == []
def test_normalize_reference_list_whitespace_separated_without_brackets() -> None:
assert normalize_reference_list("a@x.com b@y.com") == ["a@x.com", "b@y.com"]
def test_synthetic_id() -> None:
sid = synthesize_message_id(b"raw bytes here")
assert sid.endswith("@synthetic.local")
assert is_synthetic(sid)
assert not is_synthetic("real@example.com")
assert not is_synthetic(None)
# deterministic
assert synthesize_message_id(b"raw bytes here") == sid
def test_normalize_subject_strips_prefixes() -> None:
assert normalize_subject("Re: Hello") == "Hello"
assert normalize_subject("RE: FW: Fwd: Hello world") == "Hello world"
assert normalize_subject("Odp: Cześć") == "Cześć" # Polish reply prefix
assert normalize_subject("PD: X") == "X"
assert normalize_subject("No prefix") == "No prefix"
assert normalize_subject("") == ""