first commit

This commit is contained in:
2026-08-15 14:43:56 +08:00
commit 63b8c28e1d
340 changed files with 59515 additions and 0 deletions
+242
View File
@@ -0,0 +1,242 @@
import asyncio
import logging
import tempfile
import time
import unittest
from datetime import datetime, timedelta
from types import SimpleNamespace
from unittest.mock import AsyncMock, MagicMock, patch
from app.danmu_queue import CommandHandler, QueueManager, QueueSystem
class IdleDefaultQueueStateTests(unittest.TestCase):
def setUp(self):
self.temp_dir = tempfile.TemporaryDirectory()
self.manager = QueueManager(self.temp_dir.name, logging.getLogger("idle-default-test"))
self.manager.state["has_user_finished_once"] = True
def tearDown(self):
self.temp_dir.cleanup()
def _start_default_with_waiting_admin(self, uid=101):
self.manager.state.update({
"default_running": True,
"current_group": "薄荷",
"current_group_run_id": "default-run",
"group_start_time": datetime.now().isoformat(),
"billing_uid": None,
})
self.manager.join_queue(uid)
def test_default_completion_preserves_newly_joined_admin(self):
self._start_default_with_waiting_admin()
original_window = self.manager.state["admin_window_end"]
result = self.manager.group_finished("default-run")
self.assertTrue(result["accepted"])
self.assertTrue(result["was_default_running"])
self.assertTrue(result["need_default"])
self.assertEqual(self.manager.state["queue"], [101])
self.assertEqual(self.manager.state["current_admin_uid"], 101)
self.assertEqual(self.manager.state["admin_window_end"], original_window)
self.assertFalse(self.manager.state["default_running"])
self.assertIsNone(self.manager.state["current_group"])
def test_admin_window_can_expire_while_default_group_runs(self):
self._start_default_with_waiting_admin()
self.manager.state["admin_window_end"] = time.time() - 1
result = self.manager.check_admin_window_timeout()
self.assertTrue(result["timeout"])
self.assertEqual(result["kicked_uid"], 101)
self.assertEqual(self.manager.state["queue"], [])
self.assertTrue(self.manager.state["default_running"])
self.assertEqual(self.manager.state["current_group"], "薄荷")
def test_waiting_admin_leave_preserves_default_group(self):
self._start_default_with_waiting_admin()
result = self.manager.leave_queue(101)
self.assertTrue(result["success"])
self.assertTrue(self.manager.state["default_running"])
self.assertEqual(self.manager.state["current_group"], "薄荷")
self.assertEqual(self.manager.state["current_group_run_id"], "default-run")
class IdleDefaultFlowTests(unittest.IsolatedAsyncioTestCase):
@staticmethod
def _make_system(manager, runner):
system = QueueSystem.__new__(QueueSystem)
system.queue_mgr = manager
system.user_mgr = SimpleNamespace(
users={"101": {"uname": "tester"}},
get_points=lambda _uid: 10,
)
system.config = SimpleNamespace(default_group="薄荷")
system.runner = runner
system.log_monitor = SimpleNamespace(set_current_group=MagicMock())
system.login_monitor = SimpleNamespace(reset=MagicMock())
system.stats_store = None
system.logger = logging.getLogger("idle-default-race-test")
system.broadcast = AsyncMock()
system._default_start_lock = asyncio.Lock()
return system
@staticmethod
def _make_handler(system):
handler = CommandHandler.__new__(CommandHandler)
handler.config = SimpleNamespace(admin_uids=[])
handler.queue_mgr = system.queue_mgr
handler.user_mgr = system.user_mgr
handler.runner = system.runner
handler.log_monitor = system.log_monitor
handler.login_monitor = system.login_monitor
handler.logger = system.logger
handler.stats_store = None
handler.system = system
handler.broadcast = AsyncMock()
return handler
async def test_login_serializes_with_default_group_start(self):
with tempfile.TemporaryDirectory() as temp_dir:
manager = QueueManager(temp_dir, logging.getLogger("idle-default-login-race"))
manager.state["has_user_finished_once"] = True
manager.join_queue(101)
manager.state.update({
"default_running": True,
"current_group": "薄荷",
"current_group_run_id": "default-run",
"group_start_time": datetime.now().isoformat(),
})
manager._save()
kill_started = asyncio.Event()
allow_kill = asyncio.Event()
async def blocked_kill(**_kwargs):
kill_started.set()
await allow_kill.wait()
return True
runner = SimpleNamespace(
kill_bgi=AsyncMock(side_effect=blocked_kill),
start_groups=AsyncMock(return_value=True),
)
system = self._make_system(manager, runner)
handler = self._make_handler(system)
login_task = asyncio.create_task(handler._cmd_login(101, "tester"))
await kill_started.wait()
default_task = asyncio.create_task(system._start_default_group())
await asyncio.sleep(0)
allow_kill.set()
await asyncio.gather(login_task, default_task)
self.assertEqual(manager.state["login_status"], "logining")
self.assertFalse(manager.state["default_running"])
self.assertIsNone(manager.state["current_group"])
runner.start_groups.assert_awaited_once_with(["扫码上号"])
async def test_login_watchdog_runs_even_if_default_state_leaks(self):
with tempfile.TemporaryDirectory() as temp_dir:
manager = QueueManager(temp_dir, logging.getLogger("login-watchdog-default-leak"))
manager.join_queue(101)
manager.state.update({
"login_status": "logining",
"login_started_at": (datetime.now() - timedelta(seconds=300)).isoformat(),
"default_running": True,
"current_group": "薄荷",
})
manager._save()
system = self._make_system(manager, SimpleNamespace())
system._handle_login_timeout = AsyncMock()
with patch(
"app.danmu_queue.asyncio.sleep",
side_effect=[None, asyncio.CancelledError()],
):
with self.assertRaises(asyncio.CancelledError):
await system._check_login_watchdog_loop()
system._handle_login_timeout.assert_awaited_once_with(101, "tester")
async def test_user_group_completion_starts_default_while_next_admin_waits(self):
with tempfile.TemporaryDirectory() as temp_dir:
manager = QueueManager(temp_dir, logging.getLogger("idle-default-flow-test"))
manager.state.update({
"queue": [1, 2],
"current_admin_uid": 1,
"current_group": "晶蝶",
"current_group_run_id": "user-run",
"group_start_time": (datetime.now() - timedelta(seconds=200)).isoformat(),
"login_status": "logged_in",
"billing_uid": 1,
"billing_started_at": datetime.now().isoformat(),
"billing_last_at": datetime.now().isoformat(),
"has_user_finished_once": True,
})
manager._save()
system = QueueSystem.__new__(QueueSystem)
system.queue_mgr = manager
system.user_mgr = SimpleNamespace(
users={"1": {"uname": "first"}, "2": {"uname": "second"}},
get_points=lambda _uid: 10,
)
system.log_monitor = SimpleNamespace(set_current_group=MagicMock())
system.logger = logging.getLogger("idle-default-flow-test")
system.broadcast = AsyncMock()
system._start_default_group = AsyncMock()
await system._on_group_finished("晶蝶", "user-run")
self.assertEqual(manager.state["queue"], [2])
self.assertEqual(manager.state["current_admin_uid"], 2)
self.assertIsNone(manager.state["login_status"])
system._start_default_group.assert_awaited_once_with()
async def test_default_group_can_start_with_waiting_admin(self):
with tempfile.TemporaryDirectory() as temp_dir:
manager = QueueManager(temp_dir, logging.getLogger("idle-default-start-test"))
manager.state["has_user_finished_once"] = True
manager.join_queue(101)
original_window = manager.state["admin_window_end"]
system = QueueSystem.__new__(QueueSystem)
system.queue_mgr = manager
system.config = SimpleNamespace(default_group="薄荷")
system.user_mgr = SimpleNamespace(
users={"101": {"uname": "队首昵称"}},
_is_masked_uname=lambda name: (not name) or ("*" in name),
resolve_uname=AsyncMock(return_value=""),
)
system.runner = SimpleNamespace(
kill_bgi=AsyncMock(),
start_groups=AsyncMock(return_value=True),
)
system.log_monitor = SimpleNamespace(set_current_group=MagicMock())
system.stats_store = None
system.logger = logging.getLogger("idle-default-start-test")
system.broadcast = AsyncMock()
system._default_start_lock = asyncio.Lock()
await system._start_default_group()
self.assertTrue(manager.state["default_running"])
self.assertEqual(manager.state["current_group"], "薄荷")
self.assertEqual(manager.state["current_admin_uid"], 101)
self.assertEqual(manager.state["admin_window_end"], original_window)
self.assertIsNone(manager.state["billing_uid"])
system.runner.start_groups.assert_awaited_once_with(["薄荷"])
# 播报文案应使用昵称而非纯数字 UID
broadcast_texts = [str(call.args[0]) for call in system.broadcast.await_args_list]
self.assertTrue(any("队首昵称" in t for t in broadcast_texts))
self.assertFalse(any("队首101" in t for t in broadcast_texts))
if __name__ == "__main__":
unittest.main()