stackchain-dashboard/tests/test_deadline_reminders.py
timmy 02a472f841
All checks were successful
CI / lint (pull_request) Successful in 1m35s
CI / build-release (pull_request) Successful in 5s
CI / release-candidate (pull_request) Has been skipped
perf: bulk-authorize push devices (Closes #725)
2026-08-13 09:29:18 +00:00

341 lines
12 KiB
Python

import asyncio
import json
from datetime import datetime, timezone
import pytest
from src import gitea_proxy
from src.push_notifications import PushConfiguration, dispatch_deadline_reminders
from src.push_subscription_store import PushSubscriptionStore
@pytest.mark.anyio
async def test_deadline_reminder_sends_one_private_local_day_digest_and_deduplicates(tmp_path):
store = PushSubscriptionStore(tmp_path / "push.sqlite3")
store.upsert("device-a", {
"endpoint": "https://push.example/device-a",
"keys": {"p256dh": "public-key", "auth": "auth-secret"},
})
store.set_deadline_preferences(
"device-a", enabled=True, timezone="America/New_York", reminder_hour=9
)
sent = []
async def assigned():
return {
"complete": True,
"items": [
{
"id": 42,
"title": "Private launch plan",
"repository": {"full_name": "private/repo"},
"due_date": "2026-08-14T12:00:00Z",
},
{"id": 43, "due_date": "2026-08-20T12:00:00Z"},
],
}
async def send(_subscription, payload):
sent.append(json.loads(payload))
config = PushConfiguration("public", "private", "mailto:ops@example.com")
now = datetime(2026, 8, 13, 13, 5, tzinfo=timezone.utc)
assert await dispatch_deadline_reminders(store, config, assigned, send, now=now) == 1
assert await dispatch_deadline_reminders(store, config, assigned, send, now=now) == 0
assert sent == [{
"title": "1 deadline needs attention",
"body": "Open Agenda to review or replan it.",
"route": "#/my-work/agenda",
"protect_route": "#/my-work/agenda/protect-today",
"tag": "stackchain-deadline-digest-2026-08-13",
"deadline_count": 1,
}]
assert "Private launch plan" not in json.dumps(sent)
assert "private/repo" not in json.dumps(sent)
@pytest.mark.anyio
async def test_deadline_reminder_counts_calendar_days_per_device_timezone(tmp_path):
store = PushSubscriptionStore(tmp_path / "push.sqlite3")
for device, timezone_name in (("tokyo", "Asia/Tokyo"), ("la", "America/Los_Angeles")):
store.upsert(device, {
"endpoint": f"https://push.example/{device}",
"keys": {"p256dh": "public-key", "auth": "auth-secret"},
})
store.set_deadline_preferences(device, enabled=True, timezone=timezone_name, reminder_hour=0)
async def assigned():
return {"complete": True, "items": [
{"id": 1, "due_date": "2026-08-15T23:59:59Z"},
{"id": 2, "due_date": "2026-08-16T23:59:59Z"},
]}
sent = {}
async def send(subscription, payload):
device = subscription["endpoint"].rsplit("/", 1)[-1]
sent[device] = json.loads(payload)["deadline_count"]
delivered = await dispatch_deadline_reminders(
store, PushConfiguration("public", "private", "mailto:ops@example.com"),
assigned, send, now=datetime(2026, 8, 13, 23, 30, tzinfo=timezone.utc),
)
assert delivered == 2
assert sent == {"tokyo": 2, "la": 1}
@pytest.mark.anyio
async def test_deadline_reminder_fails_closed_for_incomplete_snapshot_and_before_local_hour(tmp_path):
store = PushSubscriptionStore(tmp_path / "push.sqlite3")
store.upsert("device-a", {
"endpoint": "https://push.example/device-a",
"keys": {"p256dh": "public-key", "auth": "auth-secret"},
})
store.set_deadline_preferences(
"device-a", enabled=True, timezone="America/Los_Angeles", reminder_hour=9
)
sent = []
async def incomplete():
return {"complete": False, "items": [{"id": 42, "due_date": "2026-08-14T12:00:00Z"}]}
async def complete():
return {"complete": True, "items": [{"id": 42, "due_date": "2026-08-14T12:00:00Z"}]}
async def send(_subscription, payload):
sent.append(payload)
config = PushConfiguration("public", "private", "mailto:ops@example.com")
assert await dispatch_deadline_reminders(
store, config, incomplete, send,
now=datetime(2026, 8, 13, 18, 0, tzinfo=timezone.utc),
) == 0
assert await dispatch_deadline_reminders(
store, config, complete, send,
now=datetime(2026, 8, 13, 15, 0, tzinfo=timezone.utc),
) == 0
assert sent == []
@pytest.mark.anyio
async def test_deadline_reminder_skips_snapshot_until_a_device_reaches_its_local_hour(tmp_path):
store = PushSubscriptionStore(tmp_path / "push.sqlite3")
store.upsert("device-a", {
"endpoint": "https://push.example/device-a",
"keys": {"p256dh": "public-key", "auth": "auth-secret"},
})
store.set_deadline_preferences(
"device-a", enabled=True, timezone="America/Los_Angeles", reminder_hour=9
)
snapshot_calls = 0
async def assigned():
nonlocal snapshot_calls
snapshot_calls += 1
return {"complete": True, "items": []}
delivered = await dispatch_deadline_reminders(
store,
PushConfiguration("public", "private", "mailto:ops@example.com"),
assigned,
now=datetime(2026, 8, 13, 15, 0, tzinfo=timezone.utc),
)
assert delivered == 0
assert snapshot_calls == 0
@pytest.mark.anyio
async def test_deadline_reminder_skips_snapshot_after_every_device_was_delivered_today(tmp_path):
store = PushSubscriptionStore(tmp_path / "push.sqlite3")
for session_id, timezone_name in (
("device-a", "UTC"),
("device-b", "America/New_York"),
):
store.upsert(session_id, {
"endpoint": f"https://push.example/{session_id}",
"keys": {"p256dh": "public-key", "auth": "auth-secret"},
})
store.set_deadline_preferences(
session_id, enabled=True, timezone=timezone_name, reminder_hour=9
)
store.mark_deadline_reminder_delivered(session_id, "2026-08-13")
snapshot_calls = 0
async def assigned():
nonlocal snapshot_calls
snapshot_calls += 1
return {"complete": True, "items": []}
delivered = await dispatch_deadline_reminders(
store,
PushConfiguration("public", "private", "mailto:ops@example.com"),
assigned,
now=datetime(2026, 8, 13, 15, 0, tzinfo=timezone.utc),
)
assert delivered == 0
assert snapshot_calls == 0
def test_deadline_preferences_persist_on_the_existing_device_subscription(tmp_path):
store = PushSubscriptionStore(tmp_path / "push.sqlite3")
store.upsert("device-a", {
"endpoint": "https://push.example/device-a",
"keys": {"p256dh": "public-key", "auth": "auth-secret"},
})
store.set_deadline_preferences(
"device-a", enabled=True, timezone="Europe/London", reminder_hour=8
)
assert store.deadline_preferences("device-a") == {
"enabled": True,
"timezone": "Europe/London",
"reminder_hour": 8,
}
@pytest.mark.anyio
async def test_assigned_deadline_snapshot_is_pagination_complete(monkeypatch):
pages = {
1: {"items": [{"id": 1}], "has_more": True},
2: {"items": [{"id": 2}], "has_more": False},
}
async def work_page(stream, page, limit):
assert stream == "issue"
assert limit == 50
return pages[page]
monkeypatch.setattr(gitea_proxy, "work_page", work_page)
assert await gitea_proxy.assigned_issue_snapshot() == {
"items": [{"id": 1}, {"id": 2}],
"complete": True,
}
@pytest.mark.anyio
async def test_competing_workers_send_one_deadline_digest(tmp_path):
path = tmp_path / "push.sqlite3"
first = PushSubscriptionStore(path)
second = PushSubscriptionStore(path)
first.upsert("device-a", {
"endpoint": "https://push.example/device-a",
"keys": {"p256dh": "public-key", "auth": "auth-secret"},
})
first.set_deadline_preferences(
"device-a", enabled=True, timezone="UTC", reminder_hour=9
)
sending = __import__("asyncio").Event()
release = __import__("asyncio").Event()
sent = []
async def assigned():
return {"complete": True, "items": [{"id": 1, "due_date": "2026-08-14T00:00:00Z"}]}
async def send(_subscription, payload):
sent.append(payload)
sending.set()
await release.wait()
config = PushConfiguration("public", "private", "mailto:ops@example.com")
now = datetime(2026, 8, 13, 10, 0, tzinfo=timezone.utc)
active = __import__("asyncio").create_task(
dispatch_deadline_reminders(first, config, assigned, send, now=now)
)
await sending.wait()
competing = await dispatch_deadline_reminders(second, config, assigned, send, now=now)
release.set()
assert competing == 0
assert await active == 1
assert len(sent) == 1
@pytest.mark.anyio
async def test_deadline_reminders_bound_fanout_and_isolate_failed_devices(tmp_path):
store = PushSubscriptionStore(tmp_path / "push.sqlite3")
for index in range(4):
session_id = f"device-{index}"
store.upsert(session_id, {
"endpoint": f"https://push.example/{session_id}",
"keys": {"p256dh": "public-key", "auth": "auth-secret"},
})
store.set_deadline_preferences(
session_id, enabled=True, timezone="UTC", reminder_hour=9
)
active = 0
peak = 0
two_active = asyncio.Event()
async def assigned():
return {"complete": True, "items": [{"id": 1, "due_date": "2026-08-14T00:00:00Z"}]}
async def send(subscription, _payload):
nonlocal active, peak
active += 1
peak = max(peak, active)
if active == 2:
two_active.set()
await asyncio.wait_for(two_active.wait(), timeout=0.5)
active -= 1
if subscription["endpoint"].endswith("device-1"):
raise RuntimeError("provider failed")
delivered = await dispatch_deadline_reminders(
store,
PushConfiguration("public", "private", "mailto:ops@example.com"),
assigned,
send,
now=datetime(2026, 8, 13, 10, 0, tzinfo=timezone.utc),
max_concurrency=2,
)
assert delivered == 3
assert peak == 2
@pytest.mark.anyio
async def test_deadline_reminders_bulk_authorize_due_devices_and_prune_inactive(tmp_path):
store = PushSubscriptionStore(tmp_path / "push.sqlite3")
for session_id in ("active-device", "revoked-device"):
store.upsert(session_id, {
"endpoint": f"https://push.example/{session_id}",
"keys": {"p256dh": "public-key", "auth": "auth-secret"},
})
store.set_deadline_preferences(
session_id, enabled=True, timezone="UTC", reminder_hour=9
)
authorization_calls = []
sent = []
async def assigned():
return {"complete": True, "items": [{"id": 1, "due_date": "2026-08-14T00:00:00Z"}]}
async def session_statuses(management_ids):
authorization_calls.append(list(management_ids))
return {
management_id: "active" if management_id == "active-device" else "revoked"
for management_id in management_ids
}
async def send(subscription, _payload):
sent.append(subscription["endpoint"])
delivered = await dispatch_deadline_reminders(
store,
PushConfiguration("public", "private", "mailto:ops@example.com"),
assigned,
send,
now=datetime(2026, 8, 13, 10, 0, tzinfo=timezone.utc),
session_statuses=session_statuses,
)
assert delivered == 1
assert authorization_calls == [["active-device", "revoked-device"]]
assert sent == ["https://push.example/active-device"]
assert store.is_subscribed("revoked-device") is False