fb_posts: past its publish time, a scheduled post reads as published

Facebook does not call back and the reconciliation was never built, so a
row that stayed scheduled forever was a lie of omission. Decided by Mike:
once the time passes with nothing else reported, the post is published.
status keeps what the publisher last said; state is what a reader acts
on, and the open-only list and cancel use state. Cancel on a post whose
time has passed says so.
This commit is contained in:
2026-09-04 19:53:08 -04:00
parent 55e4c67b9a
commit ea22ed53e7
3 changed files with 36 additions and 8 deletions
+8
View File
@@ -402,6 +402,14 @@ check("cancel recorded", c["status"] == "cancelled" and c["cancelled_by"] == "mi
and not any(p["id"] == c["id"] for p in S.list_fb_posts(include_done=False)))
r3 = S.upsert_fb_post({"id": "troop-73/2026-09-12-canoe", "status": "cancelled", "cancelled_by": "ntfy button"})
check("publisher-reported cancel stamps cancelled_at", r3["cancelled_at"] and r3["cancelled_by"] == "ntfy button")
past = S.upsert_fb_post({"id": "pack-73/2020-01-01-old", "status": "scheduled", "scheduled_for": "2020-01-01T12:00:00+00:00"})
future = S.upsert_fb_post({"id": "pack-73/2036-01-01-new", "status": "scheduled", "scheduled_for": "2036-01-01T12:00:00+00:00"})
check("a scheduled post past its time reads as published; a future one stays scheduled",
S.get_fb_post("pack-73/2020-01-01-old")["state"] == "published" and S.get_fb_post("pack-73/2036-01-01-new")["state"] == "scheduled"
and S.get_fb_post("pack-73/2020-01-01-old")["status"] == "scheduled")
check("open-only list drops the past one and keeps the future one",
[p["id"] for p in S.list_fb_posts(include_done=False) if p["id"].startswith("pack-73/20")] == ["pack-73/2036-01-01-new"])
check("cancelled beats the clock", S.fb_state({"status": "cancelled", "scheduled_for": "2020-01-01T00:00:00+00:00"}) == "cancelled")
check("fbposts caps: read for leaders, ingest admin-only and scopable", "fbposts:read" in I.CAPS["leader"] and "fbposts:ingest" in I.CAPS["admin"]
and "fbposts:ingest" not in I.KEY_UNSCOPABLE)
con = S.connect(); con.execute("DELETE FROM fb_posts"); con.commit(); con.close()