test(tests): update suite for sync‑twenty migration and drop obsolete gmail/notion sync tests

This commit is contained in:
Prad Nukala committed 2026-08-26 11:54:35 -04:00
1 parent 7007c4aa9d
commit b552b70783
8 files changed
+201 -312

No files matched your search

+21 -62
View File
@@ -21,8 +21,8 @@ They also pin the review findings on the fix itself:
4. The vocabulary block must not split Step 1's numbered list: section-scoped
reads of Step 1 must still see items 2-4.
5. /html-report's bucket map needs a catch-all so no tracker value drops out of
the stats silently, and /notion-sync must normalise space forms before
writing Status (Notion auto-creates a select option per unique string).
the stats silently, and /sync-twenty must normalise space forms before
mapping to Twenty's fixed stage enum.
6. /apply and /interview make status decisions and must anchor them to the
block, not restate an ad-hoc set.
@@ -37,9 +37,8 @@ REPO = Path(__file__).resolve().parent.parent
COMMANDS = REPO / ".claude" / "commands"
OUTCOME = COMMANDS / "outcome.md"
GMAIL_SYNC = COMMANDS / "gmail-sync.md"
HTML_REPORT = COMMANDS / "html-report.md"
NOTION_SYNC = COMMANDS / "notion-sync.md"
SYNC_TWENTY = COMMANDS / "sync-twenty.md"
APPLY = COMMANDS / "apply.md"
INTERVIEW = COMMANDS / "interview.md"
@@ -221,64 +220,32 @@ class ReadersBucketMap(unittest.TestCase):
"it is part of the archive `outcome.md` Status: enum, not a tracker CSV value",
)
def test_gmail_sync_references_vocabulary_block(self):
"""gmail-sync must defer to /outcome's vocabulary block for the
open-application set, not hardcode the final-status set with
space spellings that diverge from the writer."""
step2_text = section(GMAIL_SYNC, "## Step 2: Load State")
def test_sync_twenty_normalises_status_before_write(self):
"""The status → stage table must map legacy space spellings to the
canonical enum before writing. Twenty's stage field is a fixed enum,
so an unmapped space form either errors or silently drops the update."""
step6_text = section(SYNC_TWENTY, "## Step 6: Upsert Opportunity Records")
self.assertIn(
"Tracker status vocabulary",
step2_text,
"/gmail-sync Step 2 must reference the /outcome vocabulary block "
"instead of restating the final-status set with its own spellings",
)
self.assertNotIn(
"no response",
step2_text,
"/gmail-sync Step 2 must not restate the space spellings locally - "
"the vocabulary block is the single source for what counts as final, "
"and a second local list is what drifted in #298",
"normalise legacy space spellings",
step6_text,
"/sync-twenty Step 6 must map space forms per the /outcome vocabulary block",
)
def test_notion_sync_normalises_status_before_write(self):
"""Step 4 must map legacy space spellings to canonical before setting
Status. Notion auto-creates a select option per unique string, so pushing
a space form would give an existing database two options for one status
and split closed applications across two filter buckets."""
step4_text = section(NOTION_SYNC, "## Step 4: Upsert Database Rows")
self.assertIn(
"never push a space form",
step4_text,
"/notion-sync Step 4 must never write a space-form status to Notion",
)
self.assertIn(
"Tracker status vocabulary",
step4_text,
"/notion-sync Step 4 must map space forms per the /outcome vocabulary block",
)
def test_notion_sync_uses_underscore_status_spellings(self):
"""Notion Status select options must match canonical tracker spellings
so that upserted values are consistent with what /outcome writes."""
step3_text = section(NOTION_SYNC, "## Step 3: Load Sync State and Locate the Database")
def test_sync_twenty_uses_underscore_status_spellings(self):
"""The stage mapping table must key on canonical tracker spellings
so the sync recognises exactly what /outcome writes."""
step6_text = section(SYNC_TWENTY, "## Step 6: Upsert Opportunity Records")
self.assertIn(
"no_response",
step3_text,
"/notion-sync Step 3 must list `no_response` (underscore) as a Status "
"option so it matches what /outcome writes to the tracker",
step6_text,
"/sync-twenty Step 6 must map `no_response` (underscore) so it matches "
"what /outcome writes to the tracker",
)
self.assertIn(
"offer_declined",
step3_text,
"/notion-sync Step 3 must list `offer_declined` (underscore) as a Status "
"option so it matches what /outcome writes to the tracker",
)
self.assertNotIn(
"no response",
step3_text,
"/notion-sync Step 3 must not list 'no response' (space) as the primary "
"option — Notion creates a distinct select value for each unique string, "
"so mixing spellings creates duplicate options in the database",
step6_text,
"/sync-twenty Step 6 must map `offer_declined` (underscore) so it matches "
"what /outcome writes to the tracker",
)
@@ -311,14 +278,6 @@ class ReaderCases(unittest.TestCase):
"excluded from every statistic below",
"drafted rows must be excluded from every statistic, not counted as sent",
),
# /gmail-sync staleness check skips drafted
(
GMAIL_SYNC,
"## Step 9: Staleness Check",
"Skip `drafted` rows here",
"the staleness check must skip drafted rows — nothing was sent, "
"so nobody is late replying",
),
# /apply's append-vs-update decision anchors to the vocabulary
(
APPLY,