diff --git a/docs/api-reference.md b/docs/api-reference.md index b5b36fa..f5bb297 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -666,6 +666,11 @@ Body: Response: `204` +Errors: + +- `404` - user with this email was not found. +- `409` - contact cannot be added because a block relation exists. + ### DELETE `/api/v1/users/{user_id}/contacts` Auth required. diff --git a/docs/core-checklist-status.md b/docs/core-checklist-status.md index ba6d603..6a14cc0 100644 --- a/docs/core-checklist-status.md +++ b/docs/core-checklist-status.md @@ -10,7 +10,7 @@ Legend: 1. Account - `PARTIAL` (email auth, JWT, refresh, logout, reset; sessions exist, full UX still improving) 2. User Profile - `DONE` (username, name, avatar, bio, update) 3. User Status - `PARTIAL` (online/last seen/offline; "recently" heuristic limited) -4. Contacts - `PARTIAL` (list/search/add/remove/block/unblock; `add by email` flow covered by integration tests; UX moved to menu) +4. Contacts - `PARTIAL` (list/search/add/remove/block/unblock; `add by email` flow covered by integration tests including `success/not found/blocked conflict`; UX moved to menu) 5. Chat List - `DONE` (all/pinned/archive/sort/unread) 6. Chat Types - `DONE` (private/group/channel) 7. Chat Creation - `DONE` (private/group/channel) diff --git a/tests/test_chat_message_flow.py b/tests/test_chat_message_flow.py index 8ce0451..b860845 100644 --- a/tests/test_chat_message_flow.py +++ b/tests/test_chat_message_flow.py @@ -659,6 +659,27 @@ async def test_add_contact_by_email_success_and_not_found(client, db_session): assert add_missing_contact.status_code == 404 +async def test_add_contact_by_email_blocked_conflict(client, db_session): + owner = await _create_verified_user(client, db_session, "contact_email_block_owner@example.com", "contact_email_block_owner", "strongpass123") + target = await _create_verified_user(client, db_session, "contact_email_block_target@example.com", "contact_email_block_target", "strongpass123") + + me_owner = await client.get("/api/v1/auth/me", headers={"Authorization": f"Bearer {owner['access_token']}"}) + owner_id = me_owner.json()["id"] + + block_owner = await client.post( + f"/api/v1/users/{owner_id}/block", + headers={"Authorization": f"Bearer {target['access_token']}"}, + ) + assert block_owner.status_code == 204 + + add_contact_blocked = await client.post( + "/api/v1/users/contacts/by-email", + headers={"Authorization": f"Bearer {owner['access_token']}"}, + json={"email": "contact_email_block_target@example.com"}, + ) + assert add_contact_blocked.status_code == 409 + + async def test_avatar_privacy_hidden_from_other_users_search(client, db_session): owner = await _create_verified_user(client, db_session, "avatar_owner@example.com", "avatar_owner", "strongpass123") viewer = await _create_verified_user(client, db_session, "avatar_viewer@example.com", "avatar_viewer", "strongpass123")