feat: 审查修复一轮——sync 解引用软链技能/防 CLAUDE.md 崩溃/init 建 agent-tasks;upgrade SHA256 校验+收尾容错;新增 xiaohe switch 回滚;uninstall --all 扩到 ~/.metabot ~/.mytoolkit;下载路径改 xiaohe-agent

This commit is contained in:
Zhengshou Lai
2026-07-16 19:36:18 +08:00
parent f878a49c8a
commit 84ad0fa4cf
9 changed files with 432 additions and 17 deletions
+98
View File
@@ -80,6 +80,104 @@ class TestUpgradeCommand:
assert "Cancelled" in result.output
def _make_tarball_bytes() -> bytes:
import tarfile
buf = io.BytesIO()
with tarfile.open(fileobj=buf, mode="w:gz") as tf:
data = b"#!/bin/sh\n"
info = tarfile.TarInfo("workspace/setup.sh")
info.size = len(data)
tf.addfile(info, io.BytesIO(data))
return buf.getvalue()
def _wire_upgrade(
monkeypatch: pytest.MonkeyPatch,
tmp_path: Path,
payload: bytes,
sha: str,
) -> None:
import hashlib
def fake_fetch(url: str, user: str, password: str) -> str:
if url.endswith("install.sh"):
return 'VERSION="v2.0.0"\n'
if url.endswith(".sha256"):
return f"{sha} xiaohe-agent-latest.tar.gz\n"
raise AssertionError(f"unexpected url {url}")
monkeypatch.setattr(up_mod, "_fetch_text", fake_fetch)
monkeypatch.setattr(
up_mod, "_download", lambda url, u, p, dest: dest.write_bytes(payload)
)
monkeypatch.setattr(up_mod, "_install_tools", lambda runtime: [])
workspace = tmp_path / "ws"
workspace.mkdir()
monkeypatch.setattr(up_mod, "get_workspace_root", lambda create=False: workspace)
monkeypatch.setattr(
up_mod,
"sync_workspace",
lambda ws: {"added": [], "updated": [], "skipped": [], "removed": []},
)
class TestUpgradeFlow:
def test_downloads_verifies_and_switches(
self, fake_home: Path, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
import hashlib
_make_runtime(fake_home, "v1.0.0")
payload = _make_tarball_bytes()
sha = hashlib.sha256(payload).hexdigest()
_wire_upgrade(monkeypatch, tmp_path, payload, sha)
result = CliRunner().invoke(
up_mod.upgrade_cmd, ["--user", "u", "--password", "p"], input="y\n"
)
assert result.exit_code == 0, result.output
assert "Upgraded: v1.0.0 -> v2.0.0" in result.output
current = fake_home / ".xiaohe" / "runtime" / "current"
assert current.resolve().name == "v2.0.0"
assert (current / "setup.sh").is_file()
def test_checksum_mismatch_aborts_before_install(
self, fake_home: Path, monkeypatch: pytest.MonkeyPatch, tmp_path: Path
) -> None:
_make_runtime(fake_home, "v1.0.0")
payload = _make_tarball_bytes()
_wire_upgrade(monkeypatch, tmp_path, payload, "0" * 64)
result = CliRunner().invoke(
up_mod.upgrade_cmd, ["--user", "u", "--password", "p"], input="y\n"
)
assert result.exit_code != 0
assert "Checksum mismatch" in result.output
# Old runtime untouched, no new version dir.
current = fake_home / ".xiaohe" / "runtime" / "current"
assert current.resolve().name == "v1.0.0"
assert not (fake_home / ".xiaohe" / "runtime" / "v2.0.0").exists()
class TestVerifyChecksum:
def test_accepts_matching(self, tmp_path: Path) -> None:
import hashlib
blob = tmp_path / "f.tar.gz"
blob.write_bytes(b"data")
sha = hashlib.sha256(b"data").hexdigest()
up_mod._verify_checksum(blob, f"{sha} f.tar.gz\n", "http://x/")
def test_rejects_mismatch_and_garbage(self, tmp_path: Path) -> None:
import click
blob = tmp_path / "f.tar.gz"
blob.write_bytes(b"data")
with pytest.raises(click.ClickException, match="Checksum mismatch"):
up_mod._verify_checksum(blob, f"{'0' * 64} f.tar.gz\n", "http://x/")
with pytest.raises(click.ClickException, match="looks wrong"):
up_mod._verify_checksum(blob, "not-a-checksum\n", "http://x/")
class _FakeResponse:
def __init__(self, data: bytes) -> None:
self.headers = {"Content-Length": str(len(data))}