#!/usr/bin/env python3 """Test the Gmail-draft message construction (the part that doesn't need live Gmail): subject/body parsing, reply-target resolution, and the RFC822 build incl. threading headers. The actual drafts.create call is exercised on the box. Synthetic data only. Run: cd backend && python3 email_integration/test_compose.py """ import base64 import os import sqlite3 import sys sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) from email_integration import compose as cp # noqa: E402 FAILS = [] def check(cond, msg): print((" PASS " if cond else " FAIL ") + msg) if not cond: FAILS.append(msg) def main(): s, b = cp._parse_subject_body("Subject: Following up\n\nHi Sarah,\n\nthanks for the call.") check(s == "Following up" and b.startswith("Hi Sarah"), "parses 'Subject:' line + body") s2, b2 = cp._parse_subject_body("No subject prefix here") check(s2 == "" and b2 == "No subject prefix here", "no subject line -> empty subject, full body") c = sqlite3.connect(":memory:") c.row_factory = sqlite3.Row c.executescript(""" CREATE TABLE emails(id TEXT, rfc_message_id TEXT, gmail_thread_id TEXT, sent_at TEXT, is_matched INT); CREATE TABLE email_investor_links(email_id TEXT, fundraising_investor_id TEXT, matched_address TEXT); """) c.execute("INSERT INTO emails VALUES('e1','','t1','2026-06-01',1)") c.execute("INSERT INTO email_investor_links VALUES('e1','inv1','lp@harborvine.example')") c.commit() t = cp._reply_target(c, "inv1") check(t and t["to"] == "lp@harborvine.example" and t["thread_id"] == "t1" and t["in_reply_to"] == "", "reply target resolves LP address + thread + in-reply-to") check(cp._reply_target(c, "nope") is None, "no history -> no reply target") raw = cp._build_raw("grant@ten31.xyz", "lp@x.example", "Hi", "Body text here", "") dec = base64.urlsafe_b64decode(raw).decode("utf-8", "replace") check("From: grant@ten31.xyz" in dec and "To: lp@x.example" in dec, "RFC822 has From + To") check("Subject: Hi" in dec and "Body text here" in dec, "RFC822 has Subject + body") check("In-Reply-To: " in dec and "References: " in dec, "threading headers set for replies") raw2 = cp._build_raw("a@b.co", "c@d.co", "", "body", None) dec2 = base64.urlsafe_b64decode(raw2).decode("utf-8", "replace") check("Subject: (no subject)" in dec2 and "In-Reply-To" not in dec2, "no subject / no thread -> fresh email") if FAILS: print(f"\nFAILED ({len(FAILS)})") for f in FAILS: print(" - " + f) sys.exit(1) print("\nALL PASS (gmail compose message construction)") if __name__ == "__main__": main()