feat: re-alert updated Web Push threads (Closes #571)
This commit is contained in:
1 parent
257bc0110e
commit
e9271f5aab
4 files changed
+237
-38
No files matched your search
@@ -1,6 +1,7 @@
|
||||
import json
|
||||
import asyncio
|
||||
import os
|
||||
import sqlite3
|
||||
from types import SimpleNamespace
|
||||
|
||||
import httpx
|
||||
@@ -56,6 +57,65 @@ def test_subscription_store_delivers_each_unread_thread_once_per_device(tmp_path
|
||||
assert second == []
|
||||
|
||||
|
||||
def test_subscription_store_reopens_a_delivered_thread_when_its_revision_changes(tmp_path):
|
||||
store = PushSubscriptionStore(tmp_path / "push.sqlite3")
|
||||
store.upsert("session-a", {
|
||||
"endpoint": "https://push.example/device-a",
|
||||
"keys": {"p256dh": "public-key", "auth": "auth-secret"},
|
||||
})
|
||||
|
||||
first = store.claim_unseen({42: "2026-08-11T12:00:00Z"})
|
||||
store.mark_delivered("session-a", first[0].thread_revisions)
|
||||
unchanged = store.claim_unseen({42: "2026-08-11T12:00:00Z"})
|
||||
updated = store.claim_unseen({42: "2026-08-11T12:05:00Z"})
|
||||
|
||||
assert unchanged == []
|
||||
assert updated[0].thread_revisions == ((42, "2026-08-11T12:05:00Z"),)
|
||||
|
||||
|
||||
def test_subscription_store_does_not_realert_for_an_older_snapshot_revision(tmp_path):
|
||||
store = PushSubscriptionStore(tmp_path / "push.sqlite3")
|
||||
store.upsert("session-a", {
|
||||
"endpoint": "https://push.example/device-a",
|
||||
"keys": {"p256dh": "public-key", "auth": "auth-secret"},
|
||||
})
|
||||
store.mark_delivered("session-a", {42: "2026-08-11T12:05:00Z"})
|
||||
|
||||
assert store.claim_unseen({42: "2026-08-11T12:00:00Z"}) == []
|
||||
|
||||
|
||||
def test_subscription_store_migrates_legacy_delivery_without_replaying_it(tmp_path):
|
||||
path = tmp_path / "push.sqlite3"
|
||||
subscription = {
|
||||
"endpoint": "https://push.example/device-a",
|
||||
"keys": {"p256dh": "public-key", "auth": "auth-secret"},
|
||||
}
|
||||
with sqlite3.connect(path) as connection:
|
||||
connection.executescript("""
|
||||
CREATE TABLE push_subscriptions (
|
||||
session_id TEXT PRIMARY KEY,
|
||||
endpoint TEXT NOT NULL UNIQUE,
|
||||
subscription_json TEXT NOT NULL
|
||||
);
|
||||
CREATE TABLE push_deliveries (
|
||||
session_id TEXT NOT NULL,
|
||||
thread_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (session_id, thread_id)
|
||||
);
|
||||
""")
|
||||
connection.execute(
|
||||
"INSERT INTO push_subscriptions VALUES (?, ?, ?)",
|
||||
("session-a", subscription["endpoint"], json.dumps(subscription)),
|
||||
)
|
||||
connection.execute("INSERT INTO push_deliveries VALUES (?, ?)", ("session-a", 42))
|
||||
|
||||
store = PushSubscriptionStore(path)
|
||||
|
||||
assert store.claim_unseen({42: "2026-08-11T12:00:00Z"}) == []
|
||||
updated = store.claim_unseen({42: "2026-08-11T12:05:00Z"})
|
||||
assert updated[0].thread_revisions == ((42, "2026-08-11T12:05:00Z"),)
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_managed_session_active_applies_configured_idle_deadline(monkeypatch):
|
||||
calls = []
|
||||
@@ -150,6 +210,37 @@ async def test_dispatch_sends_one_privacy_safe_deep_link_per_new_thread(tmp_path
|
||||
assert "Secret title" not in json.dumps(sent)
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_dispatch_realerts_when_the_same_thread_has_a_newer_revision(tmp_path):
|
||||
store = PushSubscriptionStore(tmp_path / "push.sqlite3")
|
||||
store.upsert("session-a", {
|
||||
"endpoint": "https://push.example/device-a",
|
||||
"keys": {"p256dh": "public-key", "auth": "auth-secret"},
|
||||
})
|
||||
revision = "2026-08-11T12:00:00Z"
|
||||
sent = []
|
||||
|
||||
async def unread():
|
||||
return {"items": [{
|
||||
"id": 42,
|
||||
"updated_at": revision,
|
||||
"repository": "private/repo",
|
||||
"title": "Secret follow-up",
|
||||
}]}
|
||||
|
||||
async def send(_subscription, payload):
|
||||
sent.append(json.loads(payload))
|
||||
|
||||
config = PushConfiguration("public", "private", "mailto:ops@example.com")
|
||||
assert await dispatch_unread_updates(store, config, unread, send) == 1
|
||||
assert await dispatch_unread_updates(store, config, unread, send) == 0
|
||||
revision = "2026-08-11T12:05:00Z"
|
||||
assert await dispatch_unread_updates(store, config, unread, send) == 1
|
||||
assert [payload["notification_id"] for payload in sent] == [42, 42]
|
||||
assert "updated_at" not in json.dumps(sent)
|
||||
assert "Secret follow-up" not in json.dumps(sent)
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_update_burst_sends_bounded_individual_pushes_and_one_private_digest(tmp_path):
|
||||
store = PushSubscriptionStore(tmp_path / "push.sqlite3")
|
||||
|
||||
Reference in new issue
Block a user