From 18c6d2b0dd77b38f487747aad1fcd1218aa8c356 Mon Sep 17 00:00:00 2001 From: yj <2077506045@qq.com> Date: 星期三, 27 八月 2025 09:19:57 +0800 Subject: [PATCH] 1. 新增测试群组关键字,测试群组名称包含关键字,在其中发言一律不触发静默。 --- app/services/friend_ignore_service.py | 16 ++ /dev/null | 177 ----------------------------------- app/services/silence_service.py | 41 ++++++++ config.py | 3 app/services/message_processor.py | 4 logs/app.log | 5 6 files changed, 61 insertions(+), 185 deletions(-) diff --git a/app/services/friend_ignore_service.py b/app/services/friend_ignore_service.py index 061c771..2b0123f 100644 --- a/app/services/friend_ignore_service.py +++ b/app/services/friend_ignore_service.py @@ -8,6 +8,7 @@ from app.services.redis_queue import redis_queue from app.models.database import get_db from app.models.contact import Contact +from app.services.silence_service import silence_service from config import settings @@ -84,18 +85,20 @@ logger.error(f"娣诲姞濂藉弸鍒板拷鐣ュ垪琛ㄥ紓甯�: error={str(e)}") return False - def is_friend_ignored(self, w_id: str) -> bool: + def is_friend_ignored(self, w_id: str, group_id: Optional[str] = None) -> bool: """ 妫�鏌ユ寚瀹歸_id鏄惁搴旇琚拷鐣� 閫昏緫锛� 1. 濡傛灉濂藉弸蹇界暐鍔熻兘鏈惎鐢紝杩斿洖False锛堜笉蹇界暐锛� 2. 濡傛灉w_id鍦ㄧ櫧鍚嶅崟涓紝杩斿洖False锛堜笉蹇界暐锛� - 3. 濡傛灉w_id鍦ㄥ拷鐣ュ垪琛ㄤ腑锛岃繑鍥濼rue锛堝拷鐣ワ級 - 4. 濡傛灉w_id涓嶅湪蹇界暐鍒楄〃涓紝杩斿洖False锛堜笉蹇界暐锛� + 3. 濡傛灉w_id鍦ㄥ拷鐣ュ垪琛ㄤ腑锛屼絾鎵�鍦ㄧ兢缁勪负娴嬭瘯缇ょ粍锛岃繑鍥濬alse锛堜笉蹇界暐锛� + 4. 濡傛灉w_id鍦ㄥ拷鐣ュ垪琛ㄤ腑锛岃繑鍥濼rue锛堝拷鐣ワ級 + 5. 濡傛灉w_id涓嶅湪蹇界暐鍒楄〃涓紝杩斿洖False锛堜笉蹇界暐锛� Args: w_id: 鐢ㄦ埛w_id + group_id: 缇ょ粍ID锛堝彲閫夛級锛岀敤浜庢鏌ユ槸鍚︿负娴嬭瘯缇ょ粍 Returns: 濡傛灉搴旇琚拷鐣ヨ繑鍥濼rue锛屽惁鍒欒繑鍥濬alse @@ -116,8 +119,13 @@ is_in_ignore_list = redis_queue.redis_client.sismember(self.ignore_list_key, w_id) if is_in_ignore_list: + # 濡傛灉鍦ㄥ拷鐣ュ垪琛ㄤ腑锛屾鏌ユ槸鍚﹀湪娴嬭瘯缇ょ粍涓� + if group_id and silence_service.is_test_group(group_id): + logger.info(f"娴嬭瘯缇ょ粍涓殑濂藉弸娑堟伅涓嶈蹇界暐: w_id={w_id}, group_id={group_id}") + return False + logger.info(f"w_id鍦ㄥ拷鐣ュ垪琛ㄤ腑锛屽拷鐣ユ秷鎭�: w_id={w_id}") - + return is_in_ignore_list except Exception as e: diff --git a/app/services/message_processor.py b/app/services/message_processor.py index 0b26bff..fecc840 100644 --- a/app/services/message_processor.py +++ b/app/services/message_processor.py @@ -201,8 +201,8 @@ from_user = data.get("fromUser") from_group = data.get("fromGroup") - # 妫�鏌ュ彂閫佽�呮槸鍚﹀湪濂藉弸蹇界暐鍒楄〃涓� - is_friend_ignored = friend_ignore_service.is_friend_ignored(from_user) + # 妫�鏌ュ彂閫佽�呮槸鍚﹀湪濂藉弸蹇界暐鍒楄〃涓紙浼犲叆缇ょ粍ID鐢ㄤ簬娴嬭瘯缇ょ粍妫�鏌ワ級 + is_friend_ignored = friend_ignore_service.is_friend_ignored(from_user, from_group) if is_friend_ignored: logger.info( diff --git a/app/services/silence_service.py b/app/services/silence_service.py index 0161198..10727ae 100644 --- a/app/services/silence_service.py +++ b/app/services/silence_service.py @@ -5,8 +5,11 @@ import time from typing import Optional from loguru import logger +from sqlalchemy.orm import Session from app.services.redis_queue import redis_queue +from app.models.database import get_db +from app.models.contact import Contact from config import settings @@ -25,6 +28,39 @@ """鑾峰彇缇ょ粍闈欓粯缁撴潫鏃堕棿閿�""" return f"{self.silence_end_time_key_prefix}{group_id}" + def is_test_group(self, group_id: str) -> bool: + """ + 妫�鏌ユ寚瀹氱兢缁勬槸鍚︿负娴嬭瘯缇ょ粍 + + Args: + group_id: 缇ょ粍ID + + Returns: + 濡傛灉鏄祴璇曠兢缁勮繑鍥濼rue锛屽惁鍒欒繑鍥濬alse + """ + try: + # 妫�鏌ユ槸鍚﹂厤缃簡娴嬭瘯缇ょ粍鍏抽敭瀛� + if not settings.test_group_keywords: + return False + + # 浠庢暟鎹簱鏌ヨ缇ょ粍淇℃伅 + with next(get_db()) as db: + contact = db.query(Contact).filter(Contact.wc_id == group_id).first() + if not contact or not contact.nick_name: + return False + + # 妫�鏌ョ兢缁勬樀绉版槸鍚﹀寘鍚换浣曟祴璇曞叧閿瓧 + nick_name = contact.nick_name + for keyword in settings.test_group_keywords: + if keyword in nick_name: + logger.info(f"璇嗗埆鍒版祴璇曠兢缁�: group_id={group_id}, nick_name={nick_name}, keyword={keyword}") + return True + + return False + except Exception as e: + logger.error(f"妫�鏌ユ祴璇曠兢缁勫紓甯�: group_id={group_id}, error={str(e)}") + return False + def activate_silence_mode(self, group_id: str) -> bool: """ 婵�娲绘寚瀹氱兢缁勭殑闈欓粯妯″紡 @@ -36,6 +72,11 @@ 婵�娲绘垚鍔熻繑鍥濼rue锛屽け璐ヨ繑鍥濬alse """ try: + # 妫�鏌ユ槸鍚︿负娴嬭瘯缇ょ粍锛屽鏋滄槸鍒欎笉婵�娲婚潤榛樻ā寮� + if self.is_test_group(group_id): + logger.info(f"娴嬭瘯缇ょ粍涓嶆縺娲婚潤榛樻ā寮�: group_id={group_id}") + return True + if not settings.silence_mode_enabled: logger.debug("闈欓粯妯″紡鍔熻兘宸茬鐢�") return False diff --git a/config.py b/config.py index 7942292..79c1456 100644 --- a/config.py +++ b/config.py @@ -118,6 +118,9 @@ # 缁撴潫瀛楃涓查厤缃� self.end_str_list = config_data.get("end_str_list", []) + + # 娴嬭瘯缇ょ粍鍏抽敭瀛楅厤缃� + self.test_group_keywords = config_data.get("test_group_keywords", []) def update_ecloud_w_id(self, new_w_id: str) -> bool: """ diff --git a/logs/app.log b/logs/app.log index 22941d3..dc08815 100644 --- a/logs/app.log +++ b/logs/app.log @@ -1,2 +1,3 @@ -2025-08-22 16:15:05 | INFO | __main__:<module>:132 - 鍚姩E浜戠瀹�-DifyAI瀵规帴鏈嶅姟 -2025-08-22 16:34:15 | INFO | __main__:<module>:132 - 鍚姩E浜戠瀹�-DifyAI瀵规帴鏈嶅姟 +2025-08-26 15:35:24 | INFO | __main__:<module>:132 - 鍚姩E浜戠瀹�-DifyAI瀵规帴鏈嶅姟 +2025-08-26 15:55:13 | INFO | __main__:<module>:132 - 鍚姩E浜戠瀹�-DifyAI瀵规帴鏈嶅姟 +2025-08-26 16:08:41 | INFO | __main__:<module>:132 - 鍚姩E浜戠瀹�-DifyAI瀵规帴鏈嶅姟 diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index 7188324..0000000 --- a/tests/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# 娴嬭瘯鍖呭垵濮嬪寲鏂囦欢 diff --git a/tests/test_api.py b/tests/test_api.py deleted file mode 100644 index 7791983..0000000 --- a/tests/test_api.py +++ /dev/null @@ -1,102 +0,0 @@ -""" -API鎺ュ彛娴嬭瘯 -""" -import pytest -from fastapi.testclient import TestClient -from unittest.mock import patch -from main import app - - -client = TestClient(app) - - -class TestCallbackAPI: - """鍥炶皟API娴嬭瘯绫�""" - - def test_health_check(self): - """娴嬭瘯鍋ュ悍妫�鏌ユ帴鍙�""" - response = client.get("/api/v1/health") - assert response.status_code == 200 - data = response.json() - assert data["status"] == "healthy" - - @patch('app.services.message_processor.message_processor') - @patch('app.workers.message_worker.message_worker') - def test_callback_success(self, mock_worker, mock_processor): - """娴嬭瘯鍥炶皟鎺ュ彛鎴愬姛""" - mock_processor.enqueue_callback_message.return_value = True - - callback_data = { - "account": "17200000000", - "messageType": "80001", - "wcId": "wxid_phyyedw9xap22", - "data": { - "content": "娴嬭瘯娑堟伅", - "fromGroup": "25411981800@chatroom", - "fromUser": "wxid_ynu1tgjz156j22", - "memberCount": 8, - "msgId": 1685074592, - "newMsgId": 1497474118261484795, - "self": False, - "timestamp": 1640772092, - "toUser": "wxid_phyyedw9xap22", - "wId": "12491ae9-62aa-4f7a-83e6-9db4e9f28e3c" - } - } - - response = client.post("/api/v1/callback", json=callback_data) - - assert response.status_code == 200 - data = response.json() - assert data["success"] is True - assert data["message"] == "娑堟伅宸叉垚鍔熷姞鍏ュ鐞嗛槦鍒�" - - mock_processor.enqueue_callback_message.assert_called_once() - mock_worker.process_user_queue.assert_called_once_with("wxid_ynu1tgjz156j22") - - @patch('app.services.message_processor.message_processor') - def test_callback_failure(self, mock_processor): - """娴嬭瘯鍥炶皟鎺ュ彛澶辫触""" - mock_processor.enqueue_callback_message.return_value = False - - callback_data = { - "account": "17200000000", - "messageType": "80001", - "wcId": "wxid_phyyedw9xap22", - "data": { - "content": "娴嬭瘯娑堟伅", - "fromGroup": "25411981800@chatroom", - "fromUser": "wxid_ynu1tgjz156j22", - "self": False - } - } - - response = client.post("/api/v1/callback", json=callback_data) - - assert response.status_code == 200 - data = response.json() - assert data["success"] is False - assert data["message"] == "娑堟伅澶勭悊澶辫触" - - def test_callback_invalid_data(self): - """娴嬭瘯鏃犳晥鏁版嵁""" - invalid_data = { - "messageType": "80001" - # 缂哄皯蹇呰瀛楁 - } - - response = client.post("/api/v1/callback", json=invalid_data) - assert response.status_code == 422 # 楠岃瘉閿欒 - - def test_root_endpoint(self): - """娴嬭瘯鏍硅矾寰�""" - response = client.get("/") - assert response.status_code == 200 - data = response.json() - assert data["message"] == "E浜戠瀹�-DifyAI瀵规帴鏈嶅姟" - assert data["version"] == "1.0.0" - assert data["status"] == "running" - - -if __name__ == "__main__": - pytest.main([__file__]) diff --git a/tests/test_dify_streaming.py b/tests/test_dify_streaming.py deleted file mode 100644 index 8fa8630..0000000 --- a/tests/test_dify_streaming.py +++ /dev/null @@ -1,174 +0,0 @@ -""" -娴嬭瘯Dify娴佸紡妯″紡鍔熻兘 -""" - -import pytest -import json -from unittest.mock import Mock, patch, MagicMock -from app.services.dify_client import DifyClient -from config import settings - - -class TestDifyStreaming: - """娴嬭瘯Dify娴佸紡妯″紡""" - - def setup_method(self): - """娴嬭瘯鍓嶈缃�""" - self.client = DifyClient() - - def test_process_stream_response_success(self): - """娴嬭瘯鎴愬姛澶勭悊娴佸紡鍝嶅簲""" - # 妯℃嫙娴佸紡鍝嶅簲鏁版嵁 - stream_data = [ - "data: {\"event\": \"message\", \"task_id\": \"test-task\", \"id\": \"test-msg\", \"conversation_id\": \"test-conv\", \"answer\": \"Hello\", \"created_at\": 1705398420}", - "data: {\"event\": \"message\", \"task_id\": \"test-task\", \"id\": \"test-msg\", \"conversation_id\": \"test-conv\", \"answer\": \" World\", \"created_at\": 1705398420}", - "data: {\"event\": \"message_end\", \"metadata\": {\"usage\": {\"total_tokens\": 10}}, \"usage\": {\"total_tokens\": 10}}", - ] - - # 鍒涘缓妯℃嫙鍝嶅簲瀵硅薄 - mock_response = Mock() - mock_response.iter_lines.return_value = stream_data - - # 娴嬭瘯澶勭悊娴佸紡鍝嶅簲 - result = self.client._process_stream_response(mock_response, "test_user") - - # 楠岃瘉缁撴灉 - assert result is not None - assert result["answer"] == "Hello World" - assert result["conversation_id"] == "test-conv" - assert result["task_id"] == "test-task" - assert result["usage"]["total_tokens"] == 10 - - def test_process_stream_response_error(self): - """娴嬭瘯澶勭悊娴佸紡鍝嶅簲閿欒""" - # 妯℃嫙閿欒鍝嶅簲鏁版嵁 - stream_data = [ - "data: {\"event\": \"error\", \"message\": \"API璋冪敤澶辫触\", \"code\": \"500\"}", - ] - - # 鍒涘缓妯℃嫙鍝嶅簲瀵硅薄 - mock_response = Mock() - mock_response.iter_lines.return_value = stream_data - - # 娴嬭瘯澶勭悊娴佸紡鍝嶅簲 - result = self.client._process_stream_response(mock_response, "test_user") - - # 楠岃瘉缁撴灉 - assert result is None - - def test_process_stream_response_incomplete(self): - """娴嬭瘯澶勭悊涓嶅畬鏁寸殑娴佸紡鍝嶅簲""" - # 妯℃嫙涓嶅畬鏁村搷搴旀暟鎹紙缂哄皯message_end浜嬩欢锛� - stream_data = [ - "data: {\"event\": \"message\", \"task_id\": \"test-task\", \"id\": \"test-msg\", \"conversation_id\": \"test-conv\", \"answer\": \"Hello\", \"created_at\": 1705398420}", - ] - - # 鍒涘缓妯℃嫙鍝嶅簲瀵硅薄 - mock_response = Mock() - mock_response.iter_lines.return_value = stream_data - - # 娴嬭瘯澶勭悊娴佸紡鍝嶅簲 - result = self.client._process_stream_response(mock_response, "test_user") - - # 楠岃瘉缁撴灉 - 鍗充娇娌℃湁message_end浜嬩欢锛屽彧瑕佹湁鍐呭鍜宑onversation_id涔熷簲璇ヨ繑鍥炵粨鏋� - assert result is not None - assert result["answer"] == "Hello" - assert result["conversation_id"] == "test-conv" - - @patch('app.services.dify_client.settings') - def test_send_message_uses_streaming_when_enabled(self, mock_settings): - """娴嬭瘯褰撳惎鐢ㄦ祦寮忔ā寮忔椂浣跨敤娴佸紡鍙戦��""" - # 璁剧疆閰嶇疆涓哄惎鐢ㄦ祦寮忔ā寮� - mock_settings.dify_streaming_enabled = True - - # 妯℃嫙娴佸紡鍙戦�佹柟娉� - with patch.object(self.client, 'send_chat_message_stream') as mock_stream: - mock_stream.return_value = {"answer": "test response", "conversation_id": "test-conv"} - - result = self.client.send_message("test query", "test_user") - - # 楠岃瘉璋冪敤浜嗘祦寮忔柟娉� - mock_stream.assert_called_once_with("test query", "test_user", None, None) - assert result["answer"] == "test response" - - @patch('app.services.dify_client.settings') - def test_send_message_uses_blocking_when_disabled(self, mock_settings): - """娴嬭瘯褰撶鐢ㄦ祦寮忔ā寮忔椂浣跨敤闃诲鍙戦��""" - # 璁剧疆閰嶇疆涓虹鐢ㄦ祦寮忔ā寮� - mock_settings.dify_streaming_enabled = False - - # 妯℃嫙闃诲鍙戦�佹柟娉� - with patch.object(self.client, 'send_chat_message') as mock_blocking: - mock_blocking.return_value = {"answer": "test response", "conversation_id": "test-conv"} - - result = self.client.send_message("test query", "test_user") - - # 楠岃瘉璋冪敤浜嗛樆濉炴柟娉� - mock_blocking.assert_called_once_with("test query", "test_user", None, None) - assert result["answer"] == "test response" - - @patch('app.services.dify_client.settings') - def test_send_message_force_streaming_override(self, mock_settings): - """娴嬭瘯寮哄埗娴佸紡妯″紡瑕嗙洊閰嶇疆""" - # 璁剧疆閰嶇疆涓虹鐢ㄦ祦寮忔ā寮� - mock_settings.dify_streaming_enabled = False - - # 妯℃嫙娴佸紡鍙戦�佹柟娉� - with patch.object(self.client, 'send_chat_message_stream') as mock_stream: - mock_stream.return_value = {"answer": "test response", "conversation_id": "test-conv"} - - # 寮哄埗浣跨敤娴佸紡妯″紡 - result = self.client.send_message("test query", "test_user", force_streaming=True) - - # 楠岃瘉璋冪敤浜嗘祦寮忔柟娉曪紙瑕嗙洊浜嗛厤缃級 - mock_stream.assert_called_once_with("test query", "test_user", None, None) - assert result["answer"] == "test response" - - def test_process_stream_response_with_ping_events(self): - """娴嬭瘯澶勭悊鍖呭惈ping浜嬩欢鐨勬祦寮忓搷搴�""" - # 妯℃嫙鍖呭惈ping浜嬩欢鐨勫搷搴旀暟鎹� - stream_data = [ - "data: {\"event\": \"ping\"}", - "data: {\"event\": \"message\", \"task_id\": \"test-task\", \"id\": \"test-msg\", \"conversation_id\": \"test-conv\", \"answer\": \"Hello\", \"created_at\": 1705398420}", - "data: {\"event\": \"ping\"}", - "data: {\"event\": \"message\", \"task_id\": \"test-task\", \"id\": \"test-msg\", \"conversation_id\": \"test-conv\", \"answer\": \" World\", \"created_at\": 1705398420}", - "data: {\"event\": \"message_end\", \"metadata\": {\"usage\": {\"total_tokens\": 10}}}", - ] - - # 鍒涘缓妯℃嫙鍝嶅簲瀵硅薄 - mock_response = Mock() - mock_response.iter_lines.return_value = stream_data - - # 娴嬭瘯澶勭悊娴佸紡鍝嶅簲 - result = self.client._process_stream_response(mock_response, "test_user") - - # 楠岃瘉缁撴灉锛坧ing浜嬩欢搴旇琚拷鐣ワ級 - assert result is not None - assert result["answer"] == "Hello World" - assert result["conversation_id"] == "test-conv" - - def test_process_stream_response_with_invalid_json(self): - """娴嬭瘯澶勭悊鍖呭惈鏃犳晥JSON鐨勬祦寮忓搷搴�""" - # 妯℃嫙鍖呭惈鏃犳晥JSON鐨勫搷搴旀暟鎹� - stream_data = [ - "data: {\"event\": \"message\", \"task_id\": \"test-task\", \"id\": \"test-msg\", \"conversation_id\": \"test-conv\", \"answer\": \"Hello\", \"created_at\": 1705398420}", - "data: invalid json data", - "data: {\"event\": \"message\", \"task_id\": \"test-task\", \"id\": \"test-msg\", \"conversation_id\": \"test-conv\", \"answer\": \" World\", \"created_at\": 1705398420}", - "data: {\"event\": \"message_end\"}", - ] - - # 鍒涘缓妯℃嫙鍝嶅簲瀵硅薄 - mock_response = Mock() - mock_response.iter_lines.return_value = stream_data - - # 娴嬭瘯澶勭悊娴佸紡鍝嶅簲 - result = self.client._process_stream_response(mock_response, "test_user") - - # 楠岃瘉缁撴灉锛堟棤鏁圝SON搴旇琚烦杩囷級 - assert result is not None - assert result["answer"] == "Hello World" - assert result["conversation_id"] == "test-conv" - - -if __name__ == "__main__": - pytest.main([__file__]) diff --git a/tests/test_friend_ignore_service.py b/tests/test_friend_ignore_service.py deleted file mode 100644 index 6bc92a7..0000000 --- a/tests/test_friend_ignore_service.py +++ /dev/null @@ -1,258 +0,0 @@ -""" -濂藉弸蹇界暐鏈嶅姟娴嬭瘯 -""" - -import pytest -from unittest.mock import Mock, patch -from app.services.friend_ignore_service import FriendIgnoreService - - -class TestFriendIgnoreService: - """濂藉弸蹇界暐鏈嶅姟娴嬭瘯绫�""" - - def setup_method(self): - """娴嬭瘯鍓嶅噯澶�""" - self.service = FriendIgnoreService() - - @patch('app.services.friend_ignore_service.redis_queue') - def test_add_friends_to_ignore_list_success(self, mock_redis_queue): - """娴嬭瘯鎴愬姛娣诲姞濂藉弸鍒板拷鐣ュ垪琛�""" - # 妯℃嫙Redis鎿嶄綔 - mock_redis_client = Mock() - mock_redis_queue.redis_client = mock_redis_client - - friends = ["wxid_test1", "wxid_test2", "wxid_test3"] - - result = self.service.add_friends_to_ignore_list(friends) - - # 楠岃瘉缁撴灉 - assert result is True - - # 楠岃瘉Redis鎿嶄綔琚皟鐢� - mock_redis_client.delete.assert_called_once_with(self.service.ignore_list_key) - mock_redis_client.sadd.assert_called_once_with(self.service.ignore_list_key, *friends) - - @patch('app.services.friend_ignore_service.redis_queue') - def test_add_friends_to_ignore_list_empty(self, mock_redis_queue): - """娴嬭瘯娣诲姞绌哄ソ鍙嬪垪琛�""" - result = self.service.add_friends_to_ignore_list([]) - - # 楠岃瘉缁撴灉 - assert result is True - - # 楠岃瘉Redis鎿嶄綔鏈璋冪敤 - mock_redis_queue.redis_client.delete.assert_not_called() - mock_redis_queue.redis_client.sadd.assert_not_called() - - @patch('app.services.friend_ignore_service.redis_queue') - def test_is_friend_ignored_true(self, mock_redis_queue): - """娴嬭瘯妫�鏌ュソ鍙嬪湪蹇界暐鍒楄〃涓�""" - mock_redis_client = Mock() - mock_redis_queue.redis_client = mock_redis_client - mock_redis_client.sismember.return_value = True - - result = self.service.is_friend_ignored("wxid_test1") - - assert result is True - mock_redis_client.sismember.assert_called_once_with(self.service.ignore_list_key, "wxid_test1") - - @patch('app.services.friend_ignore_service.redis_queue') - def test_is_friend_ignored_false(self, mock_redis_queue): - """娴嬭瘯妫�鏌ュソ鍙嬩笉鍦ㄥ拷鐣ュ垪琛ㄤ腑""" - mock_redis_client = Mock() - mock_redis_queue.redis_client = mock_redis_client - mock_redis_client.sismember.return_value = False - - result = self.service.is_friend_ignored("wxid_test1") - - assert result is False - mock_redis_client.sismember.assert_called_once_with(self.service.ignore_list_key, "wxid_test1") - - @patch('app.services.friend_ignore_service.redis_queue') - def test_get_ignore_list(self, mock_redis_queue): - """娴嬭瘯鑾峰彇蹇界暐鍒楄〃""" - mock_redis_client = Mock() - mock_redis_queue.redis_client = mock_redis_client - expected_set = {"wxid_test1", "wxid_test2"} - mock_redis_client.smembers.return_value = expected_set - - result = self.service.get_ignore_list() - - assert result == expected_set - mock_redis_client.smembers.assert_called_once_with(self.service.ignore_list_key) - - @patch('app.services.friend_ignore_service.redis_queue') - def test_remove_friend_from_ignore_list(self, mock_redis_queue): - """娴嬭瘯浠庡拷鐣ュ垪琛ㄧЩ闄ゅソ鍙�""" - mock_redis_client = Mock() - mock_redis_queue.redis_client = mock_redis_client - mock_redis_client.srem.return_value = 1 # 琛ㄧず鎴愬姛绉婚櫎 - - result = self.service.remove_friend_from_ignore_list("wxid_test1") - - assert result is True - mock_redis_client.srem.assert_called_once_with(self.service.ignore_list_key, "wxid_test1") - - @patch('app.services.friend_ignore_service.redis_queue') - def test_clear_ignore_list(self, mock_redis_queue): - """娴嬭瘯娓呯┖蹇界暐鍒楄〃""" - mock_redis_client = Mock() - mock_redis_queue.redis_client = mock_redis_client - - result = self.service.clear_ignore_list() - - assert result is True - mock_redis_client.delete.assert_called_once_with(self.service.ignore_list_key) - - @patch('app.services.friend_ignore_service.redis_queue') - def test_get_ignore_list_count(self, mock_redis_queue): - """娴嬭瘯鑾峰彇蹇界暐鍒楄〃鏁伴噺""" - mock_redis_client = Mock() - mock_redis_queue.redis_client = mock_redis_client - mock_redis_client.scard.return_value = 5 - - result = self.service.get_ignore_list_count() - - assert result == 5 - mock_redis_client.scard.assert_called_once_with(self.service.ignore_list_key) - - @patch('app.services.friend_ignore_service.redis_queue') - def test_add_friends_exception_handling(self, mock_redis_queue): - """娴嬭瘯娣诲姞濂藉弸鏃剁殑寮傚父澶勭悊""" - mock_redis_client = Mock() - mock_redis_queue.redis_client = mock_redis_client - mock_redis_client.sadd.side_effect = Exception("Redis error") - - result = self.service.add_friends_to_ignore_list(["wxid_test1"]) - - assert result is False - - @patch('app.services.friend_ignore_service.redis_queue') - def test_is_friend_ignored_exception_handling(self, mock_redis_queue): - """娴嬭瘯妫�鏌ュソ鍙嬫椂鐨勫紓甯稿鐞�""" - mock_redis_client = Mock() - mock_redis_queue.redis_client = mock_redis_client - mock_redis_client.sismember.side_effect = Exception("Redis error") - - result = self.service.is_friend_ignored("wxid_test1") - - assert result is False - - @patch('app.services.friend_ignore_service.settings') - @patch('app.services.friend_ignore_service.redis_queue') - @patch('app.services.friend_ignore_service.get_db') - def test_is_friend_ignored_whitelist(self, mock_get_db, mock_redis_queue, mock_settings): - """娴嬭瘯鐧藉悕鍗曞姛鑳�""" - # 妯℃嫙閰嶇疆 - mock_settings.friend_ignore_enabled = True - mock_settings.friend_ignore_whitelist = ["娴嬭瘯鐢ㄦ埛1", "娴嬭瘯鐢ㄦ埛2"] - - # 妯℃嫙鏁版嵁搴撴煡璇� - mock_db = Mock() - mock_get_db.return_value.__next__.return_value.__enter__.return_value = mock_db - mock_get_db.return_value.__next__.return_value.__exit__.return_value = None - - # 妯℃嫙鑱旂郴浜烘煡璇㈢粨鏋� - 鏍规嵁鏄电О杩斿洖涓嶅悓鐨勮仈绯讳汉 - def mock_query_side_effect(*args, **kwargs): - mock_query = Mock() - mock_filter = Mock() - mock_query.filter.return_value = mock_filter - - # 鏍规嵁鏌ヨ鏉′欢杩斿洖涓嶅悓鐨勭粨鏋� - def mock_first(): - # 杩欓噷绠�鍖栧鐞嗭紝鍋囪鏌ヨ"娴嬭瘯鐢ㄦ埛1"鏃惰繑鍥瀢xid_whitelist1 - mock_contact = Mock() - mock_contact.wc_id = "wxid_whitelist1" - return mock_contact - - mock_filter.first = mock_first - return mock_query - - mock_db.query.side_effect = mock_query_side_effect - - # 妯℃嫙Redis鎿嶄綔 - mock_redis_client = Mock() - mock_redis_queue.redis_client = mock_redis_client - mock_redis_client.sismember.return_value = True # 鍦ㄥ拷鐣ュ垪琛ㄤ腑 - - # 娴嬭瘯鐧藉悕鍗曠敤鎴凤紙鍗充娇鍦ㄥ拷鐣ュ垪琛ㄤ腑涔熶笉搴旇琚拷鐣ワ級 - result = self.service.is_friend_ignored("wxid_whitelist1") - assert result is False - - # 娴嬭瘯闈炵櫧鍚嶅崟鐢ㄦ埛锛堝湪蹇界暐鍒楄〃涓簲璇ヨ蹇界暐锛� - result = self.service.is_friend_ignored("wxid_normal_user") - assert result is True - - @patch('app.services.friend_ignore_service.settings') - def test_is_friend_ignored_disabled(self, mock_settings): - """娴嬭瘯鍔熻兘绂佺敤鏃剁殑琛屼负""" - mock_settings.friend_ignore_enabled = False - mock_settings.friend_ignore_whitelist = [] - - result = self.service.is_friend_ignored("wxid_test1") - assert result is False - - @patch('app.services.friend_ignore_service.settings') - @patch('app.services.friend_ignore_service.redis_queue') - @patch('app.services.friend_ignore_service.get_db') - def test_get_ignore_status_info(self, mock_get_db, mock_redis_queue, mock_settings): - """娴嬭瘯鑾峰彇璇︾粏鐘舵�佷俊鎭�""" - mock_settings.friend_ignore_enabled = True - mock_settings.friend_ignore_whitelist = ["娴嬭瘯鐢ㄦ埛1"] - - # 妯℃嫙鏁版嵁搴撴煡璇� - mock_db = Mock() - mock_get_db.return_value.__enter__.return_value = mock_db - mock_get_db.return_value.__exit__.return_value = None - - # 妯℃嫙鑱旂郴浜烘煡璇㈢粨鏋� - mock_contact = Mock() - mock_contact.wc_id = "wxid_whitelist1" - mock_db.query.return_value.filter.return_value.first.return_value = mock_contact - - mock_redis_client = Mock() - mock_redis_queue.redis_client = mock_redis_client - mock_redis_client.sismember.return_value = True - - # 娴嬭瘯鐧藉悕鍗曠敤鎴� - result = self.service.get_ignore_status_info("wxid_whitelist1") - assert result["w_id"] == "wxid_whitelist1" - assert result["in_whitelist"] is True - assert result["final_ignored"] is False - assert "鐧藉悕鍗�" in result["reason"] - assert result["whitelist_nicknames"] == ["娴嬭瘯鐢ㄦ埛1"] - - # 娴嬭瘯鏅�氱敤鎴� - result = self.service.get_ignore_status_info("wxid_normal_user") - assert result["w_id"] == "wxid_normal_user" - assert result["in_whitelist"] is False - assert result["in_ignore_list"] is True - assert result["final_ignored"] is True - - @patch('app.services.friend_ignore_service.FriendIgnoreService._get_whitelist_wids') - @patch('app.services.friend_ignore_service.settings') - @patch('app.services.friend_ignore_service.redis_queue') - def test_is_friend_ignored_nickname_whitelist(self, mock_redis_queue, mock_settings, mock_get_whitelist_wids): - """娴嬭瘯鏄电О鐧藉悕鍗曞姛鑳�""" - # 妯℃嫙閰嶇疆 - mock_settings.friend_ignore_enabled = True - mock_settings.friend_ignore_whitelist = ["娴嬭瘯鐢ㄦ埛1", "娴嬭瘯鐢ㄦ埛2"] - - # 妯℃嫙鐧藉悕鍗晈_id杞崲缁撴灉 - mock_get_whitelist_wids.return_value = ["wxid_whitelist1", "wxid_whitelist2"] - - # 妯℃嫙Redis鎿嶄綔 - mock_redis_client = Mock() - mock_redis_queue.redis_client = mock_redis_client - mock_redis_client.sismember.return_value = True # 鍦ㄥ拷鐣ュ垪琛ㄤ腑 - - # 娴嬭瘯鐧藉悕鍗曠敤鎴凤紙鍗充娇鍦ㄥ拷鐣ュ垪琛ㄤ腑涔熶笉搴旇琚拷鐣ワ級 - result = self.service.is_friend_ignored("wxid_whitelist1") - assert result is False - - # 娴嬭瘯闈炵櫧鍚嶅崟鐢ㄦ埛锛堝湪蹇界暐鍒楄〃涓簲璇ヨ蹇界暐锛� - result = self.service.is_friend_ignored("wxid_normal_user") - assert result is True - - # 楠岃瘉鏂规硶琚皟鐢� - assert mock_get_whitelist_wids.call_count >= 2 diff --git a/tests/test_message_aggregator.py b/tests/test_message_aggregator.py deleted file mode 100644 index 42a7a27..0000000 --- a/tests/test_message_aggregator.py +++ /dev/null @@ -1,405 +0,0 @@ -""" -娑堟伅鑱氬悎鏈嶅姟娴嬭瘯 -""" - -import time -import threading -import pytest -from unittest.mock import patch, MagicMock -from app.services.message_aggregator import MessageAggregatorService, PendingMessage, MessageAggregation - - -class TestMessageAggregatorService: - """娑堟伅鑱氬悎鏈嶅姟娴嬭瘯绫�""" - - def setup_method(self): - """娴嬭瘯鍓嶅噯澶�""" - self.aggregator = MessageAggregatorService() - # 璁剧疆杈冪煭鐨勮秴鏃舵椂闂寸敤浜庢祴璇� - self.aggregator.aggregation_timeout = 2 - self.aggregator.aggregation_enabled = True - - def teardown_method(self): - """娴嬭瘯鍚庢竻鐞�""" - self.aggregator.stop() - - def test_should_aggregate_message_valid(self): - """娴嬭瘯鏈夋晥娑堟伅鐨勮仛鍚堝垽鏂�""" - message_data = { - "messageType": "80001", - "data": { - "fromUser": "wxid_test123", - "fromGroup": "group123@chatroom", - "content": "娴嬭瘯娑堟伅", - "self": False - } - } - - result = self.aggregator.should_aggregate_message(message_data) - assert result is True - - def test_should_aggregate_message_disabled(self): - """娴嬭瘯鑱氬悎鍔熻兘绂佺敤鏃剁殑鍒ゆ柇""" - self.aggregator.aggregation_enabled = False - - message_data = { - "messageType": "80001", - "data": { - "fromUser": "wxid_test123", - "fromGroup": "group123@chatroom", - "content": "娴嬭瘯娑堟伅", - "self": False - } - } - - result = self.aggregator.should_aggregate_message(message_data) - assert result is False - - def test_should_aggregate_message_wrong_type(self): - """娴嬭瘯閿欒娑堟伅绫诲瀷鐨勮仛鍚堝垽鏂�""" - message_data = { - "messageType": "80002", # 闈炵兢鑱婃秷鎭� - "data": { - "fromUser": "wxid_test123", - "fromGroup": "group123@chatroom", - "content": "娴嬭瘯娑堟伅", - "self": False - } - } - - result = self.aggregator.should_aggregate_message(message_data) - assert result is False - - def test_should_aggregate_message_self_message(self): - """娴嬭瘯鑷繁鍙戦�佺殑娑堟伅鐨勮仛鍚堝垽鏂�""" - message_data = { - "messageType": "80001", - "data": { - "fromUser": "wxid_test123", - "fromGroup": "group123@chatroom", - "content": "娴嬭瘯娑堟伅", - "self": True # 鑷繁鍙戦�佺殑娑堟伅 - } - } - - result = self.aggregator.should_aggregate_message(message_data) - assert result is False - - def test_should_aggregate_message_missing_fields(self): - """娴嬭瘯缂哄皯蹇呰瀛楁鐨勬秷鎭殑鑱氬悎鍒ゆ柇""" - message_data = { - "messageType": "80001", - "data": { - "fromUser": "wxid_test123", - # 缂哄皯 fromGroup - "content": "娴嬭瘯娑堟伅", - "self": False - } - } - - result = self.aggregator.should_aggregate_message(message_data) - assert result is False - - def test_add_message_to_aggregation_no_aggregation(self): - """娴嬭瘯涓嶉渶瑕佽仛鍚堢殑娑堟伅""" - self.aggregator.aggregation_enabled = False - - message_data = { - "messageType": "80001", - "data": { - "fromUser": "wxid_test123", - "fromGroup": "group123@chatroom", - "content": "娴嬭瘯娑堟伅", - "self": False - } - } - - should_process, aggregated_data = self.aggregator.add_message_to_aggregation(message_data) - - assert should_process is True - assert aggregated_data == message_data - - def test_add_message_to_aggregation_first_message(self): - """娴嬭瘯娣诲姞绗竴鏉℃秷鎭埌鑱氬悎""" - message_data = { - "messageType": "80001", - "data": { - "fromUser": "wxid_test123", - "fromGroup": "group123@chatroom", - "content": "绗竴鏉℃秷鎭�", - "self": False - } - } - - should_process, aggregated_data = self.aggregator.add_message_to_aggregation(message_data) - - assert should_process is False - assert aggregated_data is None - - # 妫�鏌ヨ仛鍚堢姸鎬� - status = self.aggregator.get_aggregation_status() - assert status["active_aggregations"] == 1 - assert "group123@chatroom:wxid_test123" in status["aggregations"] - - def test_add_message_to_aggregation_multiple_messages(self): - """娴嬭瘯娣诲姞澶氭潯娑堟伅鍒拌仛鍚�""" - message_data_1 = { - "messageType": "80001", - "data": { - "fromUser": "wxid_test123", - "fromGroup": "group123@chatroom", - "content": "绗竴鏉℃秷鎭�", - "self": False - } - } - - message_data_2 = { - "messageType": "80001", - "data": { - "fromUser": "wxid_test123", - "fromGroup": "group123@chatroom", - "content": "绗簩鏉℃秷鎭�", - "self": False - } - } - - # 娣诲姞绗竴鏉℃秷鎭� - should_process_1, _ = self.aggregator.add_message_to_aggregation(message_data_1) - assert should_process_1 is False - - # 娣诲姞绗簩鏉℃秷鎭� - should_process_2, _ = self.aggregator.add_message_to_aggregation(message_data_2) - assert should_process_2 is False - - # 妫�鏌ヨ仛鍚堢姸鎬� - status = self.aggregator.get_aggregation_status() - aggregation_info = status["aggregations"]["group123@chatroom:wxid_test123"] - assert aggregation_info["message_count"] == 2 - - @patch('app.services.message_processor.message_processor') - def test_process_aggregated_messages_timeout(self, mock_message_processor): - """娴嬭瘯鑱氬悎娑堟伅瓒呮椂澶勭悊""" - mock_message_processor.process_single_message.return_value = True - - message_data = { - "messageType": "80001", - "data": { - "fromUser": "wxid_test123", - "fromGroup": "group123@chatroom", - "content": "娴嬭瘯娑堟伅", - "self": False - } - } - - # 娣诲姞娑堟伅鍒拌仛鍚� - should_process, _ = self.aggregator.add_message_to_aggregation(message_data) - assert should_process is False - - # 绛夊緟瓒呮椂澶勭悊 - time.sleep(2.5) - - # 楠岃瘉娑堟伅澶勭悊鍣ㄨ璋冪敤 - mock_message_processor.process_single_message.assert_called_once() - - # 妫�鏌ヨ仛鍚堝凡琚竻鐞� - status = self.aggregator.get_aggregation_status() - assert status["active_aggregations"] == 0 - - def test_get_aggregation_status(self): - """娴嬭瘯鑾峰彇鑱氬悎鐘舵��""" - # 鍒濆鐘舵�� - status = self.aggregator.get_aggregation_status() - assert status["enabled"] is True - assert status["timeout"] == 2 - assert status["active_aggregations"] == 0 - assert status["aggregations"] == {} - - # 娣诲姞娑堟伅鍚庣殑鐘舵�� - message_data = { - "messageType": "80001", - "data": { - "fromUser": "wxid_test123", - "fromGroup": "group123@chatroom", - "content": "娴嬭瘯娑堟伅", - "self": False - } - } - - self.aggregator.add_message_to_aggregation(message_data) - - status = self.aggregator.get_aggregation_status() - assert status["active_aggregations"] == 1 - assert "group123@chatroom:wxid_test123" in status["aggregations"] - - aggregation_info = status["aggregations"]["group123@chatroom:wxid_test123"] - assert aggregation_info["from_user"] == "wxid_test123" - assert aggregation_info["from_group"] == "group123@chatroom" - assert aggregation_info["message_count"] == 1 - - @patch('app.services.message_processor.message_processor') - def test_force_process_aggregation(self, mock_message_processor): - """娴嬭瘯寮哄埗澶勭悊鑱氬悎娑堟伅""" - mock_message_processor.process_single_message.return_value = True - - message_data = { - "messageType": "80001", - "data": { - "fromUser": "wxid_test123", - "fromGroup": "group123@chatroom", - "content": "娴嬭瘯娑堟伅", - "self": False - } - } - - # 娣诲姞娑堟伅鍒拌仛鍚� - self.aggregator.add_message_to_aggregation(message_data) - - # 寮哄埗澶勭悊鑱氬悎 - result = self.aggregator.force_process_aggregation("wxid_test123", "group123@chatroom") - assert result is True - - # 楠岃瘉娑堟伅澶勭悊鍣ㄨ璋冪敤 - mock_message_processor.process_single_message.assert_called_once() - - # 妫�鏌ヨ仛鍚堝凡琚竻鐞� - status = self.aggregator.get_aggregation_status() - assert status["active_aggregations"] == 0 - - def test_force_process_aggregation_not_found(self): - """娴嬭瘯寮哄埗澶勭悊涓嶅瓨鍦ㄧ殑鑱氬悎""" - result = self.aggregator.force_process_aggregation("wxid_test123", "group123@chatroom") - assert result is False - - -class TestPendingMessage: - """寰呰仛鍚堟秷鎭祴璇曠被""" - - def test_pending_message_creation(self): - """娴嬭瘯寰呰仛鍚堟秷鎭垱寤�""" - message_data = {"test": "data"} - timestamp = time.time() - - pending_message = PendingMessage( - message_data=message_data, - timestamp=timestamp, - content="娴嬭瘯鍐呭", - from_user="wxid_test123", - from_group="group123@chatroom" - ) - - assert pending_message.message_data == message_data - assert pending_message.timestamp == timestamp - assert pending_message.content == "娴嬭瘯鍐呭" - assert pending_message.from_user == "wxid_test123" - assert pending_message.from_group == "group123@chatroom" - - -class TestMessageAggregation: - """娑堟伅鑱氬悎瀵硅薄娴嬭瘯绫�""" - - def test_message_aggregation_creation(self): - """娴嬭瘯娑堟伅鑱氬悎瀵硅薄鍒涘缓""" - aggregation = MessageAggregation( - from_user="wxid_test123", - from_group="group123@chatroom" - ) - - assert aggregation.from_user == "wxid_test123" - assert aggregation.from_group == "group123@chatroom" - assert len(aggregation.messages) == 0 - assert aggregation.first_message_time == 0.0 - assert aggregation.last_message_time == 0.0 - assert aggregation.timer is None - - def test_add_message(self): - """娴嬭瘯娣诲姞娑堟伅鍒拌仛鍚�""" - aggregation = MessageAggregation( - from_user="wxid_test123", - from_group="group123@chatroom" - ) - - timestamp = time.time() - pending_message = PendingMessage( - message_data={"test": "data"}, - timestamp=timestamp, - content="娴嬭瘯鍐呭", - from_user="wxid_test123", - from_group="group123@chatroom" - ) - - aggregation.add_message(pending_message) - - assert len(aggregation.messages) == 1 - assert aggregation.first_message_time == timestamp - assert aggregation.last_message_time == timestamp - - def test_get_aggregated_content(self): - """娴嬭瘯鑾峰彇鑱氬悎鍚庣殑鍐呭""" - aggregation = MessageAggregation( - from_user="wxid_test123", - from_group="group123@chatroom" - ) - - # 娣诲姞澶氭潯娑堟伅 - for i in range(3): - pending_message = PendingMessage( - message_data={"test": f"data{i}"}, - timestamp=time.time() + i, - content=f"娑堟伅{i+1}", - from_user="wxid_test123", - from_group="group123@chatroom" - ) - aggregation.add_message(pending_message) - - aggregated_content = aggregation.get_aggregated_content() - assert aggregated_content == "娑堟伅1\n娑堟伅2\n娑堟伅3" - - def test_get_latest_message_data(self): - """娴嬭瘯鑾峰彇鏈�鏂版秷鎭暟鎹�""" - aggregation = MessageAggregation( - from_user="wxid_test123", - from_group="group123@chatroom" - ) - - # 娣诲姞澶氭潯娑堟伅 - for i in range(3): - pending_message = PendingMessage( - message_data={"test": f"data{i}", "timestamp": i}, - timestamp=time.time() + i, - content=f"娑堟伅{i+1}", - from_user="wxid_test123", - from_group="group123@chatroom" - ) - aggregation.add_message(pending_message) - - latest_data = aggregation.get_latest_message_data() - assert latest_data["test"] == "data2" # 鏈�鍚庝竴鏉℃秷鎭� - assert latest_data["timestamp"] == 2 - - def test_clear(self): - """娴嬭瘯娓呯┖鑱氬悎鏁版嵁""" - aggregation = MessageAggregation( - from_user="wxid_test123", - from_group="group123@chatroom" - ) - - # 娣诲姞娑堟伅 - pending_message = PendingMessage( - message_data={"test": "data"}, - timestamp=time.time(), - content="娴嬭瘯鍐呭", - from_user="wxid_test123", - from_group="group123@chatroom" - ) - aggregation.add_message(pending_message) - - # 璁剧疆瀹氭椂鍣� - aggregation.timer = threading.Timer(1, lambda: None) - - # 娓呯┖ - aggregation.clear() - - assert len(aggregation.messages) == 0 - assert aggregation.first_message_time == 0.0 - assert aggregation.last_message_time == 0.0 - assert aggregation.timer is None diff --git a/tests/test_message_processor.py b/tests/test_message_processor.py deleted file mode 100644 index e149ce8..0000000 --- a/tests/test_message_processor.py +++ /dev/null @@ -1,259 +0,0 @@ -""" -娑堟伅澶勭悊鍣ㄦ祴璇� -""" -import pytest -from unittest.mock import Mock, patch -from app.services.message_processor import MessageProcessor - - -class TestMessageProcessor: - """娑堟伅澶勭悊鍣ㄦ祴璇曠被""" - - def setup_method(self): - """娴嬭瘯鍓嶅噯澶�""" - self.processor = MessageProcessor() - - @patch('app.services.message_processor.silence_service') - @patch('app.services.message_processor.friend_ignore_service') - def test_is_valid_group_message_success(self, mock_friend_ignore_service, mock_silence_service): - """娴嬭瘯鏈夋晥缇よ亰娑堟伅楠岃瘉""" - mock_silence_service.is_silence_active.return_value = False - mock_friend_ignore_service.is_friend_ignored.return_value = False - - callback_data = { - "messageType": "80001", - "data": { - "fromUser": "wxid_test123", - "fromGroup": "group123@chatroom", - "content": "娴嬭瘯娑堟伅", - "self": False - } - } - - result = self.processor.is_valid_group_message(callback_data) - assert result is True - - def test_is_valid_group_message_wrong_type(self): - """娴嬭瘯閿欒娑堟伅绫诲瀷""" - callback_data = { - "messageType": "80002", # 闈炵兢鑱婃秷鎭� - "data": { - "fromUser": "wxid_test123", - "fromGroup": "group123@chatroom", - "content": "娴嬭瘯娑堟伅", - "self": False - } - } - - result = self.processor.is_valid_group_message(callback_data) - assert result is False - - def test_is_valid_group_message_self_sent(self): - """娴嬭瘯鑷繁鍙戦�佺殑娑堟伅""" - callback_data = { - "messageType": "80001", - "data": { - "fromUser": "wxid_test123", - "fromGroup": "group123@chatroom", - "content": "娴嬭瘯娑堟伅", - "self": True # 鑷繁鍙戦�佺殑娑堟伅 - } - } - - result = self.processor.is_valid_group_message(callback_data) - assert result is False - - def test_is_valid_group_message_missing_fields(self): - """娴嬭瘯缂哄皯蹇呰瀛楁""" - callback_data = { - "messageType": "80001", - "data": { - "fromUser": "wxid_test123", - # 缂哄皯 fromGroup 鍜� content - "self": False - } - } - - result = self.processor.is_valid_group_message(callback_data) - assert result is False - - @patch('app.services.message_processor.silence_service') - @patch('app.services.message_processor.friend_ignore_service') - def test_is_valid_group_message_friend_ignored(self, mock_friend_ignore_service, mock_silence_service): - """娴嬭瘯濂藉弸鍦ㄥ拷鐣ュ垪琛ㄤ腑鐨勬秷鎭�""" - mock_silence_service.is_silence_active.side_effect = [False, False] # 涓ゆ妫�鏌ラ兘杩斿洖False - mock_silence_service.activate_silence_mode.return_value = True - mock_friend_ignore_service.is_friend_ignored.return_value = True - - callback_data = { - "messageType": "80001", - "data": { - "fromUser": "wxid_test123", - "fromGroup": "group123@chatroom", - "content": "娴嬭瘯娑堟伅", - "self": False - } - } - - result = self.processor.is_valid_group_message(callback_data) - assert result is False - mock_friend_ignore_service.is_friend_ignored.assert_called_once_with("wxid_test123") - - @patch('app.services.message_processor.silence_service') - @patch('app.services.message_processor.friend_ignore_service') - def test_is_valid_group_message_friend_not_ignored(self, mock_friend_ignore_service, mock_silence_service): - """娴嬭瘯濂藉弸涓嶅湪蹇界暐鍒楄〃涓殑娑堟伅""" - mock_silence_service.is_silence_active.return_value = False - mock_friend_ignore_service.is_friend_ignored.return_value = False - - callback_data = { - "messageType": "80001", - "data": { - "fromUser": "wxid_test123", - "fromGroup": "group123@chatroom", - "content": "娴嬭瘯娑堟伅", - "self": False - } - } - - result = self.processor.is_valid_group_message(callback_data) - assert result is True - mock_friend_ignore_service.is_friend_ignored.assert_called_once_with("wxid_test123") - - @patch('app.services.message_processor.silence_service') - @patch('app.services.message_processor.friend_ignore_service') - def test_is_valid_group_message_silence_active(self, mock_friend_ignore_service, mock_silence_service): - """娴嬭瘯缇ょ粍闈欓粯妯″紡婵�娲绘椂鐨勬秷鎭鐞嗭紙闈炲拷鐣ュソ鍙嬶級""" - mock_friend_ignore_service.is_friend_ignored.return_value = False - mock_silence_service.is_silence_active.return_value = True - - callback_data = { - "messageType": "80001", - "data": { - "fromUser": "wxid_test123", - "fromGroup": "group123@chatroom", - "content": "娴嬭瘯娑堟伅", - "self": False - } - } - - result = self.processor.is_valid_group_message(callback_data) - assert result is False - mock_silence_service.is_silence_active.assert_called_once_with("group123@chatroom") - - @patch('app.services.message_processor.silence_service') - @patch('app.services.message_processor.friend_ignore_service') - def test_is_valid_group_message_friend_ignored_activate_silence(self, mock_friend_ignore_service, mock_silence_service): - """娴嬭瘯濂藉弸琚拷鐣ユ椂婵�娲婚潤榛樻ā寮�""" - mock_silence_service.is_silence_active.side_effect = [False, False] # 绗竴娆℃鏌ユ湭婵�娲伙紝绗簩娆℃鏌ュ拷鐣ュソ鍙嬫椂涔熸湭婵�娲� - mock_silence_service.activate_silence_mode.return_value = True - mock_friend_ignore_service.is_friend_ignored.return_value = True - - callback_data = { - "messageType": "80001", - "data": { - "fromUser": "wxid_test123", - "fromGroup": "group123@chatroom", - "content": "娴嬭瘯娑堟伅", - "self": False - } - } - - result = self.processor.is_valid_group_message(callback_data) - assert result is False - mock_silence_service.activate_silence_mode.assert_called_once_with("group123@chatroom") - - @patch('app.services.message_processor.silence_service') - @patch('app.services.message_processor.friend_ignore_service') - def test_is_valid_group_message_friend_ignored_extend_silence(self, mock_friend_ignore_service, mock_silence_service): - """娴嬭瘯濂藉弸琚拷鐣ユ椂寤堕暱缇ょ粍闈欓粯妯″紡""" - mock_silence_service.is_silence_active.return_value = True # 缇ょ粍闈欓粯妯″紡宸叉縺娲� - mock_silence_service.extend_silence_mode.return_value = True - mock_friend_ignore_service.is_friend_ignored.return_value = True - - callback_data = { - "messageType": "80001", - "data": { - "fromUser": "wxid_test123", - "fromGroup": "group123@chatroom", - "content": "娴嬭瘯娑堟伅", - "self": False - } - } - - result = self.processor.is_valid_group_message(callback_data) - assert result is False - mock_silence_service.extend_silence_mode.assert_called_once_with("group123@chatroom") - - @patch('app.services.message_processor.silence_service') - @patch('app.services.message_processor.friend_ignore_service') - def test_is_valid_group_message_friend_ignored_in_silence_mode(self, mock_friend_ignore_service, mock_silence_service): - """娴嬭瘯缇ょ粍闈欓粯妯″紡涓嬪ソ鍙嬫秷鎭粛鑳藉埛鏂版椂闀�""" - # 妯℃嫙缇ょ粍闈欓粯妯″紡宸叉縺娲荤殑鎯呭喌 - mock_friend_ignore_service.is_friend_ignored.return_value = True - mock_silence_service.is_silence_active.return_value = True - mock_silence_service.extend_silence_mode.return_value = True - - callback_data = { - "messageType": "80001", - "data": { - "fromUser": "wxid_ignored_friend", - "fromGroup": "group123@chatroom", - "content": "琚拷鐣ュソ鍙嬪湪缇ょ粍闈欓粯妯″紡涓嬬殑娑堟伅", - "self": False - } - } - - result = self.processor.is_valid_group_message(callback_data) - assert result is False - - # 楠岃瘉濂藉弸蹇界暐妫�鏌ヨ璋冪敤 - mock_friend_ignore_service.is_friend_ignored.assert_called_once_with("wxid_ignored_friend") - # 楠岃瘉缇ょ粍闈欓粯妯″紡鏃堕棿琚欢闀� - mock_silence_service.extend_silence_mode.assert_called_once_with("group123@chatroom") - - @patch('app.services.message_processor.silence_service') - @patch('app.services.message_processor.friend_ignore_service') - @patch('app.services.message_processor.redis_queue') - def test_enqueue_callback_message_success(self, mock_redis_queue, mock_friend_ignore_service, mock_silence_service): - """娴嬭瘯娑堟伅鍏ラ槦鎴愬姛""" - mock_silence_service.is_silence_active.return_value = False - mock_friend_ignore_service.is_friend_ignored.return_value = False - mock_redis_queue.enqueue_message.return_value = True - - callback_data = { - "messageType": "80001", - "data": { - "fromUser": "wxid_test123", - "fromGroup": "group123@chatroom", - "content": "娴嬭瘯娑堟伅", - "self": False - } - } - - result = self.processor.enqueue_callback_message(callback_data) - - assert result is True - mock_redis_queue.enqueue_message.assert_called_once_with("wxid_test123", callback_data) - - @patch('app.services.message_processor.redis_queue') - def test_enqueue_callback_message_invalid(self, mock_redis_queue): - """娴嬭瘯鏃犳晥娑堟伅鍏ラ槦""" - callback_data = { - "messageType": "80002", # 鏃犳晥娑堟伅绫诲瀷 - "data": { - "fromUser": "wxid_test123", - "fromGroup": "group123@chatroom", - "content": "娴嬭瘯娑堟伅", - "self": False - } - } - - result = self.processor.enqueue_callback_message(callback_data) - - assert result is False - mock_redis_queue.enqueue_message.assert_not_called() - - -if __name__ == "__main__": - pytest.main([__file__]) diff --git a/tests/test_online_status_monitor.py b/tests/test_online_status_monitor.py deleted file mode 100644 index c9368aa..0000000 --- a/tests/test_online_status_monitor.py +++ /dev/null @@ -1,168 +0,0 @@ -""" -鍦ㄧ嚎鐘舵�佺洃鎺у姛鑳芥祴璇� -""" - -import pytest -import time -from unittest.mock import Mock, patch -from app.services.ecloud_client import ecloud_client -from app.services.email_service import email_service -from app.services.sms_service import sms_service -from app.workers.online_status_worker import online_status_worker - - -class TestOnlineStatusMonitor: - """鍦ㄧ嚎鐘舵�佺洃鎺ф祴璇�""" - - def test_ecloud_query_online_wechat_list(self): - """娴嬭瘯鏌ヨ鍦ㄧ嚎寰俊鍒楄〃""" - # 妯℃嫙鏈夊湪绾垮井淇$殑鎯呭喌 - with patch.object(ecloud_client.session, 'post') as mock_post: - mock_response = Mock() - mock_response.json.return_value = { - "code": "1000", - "message": "鎴愬姛", - "data": [ - { - "wcId": "wxid_test123", - "wId": "test-w-id-123" - } - ] - } - mock_post.return_value = mock_response - - result = ecloud_client.query_online_wechat_list() - assert result is not None - assert len(result) == 1 - assert result[0]["wcId"] == "wxid_test123" - - # 妯℃嫙娌℃湁鍦ㄧ嚎寰俊鐨勬儏鍐� - with patch.object(ecloud_client.session, 'post') as mock_post: - mock_response = Mock() - mock_response.json.return_value = { - "code": "1000", - "message": "鎴愬姛", - "data": [] - } - mock_post.return_value = mock_response - - result = ecloud_client.query_online_wechat_list() - assert result is not None - assert len(result) == 0 - - def test_sms_service_generate_sign(self): - """娴嬭瘯鐭俊鏈嶅姟绛惧悕鐢熸垚""" - # 浣跨敤鏂囨。涓殑绀轰緥鏁版嵁 - sms_service.username = "test" - sms_service.password = "123" - timestamp = 1596254400000 - - sign = sms_service._generate_sign(timestamp) - expected_sign = "e315cf297826abdeb2092cc57f29f0bf" - - assert sign == expected_sign - - def test_email_service_disabled(self): - """娴嬭瘯閭欢鏈嶅姟绂佺敤鐘舵��""" - with patch('config.settings.email_enabled', False): - result = email_service.send_notification("娴嬭瘯涓婚", "娴嬭瘯鍐呭") - assert result is True # 绂佺敤鏃跺簲璇ヨ繑鍥濼rue - - def test_sms_service_disabled(self): - """娴嬭瘯鐭俊鏈嶅姟绂佺敤鐘舵��""" - with patch('config.settings.sms_enabled', False): - result = sms_service.send_notification("娴嬭瘯鍐呭") - assert result is True # 绂佺敤鏃跺簲璇ヨ繑鍥濼rue - - def test_online_status_worker_disabled(self): - """娴嬭瘯鍦ㄧ嚎鐘舵�佹娴嬪伐浣滆繘绋嬬鐢ㄧ姸鎬�""" - with patch('config.settings.online_status_enabled', False): - # 鍒涘缓鏂扮殑宸ヤ綔杩涚▼瀹炰緥杩涜娴嬭瘯 - from app.workers.online_status_worker import OnlineStatusWorker - test_worker = OnlineStatusWorker() - - test_worker.start() - assert test_worker.running is False - assert test_worker.worker_thread is None - - def test_online_status_worker_status(self): - """娴嬭瘯鑾峰彇宸ヤ綔杩涚▼鐘舵��""" - status = online_status_worker.get_status() - - assert "running" in status - assert "enabled" in status - assert "check_interval_minutes" in status - assert "last_notification_time" in status - assert "notification_cooldown_minutes" in status - - assert isinstance(status["running"], bool) - assert isinstance(status["enabled"], bool) - assert isinstance(status["check_interval_minutes"], (int, float)) - assert isinstance(status["notification_cooldown_minutes"], (int, float)) - - def test_dynamic_w_id_update(self): - """娴嬭瘯鍔ㄦ�亀_id鏇存柊鍔熻兘""" - from config import settings - - # 淇濆瓨鍘熷w_id - original_w_id = settings.get_current_w_id() - - try: - # 娴嬭瘯鏇存柊w_id - new_w_id = "test-new-w-id-123" - success = settings.update_ecloud_w_id(new_w_id) - - # 楠岃瘉鏇存柊鎴愬姛 - assert success is True - assert settings.get_current_w_id() == new_w_id - - # 娴嬭瘯鐩稿悓w_id涓嶄細閲嶅鏇存柊 - success = settings.update_ecloud_w_id(new_w_id) - assert success is True - - finally: - # 鎭㈠鍘熷w_id - settings.update_ecloud_w_id(original_w_id) - - def test_w_id_update_in_online_status_check(self): - """娴嬭瘯鍦ㄧ嚎鐘舵�佹娴嬩腑鐨剋_id鏇存柊""" - from app.workers.online_status_worker import OnlineStatusWorker - - # 鍒涘缓娴嬭瘯宸ヤ綔杩涚▼瀹炰緥 - test_worker = OnlineStatusWorker() - - # 妯℃嫙w_id鏇存柊 - test_w_id = "test-w-id-456" - test_worker._update_w_id_if_needed(test_w_id) - - # 杩欓噷涓昏娴嬭瘯鏂规硶涓嶄細鎶涘嚭寮傚父 - # 瀹為檯鐨剋_id鏇存柊閫昏緫鍦╯ettings涓凡缁忔祴璇曡繃浜� - - def test_startup_contact_sync_logic(self): - """娴嬭瘯鍚姩鏃惰仈绯讳汉鍚屾閫昏緫""" - # 娴嬭瘯娌℃湁鍦ㄧ嚎寰俊鏃剁殑澶勭悊閫昏緫 - with patch.object(ecloud_client, 'query_online_wechat_list') as mock_query: - # 妯℃嫙鏌ヨ澶辫触 - mock_query.return_value = None - # 杩欑鎯呭喌涓嬪簲璇ヨ烦杩囪仈绯讳汉鍚屾锛屼笉浼氭姏鍑哄紓甯� - - # 妯℃嫙娌℃湁鍦ㄧ嚎寰俊 - mock_query.return_value = [] - # 杩欑鎯呭喌涓嬩篃搴旇璺宠繃鑱旂郴浜哄悓姝� - - # 妯℃嫙鏈夊湪绾垮井淇� - mock_query.return_value = [{"wcId": "test_wc_id", "wId": "test_w_id"}] - # 杩欑鎯呭喌涓嬩細灏濊瘯鍚屾鑱旂郴浜� - - def test_email_service_connection_handling(self): - """娴嬭瘯閭欢鏈嶅姟杩炴帴澶勭悊""" - # 娴嬭瘯閭欢鏈嶅姟鐨勮繛鎺ュ拰鍏抽棴閫昏緫 - # 涓昏纭繚涓嶄細鍥犱负杩炴帴鍏抽棴寮傚父鑰屽奖鍝嶅彂閫佺粨鏋� - - # 杩欓噷涓昏娴嬭瘯鏂规硶缁撴瀯锛屽疄闄呯殑SMTP娴嬭瘯闇�瑕佺湡瀹炵殑閭欢鏈嶅姟鍣� - assert hasattr(email_service, 'send_email') - assert hasattr(email_service, 'test_connection') - - -if __name__ == "__main__": - pytest.main([__file__]) diff --git a/tests/test_silence_service.py b/tests/test_silence_service.py deleted file mode 100644 index 181f9f2..0000000 --- a/tests/test_silence_service.py +++ /dev/null @@ -1,177 +0,0 @@ -""" -闈欓粯妯″紡鏈嶅姟娴嬭瘯 -""" - -import time -import pytest -from unittest.mock import Mock, patch - -from app.services.silence_service import SilenceService - - -class TestSilenceService: - """闈欓粯妯″紡鏈嶅姟娴嬭瘯绫�""" - - def setup_method(self): - """娴嬭瘯鍓嶅噯澶�""" - self.service = SilenceService() - - @patch('app.services.silence_service.settings') - @patch('app.services.silence_service.redis_queue') - def test_activate_silence_mode_success(self, mock_redis_queue, mock_settings): - """娴嬭瘯鎴愬姛婵�娲荤兢缁勯潤榛樻ā寮�""" - # 妯℃嫙閰嶇疆 - mock_settings.silence_mode_enabled = True - mock_settings.silence_duration_minutes = 10 - - # 妯℃嫙Redis鎿嶄綔 - mock_redis_client = Mock() - mock_redis_queue.redis_client = mock_redis_client - mock_redis_client.setex.return_value = True - - # 娴嬭瘯婵�娲荤兢缁勯潤榛樻ā寮� - group_id = "test_group@chatroom" - result = self.service.activate_silence_mode(group_id) - - assert result is True - # 楠岃瘉Redis璋冪敤 - assert mock_redis_client.setex.call_count == 2 # 璁剧疆涓や釜閿� - - @patch('app.services.silence_service.settings') - def test_activate_silence_mode_disabled(self, mock_settings): - """娴嬭瘯闈欓粯妯″紡鍔熻兘绂佺敤鏃剁殑琛屼负""" - mock_settings.silence_mode_enabled = False - - group_id = "test_group@chatroom" - result = self.service.activate_silence_mode(group_id) - assert result is False - - @patch('app.services.silence_service.settings') - @patch('app.services.silence_service.redis_queue') - def test_is_silence_active(self, mock_redis_queue, mock_settings): - """娴嬭瘯妫�鏌ラ潤榛樻ā寮忔槸鍚︽縺娲�""" - mock_settings.silence_mode_enabled = True - - # 妯℃嫙Redis鎿嶄綔 - mock_redis_client = Mock() - mock_redis_queue.redis_client = mock_redis_client - - # 娴嬭瘯闈欓粯妯″紡婵�娲荤姸鎬� - mock_redis_client.exists.return_value = True - group_id = "test_group@chatroom" - result = self.service.is_silence_active(group_id) - assert result is True - - # 娴嬭瘯闈欓粯妯″紡鏈縺娲荤姸鎬� - mock_redis_client.exists.return_value = False - result = self.service.is_silence_active(group_id) - assert result is False - - @patch('app.services.silence_service.settings') - def test_is_silence_active_disabled(self, mock_settings): - """娴嬭瘯鍔熻兘绂佺敤鏃剁殑闈欓粯妯″紡妫�鏌�""" - mock_settings.silence_mode_enabled = False - - group_id = "test_group@chatroom" - result = self.service.is_silence_active(group_id) - assert result is False - - @patch('app.services.silence_service.settings') - @patch('app.services.silence_service.redis_queue') - def test_get_silence_remaining_time(self, mock_redis_queue, mock_settings): - """娴嬭瘯鑾峰彇闈欓粯妯″紡鍓╀綑鏃堕棿""" - mock_settings.silence_mode_enabled = True - - # 妯℃嫙Redis鎿嶄綔 - mock_redis_client = Mock() - mock_redis_queue.redis_client = mock_redis_client - - # 妯℃嫙闈欓粯妯″紡婵�娲� - mock_redis_client.exists.return_value = True - - # 妯℃嫙缁撴潫鏃堕棿锛堝綋鍓嶆椂闂� + 300绉掞級 - future_time = time.time() + 300 - mock_redis_client.get.return_value = str(future_time) - - group_id = "test_group@chatroom" - result = self.service.get_silence_remaining_time(group_id) - assert result is not None - assert result > 0 - assert result <= 300 - - @patch('app.services.silence_service.settings') - @patch('app.services.silence_service.redis_queue') - def test_get_silence_remaining_time_inactive(self, mock_redis_queue, mock_settings): - """娴嬭瘯闈欓粯妯″紡鏈縺娲绘椂鑾峰彇鍓╀綑鏃堕棿""" - mock_settings.silence_mode_enabled = True - - # 妯℃嫙Redis鎿嶄綔 - mock_redis_client = Mock() - mock_redis_queue.redis_client = mock_redis_client - mock_redis_client.exists.return_value = False - - group_id = "test_group@chatroom" - result = self.service.get_silence_remaining_time(group_id) - assert result is None - - @patch('app.services.silence_service.settings') - @patch('app.services.silence_service.redis_queue') - def test_deactivate_silence_mode(self, mock_redis_queue, mock_settings): - """娴嬭瘯鎵嬪姩鍋滅敤闈欓粯妯″紡""" - # 妯℃嫙Redis鎿嶄綔 - mock_redis_client = Mock() - mock_redis_queue.redis_client = mock_redis_client - mock_redis_client.delete.return_value = True - - result = self.service.deactivate_silence_mode() - assert result is True - - # 楠岃瘉鍒犻櫎浜嗕袱涓敭 - assert mock_redis_client.delete.call_count == 2 - - @patch('app.services.silence_service.settings') - @patch('app.services.silence_service.redis_queue') - def test_extend_silence_mode(self, mock_redis_queue, mock_settings): - """娴嬭瘯寤堕暱闈欓粯妯″紡""" - mock_settings.silence_mode_enabled = True - mock_settings.silence_duration_minutes = 10 - - # 妯℃嫙Redis鎿嶄綔 - mock_redis_client = Mock() - mock_redis_queue.redis_client = mock_redis_client - mock_redis_client.setex.return_value = True - - group_id = "test_group@chatroom" - result = self.service.extend_silence_mode(group_id) - assert result is True - - @patch('app.services.silence_service.settings') - def test_get_silence_status(self, mock_settings): - """娴嬭瘯鑾峰彇闈欓粯妯″紡鐘舵��""" - mock_settings.silence_mode_enabled = True - mock_settings.silence_duration_minutes = 10 - - with patch.object(self.service, 'is_silence_active', return_value=False): - status = self.service.get_silence_status() - - assert status["enabled"] is True - assert status["active"] is False - assert status["duration_minutes"] == 10 - assert status["remaining_seconds"] is None - assert status["remaining_minutes"] is None - - @patch('app.services.silence_service.settings') - def test_get_silence_status_active(self, mock_settings): - """娴嬭瘯鑾峰彇婵�娲荤姸鎬佺殑闈欓粯妯″紡鐘舵��""" - mock_settings.silence_mode_enabled = True - mock_settings.silence_duration_minutes = 10 - - with patch.object(self.service, 'is_silence_active', return_value=True), \ - patch.object(self.service, 'get_silence_remaining_time', return_value=300): - - status = self.service.get_silence_status() - - assert status["enabled"] is True - assert status["active"] is True - assert status["remaining_seconds"] == 300 - assert status["remaining_minutes"] == 5.0 -- Gitblit v1.9.1