"""Spec for reconcile.py — written before the implementation. Fixtures are the REAL events pulled from the live "Events" calendar on 2026-06-06, trimmed to the fields reconcile uses. Crucially, none of them carry an `autoKey` (they were hand-created via the chat workflow), so these fixtures exercise exactly the case the fallback (title, date) matching must handle. """ import os import sys import unittest sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) import reconcile as R # noqa: E402 TIGNOR = { "summary": "Christopher Tignor + Julia Kent — Public Records (tickets req'd)", "start": {"dateTime": "2026-06-12T19:00:00-04:00", "timeZone": "America/New_York"}, } BRANCA = { "summary": 'Glenn Branca: Symphony No. 13 "Hallucination City" for 100 Guitars ' "— Lincoln Center (tickets rec'd)", "start": {"dateTime": "2026-06-12T19:30:00-04:00", "timeZone": "America/New_York"}, } SMORG = { # recurring instance (RRULE master expanded) "summary": "Smorgasburg @ The Oculus (day-of option)", "start": {"dateTime": "2026-06-19T11:00:00-04:00", "timeZone": "America/New_York"}, "recurringEventId": "dcu0np1bp18mknfpdjdpbidamg", } GOVISLAND = { # recurring all-day instance "summary": "Governors Island + Six Coasts (day-of option)", "start": {"date": "2026-06-19"}, "recurringEventId": "ul9ncfjd8po4augmn04k11g5es", } EXISTING = [TIGNOR, BRANCA, SMORG, GOVISLAND] def cand(title, start, **kw): return {"title": title, "start": start, **kw} class TestNormalize(unittest.TestCase): def test_strips_trailing_tag(self): self.assertEqual(R.normalize_title("Foo Bar (tickets req'd)"), "foo bar") def test_strips_trailing_dayof_tag(self): self.assertEqual(R.normalize_title("Smorgasburg @ The Oculus (day-of option)"), "smorgasburg @ the oculus") def test_strip_venue_em_dash(self): self.assertEqual( R.strip_venue("christopher tignor + julia kent — public records"), "christopher tignor + julia kent") def test_strip_venue_noop_without_separator(self): self.assertEqual(R.strip_venue("smorgasburg @ the oculus"), "smorgasburg @ the oculus") class TestStartDate(unittest.TestCase): def test_google_datetime(self): self.assertEqual(R.start_date(TIGNOR), "2026-06-12") def test_google_all_day(self): self.assertEqual(R.start_date(GOVISLAND), "2026-06-19") def test_candidate_iso_string(self): self.assertEqual(R.start_date(cand("x", "2026-07-10T20:00:00")), "2026-07-10") def test_candidate_all_day_date(self): self.assertEqual(R.start_date(cand("x", "2026-07-10", all_day=True)), "2026-07-10") class TestDedup(unittest.TestCase): def test_exact_re_report_is_duplicate(self): c = cand("Christopher Tignor + Julia Kent — Public Records (tickets req'd)", "2026-06-12T19:00:00") self.assertTrue(R.is_duplicate(c, TIGNOR)) def test_same_event_without_venue_or_tag(self): c = cand("Christopher Tignor + Julia Kent", "2026-06-12T19:00:00") self.assertTrue(R.is_duplicate(c, TIGNOR)) def test_branca_variant_tag_and_time_same_date(self): # model re-reports with the other tag (req'd vs rec'd), no venue, 7:00 vs 7:30 c = cand('Glenn Branca: Symphony No. 13 "Hallucination City" for 100 Guitars', "2026-06-12T19:00:00") self.assertTrue(R.is_duplicate(c, BRANCA)) def test_recurring_candidate_matches_series_on_other_date(self): c = cand("Smorgasburg @ The Oculus", "2026-07-03T11:00:00", recurrence="RRULE:FREQ=WEEKLY;BYDAY=FR") self.assertTrue(R.is_duplicate(c, SMORG)) def test_unrelated_event_same_date_not_duplicate(self): c = cand("Ryoji Ikeda — The Shed", "2026-06-12T20:00:00") # same date as Tignor/Branca self.assertFalse(any(R.is_duplicate(c, e) for e in EXISTING)) def test_same_title_different_date_not_duplicate(self): # a genuinely new one-off Tignor show on another date should NOT be suppressed c = cand("Christopher Tignor + Julia Kent — Public Records", "2026-09-04T19:00:00") self.assertFalse(any(R.is_duplicate(c, e) for e in EXISTING)) class TestReconcile(unittest.TestCase): def test_filters_and_stamps_autokey(self): cands = [ cand("Christopher Tignor + Julia Kent", "2026-06-12T19:00:00"), # dup cand("Ryoji Ikeda — The Shed", "2026-07-10T20:00:00"), # new ] out = R.reconcile(cands, EXISTING) self.assertEqual([i["title"] for i in out["insert"]], ["Ryoji Ikeda — The Shed"]) self.assertEqual(len(out["skip"]), 1) self.assertEqual(out["insert"][0]["autoKey"], R.auto_key("Ryoji Ikeda — The Shed", "2026-07-10")) def test_autokey_exact_match_short_circuits_title(self): # if an existing event carries OUR autoKey, match even when the display # name is unrecognizable (e.g. the user renamed it on the calendar) key = R.auto_key("Some Talk", "2026-08-01") ev = {"summary": "Totally Different Display Name", "start": {"date": "2026-08-01"}, "extendedProperties": {"private": {"autoKey": key}}} self.assertTrue(R.is_duplicate(cand("Some Talk", "2026-08-01"), ev)) def test_idempotent_second_run(self): # feeding the previous run's inserts back in (now present on the calendar) # produces zero new inserts first = R.reconcile([cand("Ryoji Ikeda — The Shed", "2026-07-10T20:00:00")], EXISTING) as_calendar_event = { "summary": "Ryoji Ikeda — The Shed", "start": {"dateTime": "2026-07-10T20:00:00-04:00"}, "extendedProperties": {"private": {"autoKey": first["insert"][0]["autoKey"]}}, } second = R.reconcile([cand("Ryoji Ikeda — The Shed", "2026-07-10T20:00:00")], EXISTING + [as_calendar_event]) self.assertEqual(second["insert"], []) class TestLoader(unittest.TestCase): def test_bare_list(self): self.assertEqual(R.as_event_list([TIGNOR]), [TIGNOR]) def test_events_wrapper(self): # shape returned by the list-events tool self.assertEqual(R.as_event_list({"events": [TIGNOR], "summary": "Events"}), [TIGNOR]) def test_items_wrapper(self): # raw Google API shape self.assertEqual(R.as_event_list({"items": [TIGNOR]}), [TIGNOR]) def test_unrecognized_returns_empty(self): self.assertEqual(R.as_event_list({"nope": 1}), []) class TestHardening(unittest.TestCase): """Regression tests for defects the adversarial review reproduced live.""" def test_intra_run_collapses_same_date_variants(self): # two titles for the same show on the same date, against an EMPTY calendar cands = [cand("Tim Hecker — Pioneer Works", "2026-08-07T20:00:00"), cand("Tim Hecker (tickets req'd)", "2026-08-07T20:00:00")] out = R.reconcile(cands, []) self.assertEqual(len(out["insert"]), 1) self.assertEqual(len(out["skip"]), 1) def test_recurring_candidate_matches_nonrecurring_same_title(self): # connector may return an expanded instance with no recurringEventId existing = {"summary": "Smorgasburg @ The Oculus", "start": {"dateTime": "2026-06-19T11:00:00-04:00"}} c = cand("Smorgasburg @ The Oculus", "2026-09-04T11:00:00", recurrence="RRULE:FREQ=WEEKLY;BYDAY=FR") self.assertTrue(R.is_duplicate(c, existing)) def test_null_extended_properties_does_not_crash(self): ev1 = {"summary": "X", "start": {"date": "2026-08-01"}, "extendedProperties": None} ev2 = {"summary": "Y", "start": {"date": "2026-08-01"}, "extendedProperties": {"private": None}} self.assertFalse(R.is_duplicate(cand("Totally Other", "2026-08-01"), ev1)) self.assertFalse(R.is_duplicate(cand("Totally Other", "2026-08-01"), ev2)) def test_today_drops_past_dated(self): cands = [cand("Old Show", "2020-01-01T20:00:00"), cand("Future Show", "2026-12-01T20:00:00")] out = R.reconcile(cands, [], today="2026-06-08") self.assertEqual([R.title_of(i) for i in out["insert"]], ["Future Show"]) self.assertEqual(len(out["dropped_past"]), 1) self.assertEqual(R.title_of(out["dropped_past"][0]["candidate"]), "Old Show") def test_max_caps_inserts_and_overflows_in_order(self): cands = [cand(f"Show {i}", f"2026-07-0{i}T20:00:00") for i in range(1, 6)] out = R.reconcile(cands, [], max_new=3) self.assertEqual([R.title_of(i) for i in out["insert"]], ["Show 1", "Show 2", "Show 3"]) self.assertEqual(len(out["dropped_overflow"]), 2) if __name__ == "__main__": unittest.main()