重大重构:引擎模块拆分 + P0插件实现 + 55个Bug修复
核心变更: - engine.py(1781行)拆分为8个独立模块: lifecycle/security/deps/ datastore/pl_injector/watcher/signature/manager - 新增plugin-bridge: 事件总线 + 服务注册 + RPC通信 - 新增i18n: 国际化/多语言翻译支持 - 新增plugin-storage: 插件键值/文件存储 - 新增ws-api: WebSocket实时通信(pub/sub + 自定义处理器) - nodejs-adapter统一为Plugin ABC模式 Bug修复: - 修复load_all()中store_dir未定义崩溃 - 修复DependencyResolver入度计算(拓扑排序) - 修复PermissionError隐藏内置异常 - 修复CORS中间件头部未附加到响应 - 修复IntegrityChecker跳过__pycache__目录 - 修复版本号不一致(v2.0.0→v1.2.0) - 修复测试文件的Logger导入/路径/私有方法调用 - 修复context.py缺少typing导入 - 修复config.py STORE_DIR默认路径(./mods→./store) 测试覆盖: 14→91个测试, 全部通过
This commit is contained in:
116
oss/tests/test_plugin_bridge.py
Normal file
116
oss/tests/test_plugin_bridge.py
Normal file
@@ -0,0 +1,116 @@
|
||||
"""Tests for plugin-bridge: event bus, service registry"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
|
||||
PLUGIN_DIR = Path(__file__).parent.parent / "store" / "NebulaShell" / "plugin-bridge"
|
||||
sys.path.insert(0, str(PLUGIN_DIR))
|
||||
|
||||
import importlib.util
|
||||
spec = importlib.util.spec_from_file_location("plugin_bridge_main", str(PLUGIN_DIR / "main.py"))
|
||||
main_module = importlib.util.module_from_spec(spec)
|
||||
spec.loader.exec_module(main_module)
|
||||
Bridge = main_module.Bridge
|
||||
|
||||
|
||||
class TestEventBus:
|
||||
def test_on_and_emit(self):
|
||||
b = Bridge()
|
||||
results = []
|
||||
b.on("test.event", lambda *a, **kw: results.append((a, kw)))
|
||||
b.emit("test.event", "hello", x=1)
|
||||
assert len(results) == 1
|
||||
assert results[0] == (("hello",), {"x": 1})
|
||||
|
||||
def test_multiple_handlers(self):
|
||||
b = Bridge()
|
||||
r1, r2 = [], []
|
||||
b.on("evt", lambda: r1.append(1))
|
||||
b.on("evt", lambda: r2.append(2))
|
||||
b.emit("evt")
|
||||
assert r1 == [1]
|
||||
assert r2 == [2]
|
||||
|
||||
def test_off(self):
|
||||
b = Bridge()
|
||||
results = []
|
||||
handler = lambda: results.append(1)
|
||||
b.on("evt", handler)
|
||||
b.emit("evt")
|
||||
assert results == [1]
|
||||
b.off("evt", "unknown")
|
||||
b.emit("evt")
|
||||
assert results == [1]
|
||||
|
||||
def test_no_listeners(self):
|
||||
b = Bridge()
|
||||
result = b.emit("nonexistent")
|
||||
assert result == []
|
||||
|
||||
def test_has_listeners(self):
|
||||
b = Bridge()
|
||||
assert not b.has_listeners("evt")
|
||||
b.on("evt", lambda: None)
|
||||
assert b.has_listeners("evt")
|
||||
|
||||
def test_emit_async(self):
|
||||
import time
|
||||
b = Bridge()
|
||||
results = []
|
||||
def slow():
|
||||
time.sleep(0.05)
|
||||
results.append("done")
|
||||
b.on("async", slow)
|
||||
b.emit_async("async")
|
||||
assert len(results) == 0
|
||||
time.sleep(0.1)
|
||||
assert results == ["done"]
|
||||
|
||||
def test_clear(self):
|
||||
b = Bridge()
|
||||
b.on("evt", lambda: None)
|
||||
assert b.has_listeners("evt")
|
||||
b.event_bus.clear()
|
||||
assert not b.has_listeners("evt")
|
||||
|
||||
|
||||
class TestServiceRegistry:
|
||||
def test_register_and_get(self):
|
||||
b = Bridge()
|
||||
svc = {"name": "myservice"}
|
||||
b.provide("myservice", svc)
|
||||
assert b.use("myservice") is svc
|
||||
|
||||
def test_has_service(self):
|
||||
b = Bridge()
|
||||
assert not b.has_service("x")
|
||||
b.provide("x", object())
|
||||
assert b.has_service("x")
|
||||
|
||||
def test_list_services(self):
|
||||
b = Bridge()
|
||||
b.provide("a", object())
|
||||
b.provide("b", object())
|
||||
svcs = b.list_services()
|
||||
assert "a" in svcs
|
||||
assert "b" in svcs
|
||||
|
||||
def test_get_info(self):
|
||||
b = Bridge()
|
||||
info = b.get_info()
|
||||
assert "services" in info
|
||||
assert "event_listeners" in info
|
||||
|
||||
|
||||
class TestLifecycle:
|
||||
def test_init_start_stop(self):
|
||||
b = Bridge()
|
||||
b.init()
|
||||
b.start()
|
||||
b.stop()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
pytest.main([__file__, '-v'])
|
||||
Reference in New Issue
Block a user