diff --git a/devplacepy/routers/messages.py b/devplacepy/routers/messages.py index 754ce4fa..228ca607 100644 --- a/devplacepy/routers/messages.py +++ b/devplacepy/routers/messages.py @@ -19,7 +19,6 @@ from devplacepy.templating import clear_messages_cache from devplacepy.utils import ( require_user, time_ago, - is_admin, _user_from_session, _user_from_api_key, ) @@ -153,7 +152,6 @@ async def messages_page(request: Request, with_uid: str = None, search: str = "" other_last_seen = None if with_uid: messages, other_user = get_conversation_messages(user["uid"], with_uid) - mark_conversation_read(user["uid"], with_uid) mark_notifications_read_by_target( user["uid"], f"/messages?with_uid={with_uid}" ) diff --git a/tests/api/messages/index.py b/tests/api/messages/index.py index 20d83130..d832dac0 100644 --- a/tests/api/messages/index.py +++ b/tests/api/messages/index.py @@ -115,7 +115,6 @@ def _seed_news_audit_log(): ) refresh_snapshot() return uid -from devplacepy.database import get_table JSON_content_negotiation = {"Accept": "application/json"} _counter_content_negotiation = [0] def _session_content_negotiation(password="secret123"): @@ -180,3 +179,56 @@ def test_messages_conversation_list_avatar_has_presence_dot(app_server): assert "conversation-item" in html assert "presence-dot" in html assert f'data-presence-uid="{other_uid}"' in html + + +def test_notification_open_does_not_mark_messages_read(app_server, seeded_db): + """Regression: opening a DM notification must NOT mark the conversation read. + The GET /messages?with_uid=... handler should NOT call mark_conversation_read; + only the WebSocket markRead mechanism should do that. + """ + sender, sender_name = _member() + receiver, receiver_name = _member() + + refresh_snapshot() + sender_user = _db_user(sender_name) + receiver_user = _db_user(receiver_name) + + send_resp = sender.post( + f"{BASE_URL}/messages/send", + data={"receiver_uid": receiver_user["uid"], "content": "DM that must stay unread"}, + allow_redirects=True, + ) + assert send_resp.status_code == 200, send_resp.text[:300] + + refresh_snapshot() + msg = get_table("messages").find_one( + sender_uid=sender_user["uid"], receiver_uid=receiver_user["uid"] + ) + assert msg is not None, "message should exist" + assert msg["read"] == 0, f"expected read=0 after send, got {msg['read']}" + + n_uid = generate_uid() + get_table("notifications").insert({ + "uid": n_uid, + "user_uid": receiver_user["uid"], + "type": "message", + "message": "sent you a message", + "related_uid": sender_user["uid"], + "target_url": f"/messages?with_uid={sender_user['uid']}", + "read": False, + "created_at": datetime.now(timezone.utc).isoformat(), + }) + + receiver.get(f"{BASE_URL}/notifications/open/{n_uid}", allow_redirects=True) + + refresh_snapshot() + n = get_table("notifications").find_one(uid=n_uid) + assert n is not None, "notification should exist" + assert n["read"] in (1, True, "1"), "notification should be marked read" + + refresh_snapshot() + msg_after = get_table("messages").find_one(uid=msg["uid"]) + assert msg_after is not None, "message should still exist" + assert msg_after["read"] == 0, ( + f"expected read=0 after notification open, got {msg_after['read']}" + )