# tests/integration/api/v1/admin/test_messages.py """ Integration tests for admin messaging endpoints. Tests the /api/v1/admin/messages/* endpoints. """ import pytest from models.database.message import ConversationType, ParticipantType @pytest.mark.integration @pytest.mark.api @pytest.mark.admin class TestAdminMessagesListAPI: """Tests for admin message list endpoints.""" def test_list_conversations_empty(self, client, admin_headers): """Test listing conversations when none exist.""" response = client.get("/api/v1/admin/messages", headers=admin_headers) assert response.status_code == 200 data = response.json() assert "conversations" in data assert "total" in data assert "total_unread" in data assert data["conversations"] == [] assert data["total"] == 0 def test_list_conversations_requires_auth(self, client): """Test that listing requires authentication.""" response = client.get("/api/v1/admin/messages") assert response.status_code == 401 def test_list_conversations_requires_admin(self, client, auth_headers): """Test that listing requires admin role.""" response = client.get("/api/v1/admin/messages", headers=auth_headers) assert response.status_code == 403 def test_list_conversations_with_data( self, client, admin_headers, test_conversation_admin_vendor ): """Test listing conversations with existing data.""" response = client.get("/api/v1/admin/messages", headers=admin_headers) assert response.status_code == 200 data = response.json() assert data["total"] >= 1 assert len(data["conversations"]) >= 1 # Check conversation structure conv = data["conversations"][0] assert "id" in conv assert "conversation_type" in conv assert "subject" in conv assert "is_closed" in conv def test_list_conversations_filter_by_type( self, client, admin_headers, test_conversation_admin_vendor ): """Test filtering conversations by type.""" response = client.get( "/api/v1/admin/messages", params={"conversation_type": "admin_vendor"}, headers=admin_headers, ) assert response.status_code == 200 data = response.json() for conv in data["conversations"]: assert conv["conversation_type"] == "admin_vendor" def test_list_conversations_filter_closed( self, client, admin_headers, closed_conversation ): """Test filtering closed conversations.""" response = client.get( "/api/v1/admin/messages", params={"is_closed": True}, headers=admin_headers, ) assert response.status_code == 200 data = response.json() for conv in data["conversations"]: assert conv["is_closed"] is True @pytest.mark.integration @pytest.mark.api @pytest.mark.admin class TestAdminMessagesUnreadCountAPI: """Tests for unread count endpoint.""" def test_get_unread_count(self, client, admin_headers): """Test getting unread count.""" response = client.get("/api/v1/admin/messages/unread-count", headers=admin_headers) assert response.status_code == 200 data = response.json() assert "total_unread" in data assert isinstance(data["total_unread"], int) def test_get_unread_count_with_unread( self, client, admin_headers, test_message ): """Test unread count with unread messages.""" response = client.get("/api/v1/admin/messages/unread-count", headers=admin_headers) assert response.status_code == 200 data = response.json() # The test_message is sent by admin, so no unread count for admin assert data["total_unread"] >= 0 @pytest.mark.integration @pytest.mark.api @pytest.mark.admin class TestAdminMessagesRecipientsAPI: """Tests for recipients endpoint.""" def test_get_vendor_recipients( self, client, admin_headers, test_vendor_with_vendor_user ): """Test getting vendor recipients.""" response = client.get( "/api/v1/admin/messages/recipients", params={"recipient_type": "vendor"}, headers=admin_headers, ) assert response.status_code == 200 data = response.json() assert "recipients" in data assert "total" in data def test_get_customer_recipients(self, client, admin_headers, test_customer): """Test getting customer recipients.""" response = client.get( "/api/v1/admin/messages/recipients", params={"recipient_type": "customer"}, headers=admin_headers, ) assert response.status_code == 200 data = response.json() assert "recipients" in data assert "total" in data def test_get_recipients_requires_type(self, client, admin_headers): """Test that recipient_type is required.""" response = client.get("/api/v1/admin/messages/recipients", headers=admin_headers) assert response.status_code == 422 # Validation error @pytest.mark.integration @pytest.mark.api @pytest.mark.admin class TestAdminMessagesCreateAPI: """Tests for conversation creation.""" def test_create_conversation_admin_vendor( self, client, admin_headers, test_vendor_user, test_vendor_with_vendor_user ): """Test creating admin-vendor conversation.""" response = client.post( "/api/v1/admin/messages", json={ "conversation_type": "admin_vendor", "subject": "Test Conversation", "recipient_type": "vendor", "recipient_id": test_vendor_user.id, "vendor_id": test_vendor_with_vendor_user.id, "initial_message": "Hello vendor!", }, headers=admin_headers, ) assert response.status_code == 200 data = response.json() assert data["subject"] == "Test Conversation" assert data["conversation_type"] == "admin_vendor" assert len(data["messages"]) == 1 assert data["messages"][0]["content"] == "Hello vendor!" def test_create_conversation_admin_customer( self, client, admin_headers, test_customer, test_vendor ): """Test creating admin-customer conversation.""" response = client.post( "/api/v1/admin/messages", json={ "conversation_type": "admin_customer", "subject": "Customer Support", "recipient_type": "customer", "recipient_id": test_customer.id, "vendor_id": test_vendor.id, "initial_message": "How can I help you?", }, headers=admin_headers, ) assert response.status_code == 200 data = response.json() assert data["conversation_type"] == "admin_customer" def test_create_conversation_wrong_recipient_type( self, client, admin_headers, test_vendor_user, test_vendor ): """Test error when recipient type doesn't match conversation type.""" response = client.post( "/api/v1/admin/messages", json={ "conversation_type": "admin_vendor", "subject": "Test", "recipient_type": "customer", # Wrong type "recipient_id": 1, "vendor_id": test_vendor.id, }, headers=admin_headers, ) assert response.status_code == 400 def test_create_conversation_invalid_type( self, client, admin_headers, test_vendor_user, test_vendor ): """Test error when admin tries to create vendor_customer conversation.""" response = client.post( "/api/v1/admin/messages", json={ "conversation_type": "vendor_customer", "subject": "Test", "recipient_type": "customer", "recipient_id": 1, "vendor_id": test_vendor.id, }, headers=admin_headers, ) assert response.status_code == 400 @pytest.mark.integration @pytest.mark.api @pytest.mark.admin class TestAdminMessagesDetailAPI: """Tests for conversation detail.""" def test_get_conversation_detail( self, client, admin_headers, test_conversation_admin_vendor ): """Test getting conversation detail.""" response = client.get( f"/api/v1/admin/messages/{test_conversation_admin_vendor.id}", headers=admin_headers, ) assert response.status_code == 200 data = response.json() assert data["id"] == test_conversation_admin_vendor.id assert "participants" in data assert "messages" in data def test_get_conversation_not_found(self, client, admin_headers): """Test getting nonexistent conversation.""" response = client.get("/api/v1/admin/messages/99999", headers=admin_headers) assert response.status_code == 404 def test_get_conversation_marks_read( self, client, admin_headers, test_conversation_admin_vendor ): """Test that getting detail marks as read by default.""" response = client.get( f"/api/v1/admin/messages/{test_conversation_admin_vendor.id}", headers=admin_headers, ) assert response.status_code == 200 data = response.json() assert data["unread_count"] == 0 @pytest.mark.integration @pytest.mark.api @pytest.mark.admin class TestAdminMessagesSendAPI: """Tests for sending messages.""" def test_send_message(self, client, admin_headers, test_conversation_admin_vendor): """Test sending a message.""" response = client.post( f"/api/v1/admin/messages/{test_conversation_admin_vendor.id}/messages", data={"content": "Test message content"}, headers=admin_headers, ) assert response.status_code == 200 data = response.json() assert data["content"] == "Test message content" assert data["sender_type"] == "admin" def test_send_message_to_closed(self, client, admin_headers, closed_conversation): """Test cannot send to closed conversation.""" response = client.post( f"/api/v1/admin/messages/{closed_conversation.id}/messages", data={"content": "Test message"}, headers=admin_headers, ) assert response.status_code == 400 def test_send_message_not_found(self, client, admin_headers): """Test sending to nonexistent conversation.""" response = client.post( "/api/v1/admin/messages/99999/messages", data={"content": "Test message"}, headers=admin_headers, ) assert response.status_code == 404 @pytest.mark.integration @pytest.mark.api @pytest.mark.admin class TestAdminMessagesActionsAPI: """Tests for conversation actions.""" def test_close_conversation( self, client, admin_headers, test_conversation_admin_vendor ): """Test closing a conversation.""" response = client.post( f"/api/v1/admin/messages/{test_conversation_admin_vendor.id}/close", headers=admin_headers, ) assert response.status_code == 200 data = response.json() assert data["success"] is True assert "closed" in data["message"].lower() def test_close_conversation_not_found(self, client, admin_headers): """Test closing nonexistent conversation.""" response = client.post( "/api/v1/admin/messages/99999/close", headers=admin_headers, ) assert response.status_code == 404 def test_reopen_conversation(self, client, admin_headers, closed_conversation): """Test reopening a closed conversation.""" response = client.post( f"/api/v1/admin/messages/{closed_conversation.id}/reopen", headers=admin_headers, ) assert response.status_code == 200 data = response.json() assert data["success"] is True assert "reopen" in data["message"].lower() def test_mark_read(self, client, admin_headers, test_conversation_admin_vendor): """Test marking conversation as read.""" response = client.put( f"/api/v1/admin/messages/{test_conversation_admin_vendor.id}/read", headers=admin_headers, ) assert response.status_code == 200 data = response.json() assert data["success"] is True assert data["unread_count"] == 0 def test_update_preferences( self, client, admin_headers, test_conversation_admin_vendor ): """Test updating notification preferences.""" response = client.put( f"/api/v1/admin/messages/{test_conversation_admin_vendor.id}/preferences", json={"email_notifications": False, "muted": True}, headers=admin_headers, ) assert response.status_code == 200 data = response.json() assert data["success"] is True