feat: 重写沙箱 + 完整测试套件 (192 tests)
沙箱 v2 - 多层隔离: - AST 静态分析: 30+ 危险模式检测 - 子进程隔离: 独立进程执行 + 资源限制 - 网络阻断: socket 模块替换 - 模块白名单: 30 个安全模块 - 文件系统限制: 临时目录隔离 - SandboxResult 数据类 + 执行历史 测试套件 (8 个模块, 192 个用例): - test_sandbox.py: AST检查(47) + 沙箱执行(15) + 安全拒绝(20) + 超时(2) + 模式对比(3) - test_conversation.py: 响应解析(13) + 思考标记(5) + 工具提取(10) - test_config.py: 配置加载/保存/缓存/损坏处理(8) - test_history.py: 历史CRUD/持久化/边界(12) - test_engines.py: 引擎管理/加载/切换(14) - test_plugins.py: code_editor(10) + code_search(7) + git_tools(4) + tool_manager(7) - test_integration.py: 沙箱+对话(2) + 历史+对话(2) + 安全链(3) + 提示词(1)
This commit is contained in:
1 parent
b1b2097311
commit
ff53a89754
10 files changed
+2205
-72
No files matched your search
@@ -0,0 +1,107 @@
|
||||
"""AI 引擎管理器测试"""
|
||||
import os
|
||||
import sys
|
||||
import pytest
|
||||
|
||||
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||
|
||||
from xiaoli.engines import EngineManager
|
||||
|
||||
|
||||
class TestEngineManager:
|
||||
"""EngineManager 测试"""
|
||||
|
||||
def test_init_empty(self):
|
||||
mgr = EngineManager()
|
||||
assert mgr.engines == {}
|
||||
assert mgr.current is None
|
||||
|
||||
def test_set_default_existing(self):
|
||||
mgr = EngineManager()
|
||||
mgr.engines = {'test': 'fake_engine'}
|
||||
assert mgr.set_default('test')
|
||||
assert mgr.current == 'fake_engine'
|
||||
|
||||
def test_set_default_missing(self):
|
||||
mgr = EngineManager()
|
||||
assert not mgr.set_default('nonexistent')
|
||||
|
||||
def test_switch_existing(self):
|
||||
mgr = EngineManager()
|
||||
mgr.engines = {'a': 'engine_a', 'b': 'engine_b'}
|
||||
assert mgr.switch('b')
|
||||
assert mgr.current == 'engine_b'
|
||||
|
||||
def test_switch_missing(self):
|
||||
mgr = EngineManager()
|
||||
assert not mgr.switch('nope')
|
||||
|
||||
def test_current_name_none(self):
|
||||
mgr = EngineManager()
|
||||
assert mgr.current_name() == '未设置'
|
||||
|
||||
def test_current_name_set(self):
|
||||
mgr = EngineManager()
|
||||
mgr.current = type('E', (), {'name': 'ollama'})()
|
||||
assert mgr.current_name() == 'ollama'
|
||||
|
||||
def test_names(self):
|
||||
mgr = EngineManager()
|
||||
mgr.engines = {'a': 1, 'b': 2, 'c': 3}
|
||||
names = mgr.names()
|
||||
assert set(names) == {'a', 'b', 'c'}
|
||||
|
||||
def test_generate_no_engine(self):
|
||||
mgr = EngineManager()
|
||||
result = mgr.generate("hello")
|
||||
assert '错误' in result or 'error' in result.lower()
|
||||
|
||||
def test_generate_with_engine(self):
|
||||
mgr = EngineManager()
|
||||
|
||||
class FakeEngine:
|
||||
name = 'fake'
|
||||
def generate_response(self, user_input, system_prompt=None):
|
||||
return f"echo: {user_input}"
|
||||
|
||||
mgr.current = FakeEngine()
|
||||
result = mgr.generate("test input")
|
||||
assert 'test input' in result
|
||||
|
||||
def test_load_all_missing_dir(self):
|
||||
mgr = EngineManager()
|
||||
result = mgr.load_all('/tmp/nonexistent_engines_dir_12345')
|
||||
assert result == {}
|
||||
|
||||
def test_find_class_patterns(self):
|
||||
mgr = EngineManager()
|
||||
import types
|
||||
mod = types.ModuleType('test')
|
||||
|
||||
class TestAI:
|
||||
def generate_response(self, **kw):
|
||||
pass
|
||||
mod.TestAI = TestAI
|
||||
|
||||
found = mgr._find_class(mod, 'test')
|
||||
assert found == 'TestAI'
|
||||
|
||||
def test_find_class_no_match(self):
|
||||
mgr = EngineManager()
|
||||
import types
|
||||
mod = types.ModuleType('test')
|
||||
mod.Foo = 42 # not a class ending in AI
|
||||
found = mgr._find_class(mod, 'test')
|
||||
assert found is None
|
||||
|
||||
def test_load_all_real_engines(self):
|
||||
"""测试加载实际引擎目录(不验证引擎可用性,只验证不崩溃)"""
|
||||
mgr = EngineManager()
|
||||
engines_dir = os.path.join(
|
||||
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||
'ai_engines'
|
||||
)
|
||||
if os.path.exists(engines_dir):
|
||||
mgr.load_all(engines_dir)
|
||||
# 至少应该尝试加载(可能因缺少依赖而失败)
|
||||
# 但不应崩溃
|
||||
Reference in new issue
Block a user