mirror of
https://github.com/jxxghp/MoviePilot.git
synced 2026-08-29 03:56:43 +08:00
136 lines
5.1 KiB
Python
136 lines
5.1 KiB
Python
import sys
|
|
import asyncio
|
|
import unittest
|
|
from types import ModuleType
|
|
from unittest.mock import patch
|
|
|
|
sys.modules.setdefault("qbittorrentapi", ModuleType("qbittorrentapi"))
|
|
setattr(sys.modules["qbittorrentapi"], "TorrentFilesList", list)
|
|
sys.modules.setdefault("transmission_rpc", ModuleType("transmission_rpc"))
|
|
setattr(sys.modules["transmission_rpc"], "File", object)
|
|
sys.modules.setdefault("psutil", ModuleType("psutil"))
|
|
|
|
from app.chain.message import MessageChain
|
|
from app.application.messaging.message import MessageQueueManager
|
|
from app.schemas.message import Message
|
|
from app.runtime.correlation import correlation_scope, get_correlation_id
|
|
from app.foundation.identity import (
|
|
SYSTEM_INTERNAL_USER_ID,
|
|
is_internal_user_id,
|
|
normalize_internal_user_id,
|
|
)
|
|
|
|
|
|
class TestSystemNotificationDispatch(unittest.TestCase):
|
|
def test_internal_userid_identity_helpers(self):
|
|
self.assertTrue(is_internal_user_id(SYSTEM_INTERNAL_USER_ID))
|
|
self.assertTrue(is_internal_user_id(" System "))
|
|
self.assertIsNone(normalize_internal_user_id(SYSTEM_INTERNAL_USER_ID))
|
|
self.assertEqual(normalize_internal_user_id("10001"), "10001")
|
|
|
|
def test_post_message_normalizes_internal_userid_before_queueing(self):
|
|
chain = MessageChain()
|
|
message = Message(
|
|
userid=SYSTEM_INTERNAL_USER_ID,
|
|
username="admin",
|
|
title="后台报告",
|
|
text="任务完成",
|
|
)
|
|
|
|
with patch("app.chain._messaging.MessageTemplateHelper.render", return_value=message), patch.object(
|
|
chain.messagehelper, "put"
|
|
), patch.object(chain.messageoper, "add"), patch.object(
|
|
chain.eventmanager, "send_event"
|
|
) as send_event, patch.object(
|
|
chain.messagequeue, "send_message"
|
|
) as send_message:
|
|
chain.post_message(message)
|
|
|
|
event_payload = send_event.call_args.kwargs["data"]
|
|
queued_message = send_message.call_args.kwargs["message"]
|
|
|
|
self.assertIsNone(event_payload["userid"])
|
|
self.assertIsNone(queued_message.userid)
|
|
self.assertFalse(send_message.call_args.kwargs["immediately"])
|
|
|
|
def test_send_direct_message_normalizes_internal_userid(self):
|
|
chain = MessageChain()
|
|
message = Message(
|
|
userid=SYSTEM_INTERNAL_USER_ID,
|
|
username="admin",
|
|
title="后台报告",
|
|
text="任务完成",
|
|
)
|
|
|
|
with patch.object(chain, "run_module") as run_module:
|
|
chain.send_direct_message(message)
|
|
|
|
sent_message = run_module.call_args.kwargs["message"]
|
|
self.assertIsNone(sent_message.userid)
|
|
|
|
def test_async_send_message_uses_executor_for_immediate_send(self):
|
|
"""异步立即发送不能在事件循环里直接执行同步渠道回调。"""
|
|
class _FakeLoop:
|
|
"""同步模拟 asyncio 的 executor 提交,并记录提交时的上下文。"""
|
|
|
|
def __init__(self, loop):
|
|
self.called = False
|
|
self.loop = loop
|
|
self.submission_correlation_id = None
|
|
|
|
def run_in_executor(self, _executor, func, *args):
|
|
"""立即执行 worker 调用并返回已完成 Future。"""
|
|
self.called = True
|
|
self.submission_correlation_id = get_correlation_id()
|
|
future = self.loop.create_future()
|
|
try:
|
|
func(*args)
|
|
except BaseException as error:
|
|
future.set_exception(error)
|
|
else:
|
|
future.set_result(None)
|
|
return future
|
|
|
|
async def _run():
|
|
manager = MessageQueueManager()
|
|
fake_loop = _FakeLoop(asyncio.get_running_loop())
|
|
observed = []
|
|
with patch(
|
|
"asyncio.get_running_loop",
|
|
return_value=fake_loop,
|
|
), patch.object(
|
|
manager,
|
|
"_send",
|
|
side_effect=lambda *_args, **_kwargs: observed.append(
|
|
get_correlation_id()
|
|
),
|
|
) as send:
|
|
with correlation_scope("message-request"):
|
|
await manager.async_send_message("payload", immediately=True)
|
|
self.assertTrue(fake_loop.called)
|
|
self.assertIsNone(fake_loop.submission_correlation_id)
|
|
self.assertEqual(observed, ["message-request"])
|
|
send.assert_called_once_with("payload")
|
|
|
|
asyncio.run(_run())
|
|
|
|
def test_async_send_message_preserves_call_context(self):
|
|
"""异步立即发送的同步渠道回调应保留当前请求关联 ID。"""
|
|
observed = []
|
|
|
|
async def _run():
|
|
manager = MessageQueueManager()
|
|
with patch.object(
|
|
manager,
|
|
"_send",
|
|
side_effect=lambda *_args, **_kwargs: observed.append(
|
|
get_correlation_id()
|
|
),
|
|
):
|
|
with correlation_scope("message-request"):
|
|
await manager.async_send_message("payload", immediately=True)
|
|
|
|
asyncio.run(_run())
|
|
|
|
self.assertEqual(observed, ["message-request"])
|