stackchain-dashboard/tests/test_start_day_reminders.py
timmy 6fb547e8ce
All checks were successful
CI / lint (pull_request) Successful in 3m3s
CI / build-release (pull_request) Successful in 6s
CI / browser-journey (pull_request) Successful in 3m40s
CI / release-candidate (pull_request) Has been skipped
fix: preserve synchronous Today reconciliation
2026-08-20 05:48:08 +00:00

150 lines
5.2 KiB
Python

import json
from datetime import datetime, timezone
from pathlib import Path
import pytest
from types import SimpleNamespace
from src import main
from src.push_notifications import PushConfiguration, dispatch_start_day_reminders
from src.push_subscription_store import PushSubscriptionStore
@pytest.mark.anyio
async def test_start_day_reminder_sends_one_private_prompt_for_due_plan(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_start_day_preferences(
"device-a", enabled=True, timezone="America/New_York", reminder_hour=9
)
sent = []
async def tomorrow():
return {
"revision": 3,
"ids": ["private/repo#42"],
"plan_date": "2026-08-20",
"timezone": "America/New_York",
}
async def send(_subscription, payload):
sent.append(json.loads(payload))
configuration = PushConfiguration("public", "private", "mailto:ops@example.com")
now = datetime(2026, 8, 20, 13, 5, tzinfo=timezone.utc)
assert await dispatch_start_day_reminders(
store, configuration, tomorrow, send, now=now
) == 1
assert await dispatch_start_day_reminders(
store, configuration, tomorrow, send, now=now
) == 0
assert sent == [{
"title": "Your planned day is ready",
"body": "Open Stackchain to prepare Today.",
"route": "#/my-work/start-day",
"tag": "stackchain-start-day-2026-08-20",
"plan_date": "2026-08-20",
}]
assert "private/repo" not in json.dumps(sent)
@pytest.mark.anyio
async def test_start_day_reminder_waits_for_hour_and_nonempty_due_plan(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_start_day_preferences(
"device-a", enabled=True, timezone="America/Los_Angeles", reminder_hour=9
)
sent = []
async def send(_subscription, payload):
sent.append(payload)
configuration = PushConfiguration("public", "private", "mailto:ops@example.com")
before_hour = datetime(2026, 8, 20, 15, 59, tzinfo=timezone.utc)
async def due_plan():
return {"ids": ["one"], "plan_date": "2026-08-20"}
assert await dispatch_start_day_reminders(
store, configuration, due_plan, send, now=before_hour
) == 0
async def empty_plan():
return {"ids": [], "plan_date": "2026-08-20"}
assert await dispatch_start_day_reminders(
store, configuration, empty_plan, send,
now=datetime(2026, 8, 20, 16, 0, tzinfo=timezone.utc),
) == 0
assert sent == []
@pytest.mark.anyio
async def test_authenticated_device_can_enable_start_day_reminders(tmp_path, monkeypatch):
store = PushSubscriptionStore(tmp_path / "push.sqlite3")
store.upsert("device-a", {
"endpoint": "https://push.example/device-a",
"keys": {"p256dh": "public-key", "auth": "auth-secret"},
})
monkeypatch.setattr(main, "_push_subscription_store", store)
async def management_id(_session):
return "device-a"
monkeypatch.setattr(main.dashboard_auth, "session_management_id", management_id)
request = SimpleNamespace(state=SimpleNamespace(dashboard_session=object()))
payload = main.StartDayReminderPayload(
enabled=True, timezone="America/New_York", reminder_hour=8
)
assert await main.update_start_day_reminders(payload, request) == {
"start_day_enabled": True,
"start_day_timezone": "America/New_York",
"start_day_reminder_hour": 8,
}
assert store.start_day_preferences("device-a") == {
"enabled": True, "timezone": "America/New_York", "reminder_hour": 8,
}
@pytest.mark.anyio
async def test_worker_snapshot_reads_the_confirmed_accounts_tomorrow_plan(monkeypatch):
async def current_user():
return {"login": "Timmy"}
class Store:
def get_tomorrow(self, login):
assert login == "Timmy"
return {"ids": ["one"], "plan_date": "2026-08-20"}
monkeypatch.setattr(main, "current_user", current_user)
monkeypatch.setattr(main, "_today_store", lambda: Store())
assert await main._start_day_plan_snapshot() == {
"ids": ["one"], "plan_date": "2026-08-20",
}
def test_mobile_settings_and_launch_route_wire_start_day_into_existing_promotion():
frontend = Path(__file__).parents[1] / "frontend"
html = (frontend / "index.html").read_text()
dashboard = (frontend / "dashboard.js").read_text()
assert 'id="push-start-day"' in html
assert 'id="push-start-day-hour"' in html
assert 'id="device-setup-start-day"' in html
assert "startDayControl:qs('#push-start-day')" in dashboard
assert "startDayHour:qs('#push-start-day-hour')" in dashboard
assert "window.location.hash === '#/my-work/start-day'" in dashboard
assert "promoteTomorrowIfDue(plan).finally(() =>" in dashboard
assert "onRemotePlan: async plan =>" not in dashboard
assert "openMobileStartDay()" in dashboard