aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests/test_notification_dismissal.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-hub/tests/test_notification_dismissal.py')
-rw-r--r--packages/meshbay-hub/tests/test_notification_dismissal.py48
1 files changed, 39 insertions, 9 deletions
diff --git a/packages/meshbay-hub/tests/test_notification_dismissal.py b/packages/meshbay-hub/tests/test_notification_dismissal.py
index 7324656..07cf715 100644
--- a/packages/meshbay-hub/tests/test_notification_dismissal.py
+++ b/packages/meshbay-hub/tests/test_notification_dismissal.py
@@ -15,8 +15,15 @@ notification came straight back. The feed still carried the fossil of the
older intent — `class="notif-item ${n.read ? '' : 'notif-unread'}"`, styling
for a read entry rendered greyed out, from before clicking meant dismissing.
-So the contract is between the two halves, and that is what this pins: the
-hub's filter behaves, and the SPA asks for it.
+Filtering the list to unread was the first fix. It corrected what the user saw
+and left the rows accumulating for nothing, invisible for ever, which is not a
+resolution so much as a place to hide the disagreement. **Dismissing one now
+deletes it** — the reasoning `purge_notifications` always carried, applied one
+at a time: these are signals, not a record.
+
+So what this pins is the contract between the two halves: the hub drops the
+row by either path, old client or new, and the SPA still sends the filter for
+the case where it is newer than the hub it is talking to.
"""
import re
@@ -91,19 +98,42 @@ async def test_a_read_notification_is_gone_from_what_the_spa_asks_for(client):
@pytest.mark.asyncio
-async def test_without_the_filter_it_does_come_back(client):
- """The other half of the same fact, so the fix cannot be read as a
- coincidence: unfiltered, the endpoint still returns it, and that is the
- request the SPA used to make."""
+async def test_it_is_gone_from_the_unfiltered_list_too(client):
+ """The filter is no longer what makes it disappear — the row is.
+
+ This assertion is the inverse of what it was. Filtering to unread fixed
+ what the user saw and left the rows behind, invisible for ever; dismissing
+ now deletes, so even a client that asks for everything sees nothing. The
+ filter stays because an interface older than the hub still sends it.
+ """
token = await _login_and_notify(client)
auth = {"Authorization": f"Bearer {token}"}
nid = (await client.get("/v1/notifications", headers=auth)).json()["notifications"][0]["id"]
- await client.post(f"/v1/notifications/{nid}/read", headers=auth)
+ await client.delete(f"/v1/notifications/{nid}", headers=auth)
unfiltered = (await client.get("/v1/notifications?limit=20", headers=auth)).json()
- assert len(unfiltered["notifications"]) == 1
- assert unfiltered["notifications"][0]["read"] is True
+ assert unfiltered["notifications"] == []
+
+
+@pytest.mark.asyncio
+async def test_the_old_read_path_dismisses_too(client):
+ """Version skew is the normal case here, not the exception.
+
+ The SPA ships inside the desktop package, so a hub is always talking to
+ some interface older than itself — CLAUDE.md records that as a standing
+ consequence of shipping the UI in a package. An old client calling
+ `/read` must stop accumulating rows as well, or the fix only reaches
+ whoever updated.
+ """
+ token = await _login_and_notify(client)
+ auth = {"Authorization": f"Bearer {token}"}
+
+ nid = (await client.get("/v1/notifications", headers=auth)).json()["notifications"][0]["id"]
+ r = await client.post(f"/v1/notifications/{nid}/read", headers=auth)
+ assert r.status_code == 200
+
+ assert (await client.get("/v1/notifications", headers=auth)).json()["notifications"] == []
# `_one_notification_for_alice` is the setup; this alias keeps the tests reading