Agent/JOY_JSON mode returns {ok,count,items,_joy} for array results
(board ls etc). Compat escape: JOY_LIST_ROOT=array keeps bare arrays.
148 lines
3.8 KiB
Python
148 lines
3.8 KiB
Python
"""test_smoke.py — 所有命令 smoke test"""
|
||
|
||
import json
|
||
import subprocess
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
import pytest
|
||
|
||
SRC = Path(__file__).parent.parent / "src"
|
||
sys.path.insert(0, str(SRC))
|
||
|
||
|
||
def run_joy(args: list[str], env_identity: str = "tux") -> subprocess.CompletedProcess:
|
||
"""运行 joy 命令"""
|
||
import os
|
||
env = os.environ.copy()
|
||
env["JOY_IDENTITY"] = env_identity
|
||
env["PYTHONPATH"] = str(SRC)
|
||
return subprocess.run(
|
||
[sys.executable, "-m", "joy"] + args,
|
||
capture_output=True, text=True, env=env, timeout=30,
|
||
)
|
||
|
||
|
||
def test_help():
|
||
r = run_joy(["help"])
|
||
assert r.returncode == 0
|
||
assert "joy" in r.stdout
|
||
assert "patrol" in r.stdout
|
||
|
||
|
||
def test_no_args():
|
||
r = run_joy([])
|
||
assert r.returncode == 0
|
||
assert "joy" in r.stdout
|
||
|
||
|
||
def test_dash_help():
|
||
"""bash 版坏的 --help,Python 版应该正常"""
|
||
r = run_joy(["--help"])
|
||
assert r.returncode == 0
|
||
assert "joy" in r.stdout
|
||
|
||
|
||
def test_h():
|
||
"""bash 版坏的 -h,Python 版应该正常"""
|
||
r = run_joy(["-h"])
|
||
assert r.returncode == 0
|
||
assert "joy" in r.stdout
|
||
|
||
|
||
def test_unknown_command():
|
||
r = run_joy(["nonexistent-command-xyz"])
|
||
assert r.returncode != 0
|
||
err = json.loads(r.stderr)
|
||
assert "error" in err
|
||
|
||
|
||
def test_all_commands_register():
|
||
"""所有 50 命令都能注册"""
|
||
from joy.cli import _import_all_commands
|
||
_import_all_commands()
|
||
import joy.cli
|
||
assert len(joy.cli.COMMANDS) >= 50
|
||
|
||
|
||
def test_patrol_returns_json():
|
||
"""patrol 返回合法 JSON(需要 SSH,可能失败)"""
|
||
r = run_joy(["patrol", "devops"])
|
||
# patrol 可能 SSH 超时,但格式应该是 JSON
|
||
try:
|
||
data = json.loads(r.stdout)
|
||
assert "vm" in data or isinstance(data, list)
|
||
except json.JSONDecodeError:
|
||
# SSH 不通时 stderr 应该有 JSON 错误
|
||
if r.stderr:
|
||
err = json.loads(r.stderr)
|
||
assert "error" in err
|
||
|
||
|
||
def test_health_status():
|
||
"""health status 返回 JSON"""
|
||
r = run_joy(["health", "status"])
|
||
# 可能因为 llm-router.sh 不在而失败
|
||
if r.returncode == 0:
|
||
data = json.loads(r.stdout)
|
||
assert isinstance(data, dict)
|
||
else:
|
||
err = json.loads(r.stderr)
|
||
assert "error" in err
|
||
|
||
|
||
def test_board_ls():
|
||
"""board ls:agent 模式为 {items:[...]};兼容 JOY_LIST_ROOT=array 为根数组"""
|
||
r = run_joy(["board", "ls"])
|
||
if r.returncode == 0:
|
||
data = json.loads(r.stdout)
|
||
assert isinstance(data, list) or (
|
||
isinstance(data, dict) and isinstance(data.get("items"), list)
|
||
)
|
||
else:
|
||
err = json.loads(r.stderr)
|
||
assert "error" in err
|
||
|
||
|
||
def test_recall_search():
|
||
"""recall search 返回内容"""
|
||
r = run_joy(["recall", "search", "test", "-n", "1"])
|
||
# recall 可能因为 cluster-recall 不在 PATH 而失败
|
||
if r.returncode == 0:
|
||
assert len(r.stdout) > 0
|
||
else:
|
||
err = json.loads(r.stderr)
|
||
assert "error" in err
|
||
|
||
|
||
def test_permission_denied():
|
||
"""tux 命令需要 tux 权限"""
|
||
r = run_joy(["tux", "pending"], env_identity="wenpai")
|
||
assert r.returncode != 0
|
||
err = json.loads(r.stderr)
|
||
assert err["code"] == "E_PERM"
|
||
|
||
|
||
def test_env_detection():
|
||
"""环境检测"""
|
||
from joy.core.env import Env
|
||
env = Env()
|
||
assert env.identity in ("tux", "konqi", "wapuu", "mac", "wenpai", "devops") or env.identity
|
||
assert env.is_mac in (True, False)
|
||
|
||
|
||
def test_error_json_format():
|
||
"""错误输出是 JSON 且在 stderr"""
|
||
r = run_joy(["patrol"]) # 缺参数
|
||
assert r.returncode != 0
|
||
err = json.loads(r.stderr)
|
||
assert "error" in err
|
||
assert "code" in err
|
||
|
||
|
||
def test_pretty_flag():
|
||
"""--pretty 美化输出"""
|
||
r = run_joy(["--pretty", "health", "status"])
|
||
if r.returncode == 0:
|
||
# pretty JSON 有换行
|
||
assert "\n" in r.stdout or r.stdout == ""
|