This commit is contained in:
Your Name
2026-07-23 17:56:25 +08:00
parent a05dae8412
commit 4970d8f8d3
4262 changed files with 735221 additions and 0 deletions
+165
View File
@@ -0,0 +1,165 @@
from __future__ import annotations
import os
import sys
import unittest
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import AsyncMock, patch
BACKEND_DIR = Path(__file__).resolve().parents[1]
os.environ["KEFU_DB_TYPE"] = "sqlite"
os.environ["KEFU_DATABASE_URL"] = ""
os.environ["KEFU_DB_PATH"] = str(BACKEND_DIR / "kefu.db")
if str(BACKEND_DIR) not in sys.path:
sys.path.insert(0, str(BACKEND_DIR))
import main
def _queue_item(account_id: int, job_id: str = "job-1") -> dict:
return {
"job_id": job_id,
"account_id": account_id,
"position": 1,
"status": "waiting",
"expedited": False,
"description": "回复 测试用户",
"sender_name": "测试用户",
"sender_id": "peer-1",
"sender_avatar": None,
"conversation_id": "conv-1",
"incoming_content": "你好",
"incoming_contents": ["你好", "第二条"],
"message_count": 2,
"replies": ["您好"],
"interval_seconds": 60,
"enqueued_at": "2026-07-20T10:00:00+00:00",
"scheduled_at": "2026-07-20T10:01:00+00:00",
"remaining_seconds": 60,
}
class _FakeService:
def __init__(self, account_id: int, items=None, action=None):
self.account_id = account_id
self._running = True
self.items = list(items or [])
self.action = action or {
"status": "accepted",
"job_id": "job-1",
"shifted_count": 2,
}
async def get_reply_queue_snapshot(self):
return list(self.items)
async def send_queued_reply_now(self, job_id: str):
return {**self.action, "job_id": job_id}
class ReplyQueueApiTests(unittest.IsolatedAsyncioTestCase):
async def asyncSetUp(self):
self.original_workers = main.manager.workers
main.manager.workers = {}
async def asyncTearDown(self):
main.manager.workers = self.original_workers
async def test_summary_filters_accounts_by_owner_scope(self):
service_one = _FakeService(1, [_queue_item(1)])
service_two = _FakeService(2, [_queue_item(2, "job-2")])
main.manager.workers = {
1: SimpleNamespace(is_running=True, _im_service=service_one),
2: SimpleNamespace(is_running=True, _im_service=service_two),
}
with patch.object(main, "owned_account_ids", AsyncMock(return_value={1})):
response = await main.get_reply_queue_summaries(
db=object(),
user=SimpleNamespace(id=10, role="operator"),
)
self.assertEqual(response.total_pending, 1)
self.assertEqual([item.account_id for item in response.items], [1])
async def test_offline_account_detail_returns_empty_snapshot(self):
account = SimpleNamespace(id=1, reply_delay_seconds=0)
with (
patch.object(main, "get_owned_account", AsyncMock(return_value=account)),
patch.object(main, "_global_reply_delay_seconds", return_value=0),
):
response = await main.get_account_reply_queue(
account_id=1,
db=object(),
user=SimpleNamespace(id=10, role="operator"),
)
self.assertFalse(response.running)
self.assertEqual(response.pending_count, 0)
self.assertEqual(response.items, [])
async def test_detail_preserves_merged_incoming_messages(self):
account = SimpleNamespace(id=1, reply_delay_seconds=60)
service = _FakeService(1, [_queue_item(1)])
main.manager.workers = {
1: SimpleNamespace(is_running=True, _im_service=service),
}
with (
patch.object(main, "get_owned_account", AsyncMock(return_value=account)),
patch.object(main, "_resolve_effective_reply_delay", return_value=60),
):
response = await main.get_account_reply_queue(
account_id=1,
db=object(),
user=SimpleNamespace(id=10, role="operator"),
)
self.assertEqual(response.pending_count, 1)
self.assertEqual(response.items[0].incoming_contents, ["你好", "第二条"])
self.assertEqual(response.items[0].message_count, 2)
self.assertEqual(response.items[0].replies, ["您好"])
async def test_send_now_returns_shifted_count(self):
service = _FakeService(1)
main.manager.workers = {
1: SimpleNamespace(is_running=True, _im_service=service),
}
with (
patch.object(main, "get_owned_account", AsyncMock(return_value=SimpleNamespace(id=1))),
patch.object(main.system_logger, "record"),
):
response = await main.send_account_queued_reply_now(
account_id=1,
job_id="job-1",
db=object(),
user=SimpleNamespace(id=10, role="operator"),
)
self.assertEqual(response.status, "accepted")
self.assertEqual(response.shifted_count, 2)
async def test_cross_account_or_finished_job_returns_not_found(self):
service = _FakeService(1, action={"status": "not_found"})
main.manager.workers = {
1: SimpleNamespace(is_running=True, _im_service=service),
}
with patch.object(
main,
"get_owned_account",
AsyncMock(return_value=SimpleNamespace(id=1)),
):
with self.assertRaises(main.HTTPException) as caught:
await main.send_account_queued_reply_now(
account_id=1,
job_id="other-account-job",
db=object(),
user=SimpleNamespace(id=10, role="operator"),
)
self.assertEqual(caught.exception.status_code, 404)
if __name__ == "__main__":
unittest.main()