diff options
Diffstat (limited to 'packages/meshbay-hub/tests/test_notification_dismissal.py')
| -rw-r--r-- | packages/meshbay-hub/tests/test_notification_dismissal.py | 48 |
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 |