a57dbc0252
功能: - 多主机管理 (增删改查, 密码/私钥双认证, 导入导出) - 远程终端 (命令执行 + 常用命令快捷栏 + 超时控制) - SFTP 文件浏览 (上传/下载带进度, 新建/删除/重命名) - 实时监控 (CPU/内存/磁盘/网络, 1-10秒可调刷新) - AI Agent (OpenAI 兼容 API, 5 工具自动调用: 命令/指标/列文件/读文件/上传) 技术栈: PyQt5 + paramiko + psutil + requests + PyInstaller 打包: build_windows.bat / build.sh 一键产出 ~57MB 单文件 exe 测试: core 6/6 + UI 3/3 + E2E 6/6 全部通过
152 lines
4.7 KiB
Python
152 lines
4.7 KiB
Python
"""
|
|
核心模块 smoke test
|
|
跳过需要真实 SSH 服务的部分,验证:
|
|
- SSHConnection 实例化、错误处理
|
|
- SystemMonitor 解析逻辑(用伪造输出)
|
|
- AIAgent 工具注册正确
|
|
- ConnectionManager 增删改
|
|
"""
|
|
import sys
|
|
import json
|
|
from pathlib import Path
|
|
|
|
# 允许在 venv 之外运行
|
|
sys.path.insert(0, str(Path(__file__).parent))
|
|
|
|
from core.ssh_client import SSHConnection
|
|
from core.monitor import SystemMonitor
|
|
from core.ai_agent import AIAgent, TOOL_DEFINITIONS
|
|
from core.manager import ConnectionManager
|
|
|
|
|
|
def test_ssh_connection_failure():
|
|
"""连不通的地址应返回失败而不是抛异常"""
|
|
c = SSHConnection("127.0.0.1", 1, "nope", "nope", timeout=2)
|
|
ok, msg = c.connect()
|
|
assert ok is False
|
|
assert "失败" in msg or "认证" in msg
|
|
print(f" ✓ 连接失败正确处理: {msg[:60]}")
|
|
|
|
|
|
def test_monitor_parse():
|
|
"""SystemMonitor 解析伪造的脚本输出"""
|
|
fake_out = """
|
|
===CPU===
|
|
CPU_USAGE=37
|
|
CPU_CORES=8
|
|
LOAD=0.50 0.40 0.30
|
|
UPTIME=86400
|
|
===MEM===
|
|
MEM_TOTAL=16777216
|
|
MEM_USED=8388608
|
|
MEM_AVAIL=8388608
|
|
SWAP_TOTAL=2097152
|
|
SWAP_USED=1048576
|
|
===DISK===
|
|
DISK|/dev/sda1|107374182400|53687091200|50%
|
|
DISK|/dev/sda2|536870912000|268435456000|50%
|
|
===NET===
|
|
NET|eth0|1234567|7654321
|
|
NET|eth1|100|200
|
|
===HOST===
|
|
HOSTNAME=test-server
|
|
KERNEL=5.15.0-test
|
|
OS=Ubuntu 22.04
|
|
"""
|
|
# 用 monkey-patch 模拟 exec_command
|
|
c = SSHConnection("dummy", 22, "u", "p")
|
|
c.connected = True
|
|
c.exec_command = lambda cmd, timeout=10: (0, fake_out, "")
|
|
|
|
m = SystemMonitor.collect(c)
|
|
assert m["cpu"] == 37.0, m["cpu"]
|
|
assert m["cores"] == 8
|
|
assert m["load1"] == 0.5
|
|
assert m["mem_total"] == 16777216
|
|
assert m["mem_percent"] == 50.0
|
|
assert len(m["disks"]) == 2
|
|
assert m["disks"][0]["mount"] == "/dev/sda1"
|
|
assert m["disks"][0]["percent"] == 50
|
|
assert len(m["net"]) == 2
|
|
assert m["net"][0]["iface"] == "eth0"
|
|
assert m["net"][0]["rx"] == 1234567
|
|
assert m["hostname"] == "test-server"
|
|
assert m["os"] == "Ubuntu 22.04"
|
|
print(f" ✓ 监控解析: CPU={m['cpu']}% MEM={m['mem_percent']:.1f}% disks={len(m['disks'])} nets={len(m['net'])}")
|
|
|
|
|
|
def test_ai_agent_tools():
|
|
"""AI Agent 工具注册"""
|
|
assert len(TOOL_DEFINITIONS) >= 4
|
|
names = {t["function"]["name"] for t in TOOL_DEFINITIONS}
|
|
assert "exec_ssh_command" in names
|
|
assert "get_system_metrics" in names
|
|
assert "list_remote_files" in names
|
|
assert "read_remote_file" in names
|
|
assert "upload_local_file" in names
|
|
a = AIAgent(api_key="test", model="gpt-4o-mini")
|
|
assert a.api_key == "test"
|
|
assert len(a.history) == 0
|
|
print(f" ✓ AI Agent 注册 {len(TOOL_DEFINITIONS)} 个工具")
|
|
|
|
|
|
def test_ai_agent_missing_key():
|
|
"""没配 API Key 时应抛出友好错误"""
|
|
a = AIAgent(api_key="", model="gpt-4o")
|
|
try:
|
|
a.chat("hello", lambda n, a: "ok")
|
|
assert False, "应抛出异常"
|
|
except RuntimeError as e:
|
|
assert "API Key" in str(e)
|
|
print(f" ✓ AI 缺 Key 抛错: {e}")
|
|
|
|
|
|
def test_manager(tmpdir=None):
|
|
"""管理器增删改查(用临时配置目录)"""
|
|
import tempfile
|
|
from pathlib import Path
|
|
import core.manager as mgr
|
|
with tempfile.TemporaryDirectory() as td:
|
|
mgr.CONFIG_DIR = Path(td)
|
|
mgr.CONFIG_FILE = Path(td) / "hosts.json"
|
|
mgr.AI_CONFIG_FILE = Path(td) / "ai.json"
|
|
m = ConnectionManager()
|
|
m.hosts = [] # 清空
|
|
new_id = m.add_host({"name": "test", "host": "1.2.3.4", "port": 22,
|
|
"username": "u", "password": "p"})
|
|
assert new_id
|
|
assert len(m.hosts) == 1
|
|
m.update_host(new_id, {"name": "renamed", "host": "5.6.7.8",
|
|
"port": 2222, "username": "u", "password": "p"})
|
|
h = m.get_host(new_id)
|
|
assert h["name"] == "renamed"
|
|
assert h["host"] == "5.6.7.8"
|
|
m.remove_host(new_id)
|
|
assert m.get_host(new_id) is None
|
|
print(" ✓ 管理器 CRUD 正常")
|
|
|
|
|
|
def test_format():
|
|
"""格式化函数"""
|
|
assert SystemMonitor.format_bytes(1024) == "1.0KB"
|
|
assert SystemMonitor.format_bytes(1024**3) == "1.0GB"
|
|
assert "1天" in SystemMonitor.format_uptime(90000)
|
|
assert "3分" in SystemMonitor.format_uptime(200)
|
|
print(" ✓ 字节/时长格式化正确")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
print("[1/6] SSHConnection 失败处理")
|
|
test_ssh_connection_failure()
|
|
print("[2/6] SystemMonitor 解析")
|
|
test_monitor_parse()
|
|
print("[3/6] AI Agent 工具注册")
|
|
test_ai_agent_tools()
|
|
print("[4/6] AI Agent 缺 Key")
|
|
test_ai_agent_missing_key()
|
|
print("[5/6] ConnectionManager CRUD")
|
|
test_manager()
|
|
print("[6/6] 格式化函数")
|
|
test_format()
|
|
print("\n所有核心模块 smoke test 通过 ✓")
|