"""integrations/discovery.py 自动发现单元测试。 覆盖 discover_integration_packages() 的黑名单跳过、非包跳过、下划线前缀跳过、 ImportError 容错与非 ImportError 异常透传。 通过 monkeypatch 模拟 pkgutil.iter_modules 与 importlib.import_module 返回值, 避免触发真实集成包导入副作用(auth_plugins 等会向全局注册表写入不同实例导致冲突)。 """ from __future__ import annotations import logging from types import ModuleType import pytest from yuxi.external_systems.integrations import discovery # ─── 黑名单 ─────────────────────────────────────────────────────────────── @pytest.mark.unit def test_blacklist_includes_infrastructure_modules(): # Arrange / Act / Assert - 框架自身模块应在黑名单中 assert "registry" in discovery._BLACKLIST assert "schemas" in discovery._BLACKLIST assert "discovery" in discovery._BLACKLIST assert "operation_registry" in discovery._BLACKLIST @pytest.mark.unit def test_blacklist_excludes_real_integration_packages(): # Arrange / Act / Assert - 真实厂商集成包不应在黑名单中 assert "salesforce" not in discovery._BLACKLIST assert "hubspot" not in discovery._BLACKLIST assert "jira" not in discovery._BLACKLIST assert "slack" not in discovery._BLACKLIST # ─── discover_integration_packages ──────────────────────────────────────── @pytest.mark.unit def test_discoverIntegrationPackages_skips_non_package_modules(monkeypatch): # Arrange - 模拟 iter_modules 返回非包项 def _mock_iter_modules(_path): yield (None, "not_a_package", False) yield (None, "real_package", True) monkeypatch.setattr(discovery.pkgutil, "iter_modules", _mock_iter_modules) imported: list[str] = [] def _mock_import_module(name): imported.append(name) return ModuleType(name) monkeypatch.setattr(discovery.importlib, "import_module", _mock_import_module) # Act result = discovery.discover_integration_packages() # Assert - 非包项(ispkg=False)应被跳过 assert "yuxi.external_systems.integrations.not_a_package" not in imported assert "yuxi.external_systems.integrations.real_package" in imported assert "yuxi.external_systems.integrations.real_package" in result @pytest.mark.unit def test_discoverIntegrationPackages_skips_underscore_prefixed(monkeypatch): # Arrange def _mock_iter_modules(_path): yield (None, "_private", True) yield (None, "salesforce", True) monkeypatch.setattr(discovery.pkgutil, "iter_modules", _mock_iter_modules) imported: list[str] = [] def _mock_import_module(name): imported.append(name) return ModuleType(name) monkeypatch.setattr(discovery.importlib, "import_module", _mock_import_module) # Act discovery.discover_integration_packages() # Assert - 下划线前缀应被跳过 assert "yuxi.external_systems.integrations._private" not in imported assert "yuxi.external_systems.integrations.salesforce" in imported @pytest.mark.unit def test_discoverIntegrationPackages_skips_blacklisted_modules(monkeypatch): # Arrange def _mock_iter_modules(_path): yield (None, "registry", True) yield (None, "schemas", True) yield (None, "discovery", True) yield (None, "operation_registry", True) yield (None, "salesforce", True) monkeypatch.setattr(discovery.pkgutil, "iter_modules", _mock_iter_modules) imported: list[str] = [] def _mock_import_module(name): imported.append(name) return ModuleType(name) monkeypatch.setattr(discovery.importlib, "import_module", _mock_import_module) # Act discovery.discover_integration_packages() # Assert - 黑名单模块应被跳过 for bl in ("registry", "schemas", "discovery", "operation_registry"): assert f"yuxi.external_systems.integrations.{bl}" not in imported assert "yuxi.external_systems.integrations.salesforce" in imported @pytest.mark.unit def test_discoverIntegrationPackages_logs_warning_on_importError(monkeypatch, caplog): # Arrange def _mock_iter_modules(_path): yield (None, "broken_pkg", True) monkeypatch.setattr(discovery.pkgutil, "iter_modules", _mock_iter_modules) def _mock_import_module(name): raise ImportError(f"missing dependency for {name}") monkeypatch.setattr(discovery.importlib, "import_module", _mock_import_module) # Act with caplog.at_level(logging.WARNING): result = discovery.discover_integration_packages() # Assert - ImportError 应被捕获并记录警告,不向上抛出;失败包不计入返回 assert any("broken_pkg" in r.message and "加载失败" in r.message for r in caplog.records) assert result == [] @pytest.mark.unit def test_discoverIntegrationPackages_propagates_non_import_errors(monkeypatch): # Arrange def _mock_iter_modules(_path): yield (None, "bad_pkg", True) monkeypatch.setattr(discovery.pkgutil, "iter_modules", _mock_iter_modules) def _mock_import_module(name): raise RuntimeError("unexpected metadata error") monkeypatch.setattr(discovery.importlib, "import_module", _mock_import_module) # Act / Assert - 非 ImportError 应向上抛出 with pytest.raises(RuntimeError, match="unexpected metadata error"): discovery.discover_integration_packages() @pytest.mark.unit def test_discoverIntegrationPackages_returns_imported_module_names(monkeypatch): # Arrange def _mock_iter_modules(_path): yield (None, "pkg_a", True) yield (None, "pkg_b", True) yield (None, "pkg_c", False) # 非包,跳过 monkeypatch.setattr(discovery.pkgutil, "iter_modules", _mock_iter_modules) def _mock_import_module(name): return ModuleType(name) monkeypatch.setattr(discovery.importlib, "import_module", _mock_import_module) # Act result = discovery.discover_integration_packages() # Assert - 返回成功导入的模块名列表 assert result == [ "yuxi.external_systems.integrations.pkg_a", "yuxi.external_systems.integrations.pkg_b", ] @pytest.mark.unit def test_discoverIntegrationPackages_empty_when_all_skipped(monkeypatch): # Arrange - 全部被跳过 def _mock_iter_modules(_path): yield (None, "registry", True) yield (None, "_private", True) yield (None, "not_pkg", False) monkeypatch.setattr(discovery.pkgutil, "iter_modules", _mock_iter_modules) def _mock_import_module(name): raise AssertionError(f"不应导入 {name}") monkeypatch.setattr(discovery.importlib, "import_module", _mock_import_module) # Act result = discovery.discover_integration_packages() # Assert assert result == []