from __future__ import annotations from types import ModuleType from unittest.mock import MagicMock, patch import pytest from yuxi.channel.plugins.loader import load_plugins class TestLoadPlugins: def test_load_plugins_missing_package_returns_empty(self): with patch("yuxi.channel.plugins.loader.logger") as mock_logger: result = load_plugins("nonexistent.package") assert result == [] mock_logger.warning.assert_called_once_with( "Channel plugin package '%s' not found, skipping load", "nonexistent.package" ) def test_loads_modules_and_skips_packages(self): fake_package = ModuleType("fake.extensions") fake_package.__path__ = ["/fake/path"] imported_modules = [] def fake_import_module(name: str) -> ModuleType: imported_modules.append(name) if name == "fake.extensions": return fake_package return ModuleType(name) fake_modules = [ (MagicMock(), "fake.extensions.wecom", False), (MagicMock(), "fake.extensions.feishu", False), (MagicMock(), "fake.extensions.subpackage", True), ] with patch("yuxi.channel.plugins.loader.importlib.import_module", side_effect=fake_import_module): with patch("yuxi.channel.plugins.loader.pkgutil.iter_modules", return_value=fake_modules): loaded = load_plugins("fake.extensions") assert loaded == ["fake.extensions.wecom", "fake.extensions.feishu"] assert "fake.extensions.subpackage" not in loaded def test_returns_empty_list_when_no_modules(self): fake_package = ModuleType("fake.extensions") fake_package.__path__ = ["/fake/path"] with patch("yuxi.channel.plugins.loader.importlib.import_module", return_value=fake_package): with patch("yuxi.channel.plugins.loader.pkgutil.iter_modules", return_value=[]): assert load_plugins("fake.extensions") == [] def test_propagates_import_error(self): fake_package = ModuleType("fake.extensions") fake_package.__path__ = ["/fake/path"] fake_modules = [(MagicMock(), "fake.extensions.broken", False)] def fake_import_module(name: str) -> ModuleType: if name == "fake.extensions": return fake_package raise ImportError("broken module") with patch("yuxi.channel.plugins.loader.importlib.import_module", side_effect=fake_import_module): with patch("yuxi.channel.plugins.loader.pkgutil.iter_modules", return_value=fake_modules): with pytest.raises(ImportError, match="broken module"): load_plugins("fake.extensions") def test_uses_default_package_name(self): fake_package = ModuleType("yuxi.channel.extensions") fake_package.__path__ = ["/fake/path"] imported = [] def fake_import_module(name: str) -> ModuleType: imported.append(name) if name == "yuxi.channel.extensions": return fake_package return ModuleType(name) fake_modules = [(MagicMock(), "yuxi.channel.extensions.dummy", False)] with patch("yuxi.channel.plugins.loader.importlib.import_module", side_effect=fake_import_module): with patch("yuxi.channel.plugins.loader.pkgutil.iter_modules", return_value=fake_modules): loaded = load_plugins() assert loaded == ["yuxi.channel.extensions.dummy"]