"""End-to-end integration tests for NebulaShell plugin system""" import os import sys import tempfile import json import shutil import pytest from pathlib import Path def _create_dummy_plugin(store_dir: str, name: str, dependencies: list = None, extra: str = ""): plugin_dir = Path(store_dir) / "NebulaShell" / name plugin_dir.mkdir(parents=True, exist_ok=True) manifest = { "metadata": {"name": name, "version": "1.0.0", "description": f"{name} plugin", "author": "test"}, "config": {"enabled": True, "args": {}}, "dependencies": dependencies or [], "permissions": ["*"], } (plugin_dir / "manifest.json").write_text(json.dumps(manifest, indent=2), encoding="utf-8") main_code = f"""class {name.capitalize().replace('-', '')}: name = "{name}" version = "1.0.0" description = "{name} plugin" def init(self, deps=None): pass def start(self): pass def stop(self): pass {extra} def New(): return {name.capitalize().replace('-', '')}() """ (plugin_dir / "main.py").write_text(main_code, encoding="utf-8") class TestIntegration: @pytest.fixture def temp_store(self): tmp = tempfile.mkdtemp() store = Path(tmp) / "store" store.mkdir() (store / "NebulaShell").mkdir() yield str(store) shutil.rmtree(tmp) def test_plugin_manager_create(self): from oss.core.manager import PluginManager pm = PluginManager() assert pm is not None assert pm.plugins == {} def test_load_single_plugin(self, temp_store): _create_dummy_plugin(temp_store, "hello-world") from oss.core.manager import PluginManager pm = PluginManager() pm.load(Path(temp_store) / "NebulaShell" / "hello-world") assert "hello-world" in pm.plugins def test_load_plugins_with_dependencies(self, temp_store): _create_dummy_plugin(temp_store, "base") _create_dummy_plugin(temp_store, "dependent", dependencies=["base"]) from oss.core.manager import PluginManager pm = PluginManager() pm.load(Path(temp_store) / "NebulaShell" / "base") pm.load(Path(temp_store) / "NebulaShell" / "dependent") pm._sort_by_dependencies() assert "base" in pm.plugins assert "dependent" in pm.plugins def test_init_and_start_all(self, temp_store): _create_dummy_plugin(temp_store, "test-me", extra=""" _started = False def is_started(self): return self._started def start(self): self._started = True """) from oss.core.manager import PluginManager pm = PluginManager() pm.load(Path(temp_store) / "NebulaShell" / "test-me") pm.init_and_start_all() instance = pm.plugins["test-me"]["instance"] assert instance.is_started() is True def test_load_all_from_dir(self, temp_store): _create_dummy_plugin(temp_store, "alpha") _create_dummy_plugin(temp_store, "beta") from oss.core.manager import PluginManager from oss.config import init_config init_config() pm = PluginManager() pm._load_plugins_from_dir(Path(temp_store)) assert "alpha" in pm.plugins assert "beta" in pm.plugins def test_stop_all(self, temp_store): _create_dummy_plugin(temp_store, "will-stop", extra=""" _stopped = False def is_stopped(self): return self._stopped def stop(self): self._stopped = True """) from oss.core.manager import PluginManager pm = PluginManager() pm.load(Path(temp_store) / "NebulaShell" / "will-stop") pm.stop_all() instance = pm.plugins["will-stop"]["instance"] assert instance.is_stopped() is True def test_plugin_manager_status(self, temp_store): _create_dummy_plugin(temp_store, "status-test") from oss.core.manager import PluginManager pm = PluginManager() pm.load(Path(temp_store) / "NebulaShell" / "status-test") status = pm.get_status() assert status["plugins"]["total"] == 1 def test_dependency_resolver(self): from oss.core.deps import DependencyResolver dr = DependencyResolver() dr.add_dependency("a", ["b"]) dr.add_dependency("b", ["c"]) dr.add_dependency("c", []) order = dr.resolve() assert order.index("c") < order.index("b") < order.index("a") def test_plugin_info(self): from oss.core.manager import PluginInfo info = PluginInfo() info.name = "test" assert info.name == "test" def test_plugin_proxy_permission(self): from oss.core.manager import PluginInfo from oss.core.security import PluginProxy, PluginPermissionError proxy = PluginProxy("caller", object(), ["allowed"], {"allowed": {"instance": object()}}) assert proxy.get_plugin("allowed") is not None with pytest.raises(PluginPermissionError): proxy.get_plugin("not-allowed") def test_data_store_basic(self): from oss.core.datastore import DataStore import tempfile ds = DataStore() orig = ds._base_dir tmp = Path(tempfile.mkdtemp()) ds._base_dir = tmp / "data" ds._base_dir.mkdir(parents=True, exist_ok=True) assert ds.save("test-plugin", "key", {"value": 42}) is True loaded = ds.load("test-plugin", "key") assert loaded == {"value": 42} ds.delete("test-plugin", "key") assert ds.load("test-plugin", "key") is None shutil.rmtree(tmp, ignore_errors=True) def test_get_status_summary(self, temp_store): _create_dummy_plugin(temp_store, "stat-p") from oss.core.manager import PluginManager pm = PluginManager() pm.load(Path(temp_store) / "NebulaShell" / "stat-p") s = pm.get_status() assert isinstance(s, dict) assert "plugins" in s def test_capability_registry(self): from oss.core.manager import CapabilityRegistry cr = CapabilityRegistry() cr.register_provider("http", "a", object()) assert cr.has_capability("http") is True assert cr.get_provider("http") is not None def test_get_ordered_plugins(self, temp_store): _create_dummy_plugin(temp_store, "first") _create_dummy_plugin(temp_store, "second") from oss.core.manager import PluginManager pm = PluginManager() pm.load(Path(temp_store) / "NebulaShell" / "first") pm.load(Path(temp_store) / "NebulaShell" / "second") ordered = pm._get_ordered_plugins() assert "first" in ordered assert "second" in ordered if __name__ == '__main__': pytest.main([__file__, '-v'])