chore: remove unused imports and fix function name conflicts (#5764)

- Remove unused imports in anthropic.py, tmdbv3api/__init__.py, tv.py, test files
- Rename conflicting function names in subscribe.py and webhook.py
- Clean up unused re-exports in tmdbv3api/__init__.py (15 unused exports)
- Apply consistent formatting across API endpoints
This commit is contained in:
DDSRem
2026-05-13 18:59:03 +08:00
committed by GitHub
parent fcf6e14ac9
commit 5a585839ba
34 changed files with 2974 additions and 1838 deletions

View File

@@ -3,7 +3,6 @@ import asyncio
import json
import tempfile
import unittest
from pathlib import Path
from types import ModuleType, SimpleNamespace
from unittest.mock import ANY, MagicMock, patch
@@ -21,15 +20,20 @@ if "Pinyin2Hanzi" not in sys.modules:
from app.modules.feishu import FeishuModule
from app.modules.feishu.feishu import Feishu
from app.schemas import Notification
from app.schemas.message import ChannelCapability, ChannelCapabilityManager, MessageResponse
from app.schemas.message import (
ChannelCapability,
ChannelCapabilityManager,
MessageResponse,
)
from app.schemas.types import MessageChannel, NotificationType
class TestFeishu(unittest.TestCase):
@staticmethod
def _build_client(**kwargs) -> Feishu:
with patch.object(Feishu, "_build_api_client", return_value=MagicMock()), patch.object(
Feishu, "_start_ws_client"
with (
patch.object(Feishu, "_build_api_client", return_value=MagicMock()),
patch.object(Feishu, "_start_ws_client"),
):
return Feishu(
FEISHU_APP_ID="cli_test_app_id",
@@ -64,7 +68,21 @@ class TestFeishu(unittest.TestCase):
return response
@staticmethod
def _build_message_api(create_response=None, patch_response=None, reply_response=None, reaction_create_response=None, reaction_delete_response=None, card_create_response=None, card_settings_response=None, card_content_response=None, image_create_response=None, file_create_response=None, image_get_response=None, file_get_response=None, message_resource_response=None):
def _build_message_api(
create_response=None,
patch_response=None,
reply_response=None,
reaction_create_response=None,
reaction_delete_response=None,
card_create_response=None,
card_settings_response=None,
card_content_response=None,
image_create_response=None,
file_create_response=None,
image_get_response=None,
file_get_response=None,
message_resource_response=None,
):
message_api = SimpleNamespace(
create=MagicMock(return_value=create_response),
patch=MagicMock(return_value=patch_response),
@@ -111,7 +129,11 @@ class TestFeishu(unittest.TestCase):
return api_client, message_api
@staticmethod
def _resource_response(content: bytes, file_name: str = "resource.bin", content_type: str = "application/octet-stream"):
def _resource_response(
content: bytes,
file_name: str = "resource.bin",
content_type: str = "application/octet-stream",
):
response = MagicMock()
response.code = 0
response.file = MagicMock()
@@ -169,9 +191,11 @@ class TestFeishu(unittest.TestCase):
class _Builder:
def __getattr__(self, name):
if name.startswith("register_"):
def _register(handler):
registered.append(name)
return self
return _register
raise AttributeError(name)
@@ -181,7 +205,10 @@ class TestFeishu(unittest.TestCase):
client = self._build_client()
fake_builder = _Builder()
with patch("app.modules.feishu.feishu.lark.EventDispatcherHandler.builder", return_value=fake_builder):
with patch(
"app.modules.feishu.feishu.lark.EventDispatcherHandler.builder",
return_value=fake_builder,
):
handler = client._build_event_handler()
self.assertEqual(handler, "handler")
@@ -190,15 +217,20 @@ class TestFeishu(unittest.TestCase):
self.assertIn("register_p2_im_message_reaction_created_v1", registered)
self.assertIn("register_p2_im_message_reaction_deleted_v1", registered)
self.assertIn("register_p2_im_message_recalled_v1", registered)
self.assertIn("register_p2_im_chat_access_event_bot_p2p_chat_entered_v1", registered)
self.assertIn(
"register_p2_im_chat_access_event_bot_p2p_chat_entered_v1", registered
)
self.assertIn("register_p2_card_action_trigger", registered)
def test_parse_message_blocks_non_admin_command(self):
client = self._build_client(FEISHU_ADMINS="ou_admin")
with patch("app.modules.feishu.feishu.UserOper.get_name", return_value=None), patch.object(
client, "send_text", return_value={"success": True}
) as send_text:
with (
patch("app.modules.feishu.feishu.UserOper.get_name", return_value=None),
patch.object(
client, "send_text", return_value={"success": True}
) as send_text,
):
result = client.parse_message(
{
"type": "message",
@@ -223,7 +255,10 @@ class TestFeishu(unittest.TestCase):
def test_parse_message_maps_feishu_ids_to_moviepilot_username(self):
client = self._build_client()
with patch("app.modules.feishu.feishu.UserOper.get_name", return_value="moviepilot-user") as get_name:
with patch(
"app.modules.feishu.feishu.UserOper.get_name",
return_value="moviepilot-user",
) as get_name:
result = client.parse_message(
{
"type": "message",
@@ -317,7 +352,9 @@ class TestFeishu(unittest.TestCase):
self.assertEqual(image_element["img_key"], "img_v2_remote")
self.assertEqual(content["body"]["elements"][1]["margin"], "12px 12px 0px 12px")
self.assertEqual(content["body"]["elements"][2]["margin"], "4px 12px 12px 12px")
self.assertEqual(content["body"]["elements"][-1]["margin"], "0px 12px 12px 12px")
self.assertEqual(
content["body"]["elements"][-1]["margin"], "0px 12px 12px 12px"
)
self.assertEqual(content["body"]["elements"][-1]["tag"], "column_set")
def test_send_notification_supports_user_id_target(self):
@@ -390,25 +427,33 @@ class TestFeishu(unittest.TestCase):
reaction_delete_response=self._success_response(),
)
reaction_id = client.add_message_reaction("om_origin", Feishu.PROCESSING_REACTION_EMOJI)
reaction_id = client.add_message_reaction(
"om_origin", Feishu.PROCESSING_REACTION_EMOJI
)
deleted = client.delete_message_reaction("om_origin", "reaction_1")
self.assertEqual(reaction_id, "reaction_1")
self.assertTrue(deleted)
create_request = client._api_client.im.v1.message_reaction.create.call_args.args[0]
create_request = (
client._api_client.im.v1.message_reaction.create.call_args.args[0]
)
self.assertEqual(create_request.message_id, "om_origin")
self.assertEqual(
create_request.request_body.reaction_type.emoji_type,
Feishu.PROCESSING_REACTION_EMOJI,
)
delete_request = client._api_client.im.v1.message_reaction.delete.call_args.args[0]
delete_request = (
client._api_client.im.v1.message_reaction.delete.call_args.args[0]
)
self.assertEqual(delete_request.message_id, "om_origin")
self.assertEqual(delete_request.reaction_id, "reaction_1")
def test_send_notification_uses_streaming_card_for_agent_text(self):
client = self._build_client()
client._api_client, message_api = self._build_message_api(
create_response=self._success_response(message_id="om_stream", chat_id="oc_stream"),
create_response=self._success_response(
message_id="om_stream", chat_id="oc_stream"
),
card_create_response=self._card_create_success_response("card_stream"),
)
@@ -422,21 +467,31 @@ class TestFeishu(unittest.TestCase):
)
self.assertTrue(result["success"])
self.assertEqual(result["metadata"]["feishu_streaming"]["card_id"], "card_stream")
self.assertEqual(
result["metadata"]["feishu_streaming"]["card_id"], "card_stream"
)
self.assertEqual(result["metadata"]["feishu_streaming"]["sequence"], 0)
card_request = client._api_client.cardkit.v1.card.create.call_args.args[0]
self.assertEqual(card_request.request_body.type, "card_json")
card_payload = json.loads(card_request.request_body.data)
self.assertTrue(card_payload["config"]["streaming_mode"])
self.assertEqual(card_payload["body"]["elements"][-1]["element_id"], Feishu.STREAM_CARD_BODY_ELEMENT_ID)
self.assertEqual(
card_payload["body"]["elements"][-1]["element_id"],
Feishu.STREAM_CARD_BODY_ELEMENT_ID,
)
message_request = message_api.create.call_args.args[0]
self.assertEqual(message_request.request_body.msg_type, "interactive")
self.assertEqual(json.loads(message_request.request_body.content)["data"]["card_id"], "card_stream")
self.assertEqual(
json.loads(message_request.request_body.content)["data"]["card_id"],
"card_stream",
)
def test_send_notification_replies_with_streaming_card_for_agent_text(self):
client = self._build_client()
client._api_client, message_api = self._build_message_api(
reply_response=self._success_response(message_id="om_reply", chat_id="oc_stream"),
reply_response=self._success_response(
message_id="om_reply", chat_id="oc_stream"
),
card_create_response=self._card_create_success_response("card_stream"),
)
@@ -455,7 +510,10 @@ class TestFeishu(unittest.TestCase):
reply_request = message_api.reply.call_args.args[0]
self.assertEqual(reply_request.message_id, "om_origin")
self.assertEqual(reply_request.request_body.msg_type, "interactive")
self.assertEqual(json.loads(reply_request.request_body.content)["data"]["card_id"], "card_stream")
self.assertEqual(
json.loads(reply_request.request_body.content)["data"]["card_id"],
"card_stream",
)
self.assertEqual(result["metadata"]["feishu_streaming"]["sequence"], 0)
def test_edit_replied_streaming_card_uses_first_increment_sequence(self):
@@ -479,7 +537,9 @@ class TestFeishu(unittest.TestCase):
self.assertTrue(success)
message_api.patch.assert_not_called()
content_request = client._api_client.cardkit.v1.card_element.content.call_args.args[0]
content_request = (
client._api_client.cardkit.v1.card_element.content.call_args.args[0]
)
self.assertEqual(content_request.request_body.sequence, 1)
def test_edit_message_uses_cardkit_content_for_streaming_card(self):
@@ -504,7 +564,9 @@ class TestFeishu(unittest.TestCase):
self.assertTrue(success)
client._api_client.cardkit.v1.card_element.content.assert_called_once()
message_api.patch.assert_not_called()
content_request = client._api_client.cardkit.v1.card_element.content.call_args.args[0]
content_request = (
client._api_client.cardkit.v1.card_element.content.call_args.args[0]
)
self.assertEqual(content_request.card_id, "card_stream")
self.assertEqual(content_request.element_id, Feishu.STREAM_CARD_BODY_ELEMENT_ID)
self.assertEqual(content_request.request_body.sequence, 1)
@@ -545,7 +607,12 @@ class TestFeishu(unittest.TestCase):
{
"type": "message",
"text": "",
"files": [{"ref": "feishu://file/file_key/report.pdf", "name": "report.pdf"}],
"files": [
{
"ref": "feishu://file/file_key/report.pdf",
"name": "report.pdf",
}
],
"message_id": "om_file",
"chat_id": "oc_chat",
"sender": {
@@ -567,14 +634,18 @@ class TestFeishu(unittest.TestCase):
message_type="image",
content=json.dumps({"image_key": "img_v2_evt"}),
)
sender = SimpleNamespace(sender_id=SimpleNamespace(open_id="ou_user_evt", user_id=None))
sender = SimpleNamespace(
sender_id=SimpleNamespace(open_id="ou_user_evt", user_id=None)
)
event = SimpleNamespace(sender=sender, message=message)
with patch.object(client, "_forward_to_message_chain") as forward:
client._on_message(SimpleNamespace(event=event))
payload = forward.call_args.args[0]
self.assertEqual(payload["images"][0]["ref"], "feishu://image/om_img_evt/img_v2_evt")
self.assertEqual(
payload["images"][0]["ref"], "feishu://image/om_img_evt/img_v2_evt"
)
def test_on_message_wraps_feishu_audio_ref_with_message_id(self):
client = self._build_client()
@@ -583,16 +654,23 @@ class TestFeishu(unittest.TestCase):
chat_id="oc_chat_evt",
chat_type="p2p",
message_type="audio",
content=json.dumps({"file_key": "file_audio_evt", "file_name": "voice.opus"}),
content=json.dumps(
{"file_key": "file_audio_evt", "file_name": "voice.opus"}
),
)
sender = SimpleNamespace(
sender_id=SimpleNamespace(open_id="ou_user_evt", user_id=None)
)
sender = SimpleNamespace(sender_id=SimpleNamespace(open_id="ou_user_evt", user_id=None))
event = SimpleNamespace(sender=sender, message=message)
with patch.object(client, "_forward_to_message_chain") as forward:
client._on_message(SimpleNamespace(event=event))
payload = forward.call_args.args[0]
self.assertEqual(payload["audio_refs"], ["feishu://file/om_audio_evt/file_audio_evt/voice.opus"])
self.assertEqual(
payload["audio_refs"],
["feishu://file/om_audio_evt/file_audio_evt/voice.opus"],
)
def test_feishu_channel_capabilities_enable_images_and_files(self):
self.assertTrue(
@@ -650,9 +728,12 @@ class TestFeishu(unittest.TestCase):
file_create_response=file_upload_response,
)
with tempfile.NamedTemporaryFile(suffix=".txt") as fp, patch.object(
client, "send_text", return_value={"success": True}
) as send_text:
with (
tempfile.NamedTemporaryFile(suffix=".txt") as fp,
patch.object(
client, "send_text", return_value={"success": True}
) as send_text,
):
fp.write(b"text-bytes")
fp.flush()
result = client.send_file(
@@ -666,7 +747,9 @@ class TestFeishu(unittest.TestCase):
client._api_client.im.v1.file.create.assert_called_once()
request = message_api.create.call_args.args[0]
self.assertEqual(request.request_body.msg_type, "file")
self.assertEqual(json.loads(request.request_body.content)["file_key"], "file_doc")
self.assertEqual(
json.loads(request.request_body.content)["file_key"], "file_doc"
)
send_text.assert_called_once()
def test_send_voice_uploads_audio_file_and_optionally_sends_caption(self):
@@ -682,7 +765,9 @@ class TestFeishu(unittest.TestCase):
with tempfile.NamedTemporaryFile(suffix=".opus") as fp:
fp.write(b"opus-bytes")
fp.flush()
with patch.object(client, "send_text", return_value={"success": True}) as send_text:
with patch.object(
client, "send_text", return_value={"success": True}
) as send_text:
result = client.send_voice(
voice_path=fp.name,
userid="ou_user_8",
@@ -692,20 +777,30 @@ class TestFeishu(unittest.TestCase):
self.assertTrue(result["success"])
request = message_api.create.call_args.args[0]
self.assertEqual(request.request_body.msg_type, "audio")
self.assertEqual(json.loads(request.request_body.content)["file_key"], "file_audio")
self.assertEqual(
json.loads(request.request_body.content)["file_key"], "file_audio"
)
send_text.assert_called_once()
def test_download_helpers_return_bytes_and_data_url(self):
client = self._build_client()
client._api_client, _ = self._build_message_api(
image_get_response=self._resource_response(b"image-bytes", file_name="poster.png", content_type="image/png"),
file_get_response=self._resource_response(b"file-bytes", file_name="report.txt", content_type="text/plain"),
message_resource_response=self._resource_response(b"resource-bytes", file_name="voice.opus", content_type="audio/ogg"),
image_get_response=self._resource_response(
b"image-bytes", file_name="poster.png", content_type="image/png"
),
file_get_response=self._resource_response(
b"file-bytes", file_name="report.txt", content_type="text/plain"
),
message_resource_response=self._resource_response(
b"resource-bytes", file_name="voice.opus", content_type="audio/ogg"
),
)
image_download = client.download_image_bytes("img_v2_test")
file_download = client.download_file_bytes("file_test")
resource_download = client.download_message_resource_bytes("om_test", "file_test", "audio")
resource_download = client.download_message_resource_bytes(
"om_test", "file_test", "audio"
)
self.assertEqual(image_download[0], b"image-bytes")
self.assertEqual(file_download[0], b"file-bytes")
@@ -722,9 +817,11 @@ class TestFeishu(unittest.TestCase):
"chat_id": "oc_789",
}
with patch.object(module, "get_configs", return_value={"feishu-main": conf}), patch.object(
module, "check_message", return_value=True
), patch.object(module, "get_instance", return_value=client):
with (
patch.object(module, "get_configs", return_value={"feishu-main": conf}),
patch.object(module, "check_message", return_value=True),
patch.object(module, "get_instance", return_value=client),
):
response = module.send_direct_message(
Notification(
targets={
@@ -757,14 +854,25 @@ class TestFeishu(unittest.TestCase):
created_loops.append(loop)
return loop
with patch("app.modules.feishu.feishu.lark_ws_client_module.loop", original_loop), patch(
"app.modules.feishu.feishu.lark_ws_client_module._select",
new=MagicMock(return_value=None),
), patch("app.modules.feishu.feishu.asyncio.new_event_loop", side_effect=_new_loop), patch(
"app.modules.feishu.feishu.lark.ws.Client", return_value=fake_ws_client
), patch.object(
fake_ws_client, "start", side_effect=lambda: None
) as mock_start:
with (
patch(
"app.modules.feishu.feishu.lark_ws_client_module.loop", original_loop
),
patch(
"app.modules.feishu.feishu.lark_ws_client_module._select",
new=MagicMock(return_value=None),
),
patch(
"app.modules.feishu.feishu.asyncio.new_event_loop",
side_effect=_new_loop,
),
patch(
"app.modules.feishu.feishu.lark.ws.Client", return_value=fake_ws_client
),
patch.object(
fake_ws_client, "start", side_effect=lambda: None
) as mock_start,
):
client._run_ws_client()
self.assertIsNone(client._ws_loop)
@@ -784,7 +892,10 @@ class TestFeishu(unittest.TestCase):
future = MagicMock()
future.result.return_value = None
with patch("app.modules.feishu.feishu.asyncio.run_coroutine_threadsafe", return_value=future) as runner:
with patch(
"app.modules.feishu.feishu.asyncio.run_coroutine_threadsafe",
return_value=future,
) as runner:
client.stop()
runner.assert_called_once()
@@ -795,13 +906,24 @@ class TestFeishu(unittest.TestCase):
client = MagicMock()
client.download_image_bytes.return_value = (b"image", "poster.png", "image/png")
client.download_file_bytes.return_value = (b"file", "note.txt", "text/plain")
client.download_message_resource_bytes.return_value = (b"image", "poster.png", "image/png")
client.download_message_resource_bytes.return_value = (
b"image",
"poster.png",
"image/png",
)
with patch.object(module, "get_config", return_value=SimpleNamespace(name="feishu-main")), patch.object(
module, "get_instance", return_value=client
with (
patch.object(
module, "get_config", return_value=SimpleNamespace(name="feishu-main")
),
patch.object(module, "get_instance", return_value=client),
):
data_url = module.download_feishu_image_to_data_url("feishu://image/om_msg/img_v2_xxx", "feishu-main")
file_bytes = module.download_feishu_file_bytes("feishu://file/file_xxx/note.txt", "feishu-main")
data_url = module.download_feishu_image_to_data_url(
"feishu://image/om_msg/img_v2_xxx", "feishu-main"
)
file_bytes = module.download_feishu_file_bytes(
"feishu://file/file_xxx/note.txt", "feishu-main"
)
audio_bytes = module.download_feishu_file_bytes(
"feishu://file/om_audio/file_audio/voice.opus",
"feishu-main",
@@ -827,11 +949,18 @@ class TestFeishu(unittest.TestCase):
client.add_message_reaction.return_value = "reaction_2"
client.delete_message_reaction.return_value = True
with patch.object(module, "get_config", return_value=SimpleNamespace(name="feishu-main")), patch.object(
module, "get_instance", return_value=client
with (
patch.object(
module, "get_config", return_value=SimpleNamespace(name="feishu-main")
),
patch.object(module, "get_instance", return_value=client),
):
reaction_id = module.add_feishu_message_reaction("om_x", "GLANCE", "feishu-main")
deleted = module.delete_feishu_message_reaction("om_x", "reaction_2", "feishu-main")
reaction_id = module.add_feishu_message_reaction(
"om_x", "GLANCE", "feishu-main"
)
deleted = module.delete_feishu_message_reaction(
"om_x", "reaction_2", "feishu-main"
)
self.assertEqual(reaction_id, "reaction_2")
self.assertTrue(deleted)
@@ -842,8 +971,11 @@ class TestFeishu(unittest.TestCase):
client = MagicMock()
client.close_streaming_card.return_value = True
with patch.object(module, "get_config", return_value=SimpleNamespace(name="feishu-main")), patch.object(
module, "get_instance", return_value=client
with (
patch.object(
module, "get_config", return_value=SimpleNamespace(name="feishu-main")
),
patch.object(module, "get_instance", return_value=client),
):
success = module.finalize_message(
MessageResponse(
@@ -862,18 +994,35 @@ class TestFeishu(unittest.TestCase):
)
self.assertTrue(success)
client.close_streaming_card.assert_called_once_with(card_id="card_stream", sequence=3)
client.close_streaming_card.assert_called_once_with(
card_id="card_stream", sequence=3
)
def test_module_post_message_prefers_file_and_voice_paths(self):
module = FeishuModule()
conf = SimpleNamespace(name="feishu-main")
client = MagicMock()
with patch.object(module, "get_configs", return_value={"feishu-main": conf}), patch.object(
module, "check_message", return_value=True
), patch.object(module, "get_instance", return_value=client):
module.post_message(Notification(file_path="/tmp/demo.txt", text="说明", title="标题", userid="ou_user"))
module.post_message(Notification(voice_path="/tmp/demo.opus", voice_caption="语音说明", userid="ou_user"))
with (
patch.object(module, "get_configs", return_value={"feishu-main": conf}),
patch.object(module, "check_message", return_value=True),
patch.object(module, "get_instance", return_value=client),
):
module.post_message(
Notification(
file_path="/tmp/demo.txt",
text="说明",
title="标题",
userid="ou_user",
)
)
module.post_message(
Notification(
voice_path="/tmp/demo.opus",
voice_caption="语音说明",
userid="ou_user",
)
)
client.send_file.assert_called_once()
client.send_voice.assert_called_once()
@@ -883,9 +1032,11 @@ class TestFeishu(unittest.TestCase):
conf = SimpleNamespace(name="feishu-main")
client = MagicMock()
with patch.object(module, "get_configs", return_value={"feishu-main": conf}), patch.object(
module, "check_message", return_value=True
), patch.object(module, "get_instance", return_value=client):
with (
patch.object(module, "get_configs", return_value={"feishu-main": conf}),
patch.object(module, "check_message", return_value=True),
patch.object(module, "get_instance", return_value=client),
):
module.post_message(
Notification(
file_path="/tmp/demo.txt",
@@ -917,9 +1068,11 @@ class TestFeishu(unittest.TestCase):
}
client.send_file.return_value = {"success": True, "message_id": "om_file"}
with patch.object(module, "get_configs", return_value={"feishu-main": conf}), patch.object(
module, "check_message", return_value=True
), patch.object(module, "get_instance", return_value=client):
with (
patch.object(module, "get_configs", return_value={"feishu-main": conf}),
patch.object(module, "check_message", return_value=True),
patch.object(module, "get_instance", return_value=client),
):
response = module.send_direct_message(
Notification(
channel=MessageChannel.Feishu,
@@ -943,9 +1096,11 @@ class TestFeishu(unittest.TestCase):
conf = SimpleNamespace(name="feishu-main")
client = MagicMock()
with patch.object(module, "get_configs", return_value={"feishu-main": conf}), patch.object(
module, "check_message", return_value=True
), patch.object(module, "get_instance", return_value=client):
with (
patch.object(module, "get_configs", return_value={"feishu-main": conf}),
patch.object(module, "check_message", return_value=True),
patch.object(module, "get_instance", return_value=client),
):
module.post_message(
Notification(
title="标题",

View File

@@ -3,7 +3,7 @@ import sys
import types
import unittest
from pathlib import Path
from unittest.mock import call, patch
from unittest.mock import patch
def _load_jellyfin_module():
@@ -58,8 +58,12 @@ def _load_jellyfin_module():
return urljoin(host, path)
log_module.logger = _Logger()
config_module.settings = types.SimpleNamespace(SUPERUSER="admin", USER_AGENT="MoviePilot")
schemas_module.MediaType = types.SimpleNamespace(MOVIE=types.SimpleNamespace(value="movie"))
config_module.settings = types.SimpleNamespace(
SUPERUSER="admin", USER_AGENT="MoviePilot"
)
schemas_module.MediaType = types.SimpleNamespace(
MOVIE=types.SimpleNamespace(value="movie")
)
schemas_module.MediaServerItem = object
schemas_module.MediaServerLibrary = object
schemas_module.Statistic = object
@@ -90,7 +94,13 @@ def _load_jellyfin_module():
for stub_module in stub_modules.values():
stub_module._jellyfin_test_stub = True
jellyfin_path = Path(__file__).resolve().parents[1] / "app" / "modules" / "jellyfin" / "jellyfin.py"
jellyfin_path = (
Path(__file__).resolve().parents[1]
/ "app"
/ "modules"
/ "jellyfin"
/ "jellyfin.py"
)
spec = importlib.util.spec_from_file_location(module_name, jellyfin_path)
module = importlib.util.module_from_spec(spec)
assert spec and spec.loader
@@ -114,9 +124,15 @@ class _FakeResponse:
class JellyfinUserResolutionTest(unittest.TestCase):
def test_loader_does_not_leave_stub_modules_in_sys_modules(self):
self.assertNotIn("_test_jellyfin_module", sys.modules)
self.assertFalse(getattr(sys.modules.get("app.log"), "_jellyfin_test_stub", False))
self.assertFalse(getattr(sys.modules.get("app.core.config"), "_jellyfin_test_stub", False))
self.assertFalse(getattr(sys.modules.get("app.utils.http"), "_jellyfin_test_stub", False))
self.assertFalse(
getattr(sys.modules.get("app.log"), "_jellyfin_test_stub", False)
)
self.assertFalse(
getattr(sys.modules.get("app.core.config"), "_jellyfin_test_stub", False)
)
self.assertFalse(
getattr(sys.modules.get("app.utils.http"), "_jellyfin_test_stub", False)
)
def _build_client(self) -> Jellyfin:
client = Jellyfin.__new__(Jellyfin)
@@ -134,9 +150,10 @@ class JellyfinUserResolutionTest(unittest.TestCase):
{"Id": "alice-id", "Name": "alice", "Policy": {"IsAdministrator": False}},
]
with patch.object(jellyfin_module, "RequestUtils") as request_utils_cls, patch.object(
jellyfin_module.logger, "warning"
) as warning_mock:
with (
patch.object(jellyfin_module, "RequestUtils") as request_utils_cls,
patch.object(jellyfin_module.logger, "warning") as warning_mock,
):
request_utils_cls.return_value.get_res.return_value = _FakeResponse(payload)
user_id = client.get_user("alice")
@@ -150,7 +167,10 @@ class JellyfinUserResolutionTest(unittest.TestCase):
{
"Id": "visible-admin-id",
"Name": "visible",
"Policy": {"IsAdministrator": True, "EnabledFolders": ["lib-1", "lib-2", "lib-3"]},
"Policy": {
"IsAdministrator": True,
"EnabledFolders": ["lib-1", "lib-2", "lib-3"],
},
},
{
"Id": "full-admin-id",
@@ -177,14 +197,18 @@ class JellyfinUserResolutionTest(unittest.TestCase):
{
"Id": "large-admin-id",
"Name": "large",
"Policy": {"IsAdministrator": True, "EnabledFolders": ["lib-1", "lib-2", "lib-3"]},
"Policy": {
"IsAdministrator": True,
"EnabledFolders": ["lib-1", "lib-2", "lib-3"],
},
},
{"Id": "user-id", "Name": "normal", "Policy": {"IsAdministrator": False}},
]
with patch.object(jellyfin_module, "RequestUtils") as request_utils_cls, patch.object(
jellyfin_module.logger, "warning"
) as warning_mock:
with (
patch.object(jellyfin_module, "RequestUtils") as request_utils_cls,
patch.object(jellyfin_module.logger, "warning") as warning_mock,
):
request_utils_cls.return_value.get_res.return_value = _FakeResponse(payload)
user_id = client.get_user("admin")
@@ -193,7 +217,9 @@ class JellyfinUserResolutionTest(unittest.TestCase):
self.assertGreaterEqual(warning_mock.call_count, 2)
warning_messages = [
call.args[0] for call in warning_mock.call_args_list if call.args and isinstance(call.args[0], str)
call.args[0]
for call in warning_mock.call_args_list
if call.args and isinstance(call.args[0], str)
]
self.assertTrue(any("超级管理员" in message for message in warning_messages))
self.assertTrue(
@@ -205,7 +231,12 @@ class JellyfinUserResolutionTest(unittest.TestCase):
for message in warning_messages
)
)
self.assertTrue(any(("回退" in message) or ("fallback" in message.lower()) for message in warning_messages))
self.assertTrue(
any(
("回退" in message) or ("fallback" in message.lower())
for message in warning_messages
)
)
def test_get_jellyfin_librarys_returns_empty_when_user_missing(self):
client = self._build_client()
@@ -223,7 +254,9 @@ class JellyfinUserResolutionTest(unittest.TestCase):
client.user = "user-id"
with patch.object(jellyfin_module, "RequestUtils") as request_utils_cls:
request_utils_cls.return_value.get_res.return_value = _FakeResponse({"Items": []})
request_utils_cls.return_value.get_res.return_value = _FakeResponse(
{"Items": []}
)
libraries = client._Jellyfin__get_jellyfin_librarys()