fix(chats): prevent duplicate saved messages entries in chat list
Some checks failed
CI / test (push) Failing after 1m57s

This commit is contained in:
2026-03-08 21:13:40 +03:00
parent af3c5bd79e
commit 926413534b
3 changed files with 116 additions and 3 deletions

View File

@@ -3,7 +3,7 @@ from datetime import datetime, timedelta, timezone
from sqlalchemy import select
from app.auth.models import EmailVerificationToken
from app.chats.models import ChatType
from app.chats.models import Chat, ChatMember, ChatMemberRole, ChatType
from app.messages.models import Message
@@ -135,6 +135,40 @@ async def test_delete_saved_messages_chat_clears_messages_but_keeps_chat(client,
assert messages_after_delete.json() == []
async def test_chat_list_hides_duplicate_saved_chats_and_returns_single_saved_entry(client, db_session):
user = await _create_verified_user(
client,
db_session,
"saved_duplicate_user@example.com",
"saved_duplicate_user",
"strongpass123",
)
auth = {"Authorization": f"Bearer {user['access_token']}"}
me_response = await client.get("/api/v1/auth/me", headers=auth)
user_id = me_response.json()["id"]
# Force-create an extra saved chat row to emulate historical duplicate data.
duplicate_saved_chat = Chat(
type=ChatType.PRIVATE,
title="Saved Messages",
description="Personal cloud chat",
is_public=False,
is_saved=True,
)
db_session.add(duplicate_saved_chat)
await db_session.flush()
db_session.add(
ChatMember(chat_id=duplicate_saved_chat.id, user_id=user_id, role=ChatMemberRole.OWNER)
)
await db_session.commit()
chats_response = await client.get("/api/v1/chats", headers=auth)
assert chats_response.status_code == 200
rows = chats_response.json()
saved_rows = [row for row in rows if row.get("is_saved") is True]
assert len(saved_rows) == 1
async def test_chat_list_includes_notification_muted_flag(client, db_session):
u1 = await _create_verified_user(client, db_session, "muted_flag_u1@example.com", "muted_flag_u1", "strongpass123")
u2 = await _create_verified_user(client, db_session, "muted_flag_u2@example.com", "muted_flag_u2", "strongpass123")
@@ -193,6 +227,80 @@ async def test_media_upload_url_accepts_mp4_voice_mime_types(client, db_session)
assert "upload_url" in m4a_response.json()
async def test_archive_and_pin_chat_are_user_scoped(client, db_session):
u1 = await _create_verified_user(client, db_session, "scope_u1@example.com", "scope_u1", "strongpass123")
u2 = await _create_verified_user(client, db_session, "scope_u2@example.com", "scope_u2", "strongpass123")
me_u2 = await client.get("/api/v1/auth/me", headers={"Authorization": f"Bearer {u2['access_token']}"})
u2_id = me_u2.json()["id"]
create_chat_response = await client.post(
"/api/v1/chats",
headers={"Authorization": f"Bearer {u1['access_token']}"},
json={"type": ChatType.PRIVATE.value, "title": None, "member_ids": [u2_id]},
)
assert create_chat_response.status_code == 200
chat_id = create_chat_response.json()["id"]
pin_response = await client.post(
f"/api/v1/chats/{chat_id}/pin-chat",
headers={"Authorization": f"Bearer {u1['access_token']}"},
)
assert pin_response.status_code == 200
assert pin_response.json()["pinned"] is True
u1_chats_after_pin = await client.get(
"/api/v1/chats",
headers={"Authorization": f"Bearer {u1['access_token']}"},
)
assert u1_chats_after_pin.status_code == 200
u1_row_after_pin = next((item for item in u1_chats_after_pin.json() if item["id"] == chat_id), None)
assert u1_row_after_pin is not None
assert u1_row_after_pin["pinned"] is True
u2_chats_after_u1_pin = await client.get(
"/api/v1/chats",
headers={"Authorization": f"Bearer {u2['access_token']}"},
)
assert u2_chats_after_u1_pin.status_code == 200
u2_row_after_u1_pin = next((item for item in u2_chats_after_u1_pin.json() if item["id"] == chat_id), None)
assert u2_row_after_u1_pin is not None
assert u2_row_after_u1_pin["pinned"] is False
archive_response = await client.post(
f"/api/v1/chats/{chat_id}/archive",
headers={"Authorization": f"Bearer {u1['access_token']}"},
)
assert archive_response.status_code == 200
assert archive_response.json()["archived"] is True
u1_active_chats = await client.get(
"/api/v1/chats",
headers={"Authorization": f"Bearer {u1['access_token']}"},
)
assert u1_active_chats.status_code == 200
assert all(item["id"] != chat_id for item in u1_active_chats.json())
u1_archived_chats = await client.get(
"/api/v1/chats",
params={"archived": True},
headers={"Authorization": f"Bearer {u1['access_token']}"},
)
assert u1_archived_chats.status_code == 200
u1_archived_row = next((item for item in u1_archived_chats.json() if item["id"] == chat_id), None)
assert u1_archived_row is not None
assert u1_archived_row["archived"] is True
u2_active_chats = await client.get(
"/api/v1/chats",
headers={"Authorization": f"Bearer {u2['access_token']}"},
)
assert u2_active_chats.status_code == 200
u2_row_after_u1_archive = next((item for item in u2_active_chats.json() if item["id"] == chat_id), None)
assert u2_row_after_u1_archive is not None
assert u2_row_after_u1_archive["archived"] is False
async def test_private_chat_respects_contacts_only_policy(client, db_session):
u1 = await _create_verified_user(client, db_session, "pm_u1@example.com", "pm_user_one", "strongpass123")
u2 = await _create_verified_user(client, db_session, "pm_u2@example.com", "pm_user_two", "strongpass123")