refactor: drop workspace sync launcher path; simplify upgrade/switch

Align with xiaohe wheel/pip install: remove sync_workspace and desktop
launcher coupling from upgrade/switch flows.
This commit is contained in:
Zhengshou Lai
2026-07-25 12:43:24 +08:00
parent 37961feb21
commit d99c5534cb
11 changed files with 151 additions and 1428 deletions
+4 -125
View File
@@ -1,134 +1,13 @@
"""Tests for myagents.commands.switch (version / agent subcommands)."""
"""Tests for myagents.commands.switch (legacy shim)."""
from pathlib import Path
import pytest
from click.testing import CliRunner
from myagents.commands import switch as sw_mod
@pytest.fixture()
def fake_home(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path:
home = tmp_path / "home"
home.mkdir()
monkeypatch.setattr(Path, "home", staticmethod(lambda: home))
workspace = tmp_path / "ws"
workspace.mkdir()
monkeypatch.setattr(sw_mod, "get_workspace_root", lambda create=False: workspace)
# sync_workspace is imported locally in switch_version
import myagents.commands.sync_workspace as sync_mod
monkeypatch.setattr(
sync_mod,
"sync_workspace",
lambda _ws: {"added": [], "updated": [], "skipped": [], "removed": []},
)
# _install_tools is imported locally inside switch_version from upgrade
import myagents.commands.upgrade as ug_mod
monkeypatch.setattr(ug_mod, "_install_tools", lambda _rt: [])
return home
def _make_versions(home: Path, versions: tuple[str, ...], current: str) -> None:
root = home / ".xiaohe" / "runtime"
for name in versions:
(root / name).mkdir(parents=True)
(root / "current").symlink_to(root / current)
class TestSwitchVersion:
def test_switches_current_and_reinstalls(
self, fake_home: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
_make_versions(fake_home, ("v1", "v2"), "v2")
installed: list[str] = []
import myagents.commands.upgrade as ug_mod
monkeypatch.setattr(
ug_mod,
"_install_tools",
lambda _rt: (installed.append(_rt.name), [])[1] or [],
)
result = CliRunner().invoke(sw_mod.switch_version, ["v1", "--yes"])
assert result.exit_code == 0, result.output
assert "Switched:" in result.output
current = fake_home / ".xiaohe" / "runtime" / "current"
assert current.resolve().name == "v1"
assert installed == ["v1"]
def test_unknown_version_errors(self, fake_home: Path) -> None:
_make_versions(fake_home, ("v1", "v2"), "v2")
result = CliRunner().invoke(sw_mod.switch_version, ["v9", "--yes"])
assert result.exit_code != 0
assert "not installed" in result.output
def test_already_current_is_noop(self, fake_home: Path) -> None:
_make_versions(fake_home, ("v1", "v2"), "v2")
result = CliRunner().invoke(sw_mod.switch_version, ["v2", "--yes"])
assert result.exit_code == 0
assert "Already on v2" in result.output
def test_interactive_list_and_prompt(self, fake_home: Path) -> None:
_make_versions(fake_home, ("v1", "v2"), "v2")
result = CliRunner().invoke(sw_mod.switch_version, [], input="v2\ny\n")
assert result.exit_code == 0, result.output
assert "v2" in result.output and "(current)" in result.output
def test_cancelled_keeps_current(self, fake_home: Path) -> None:
_make_versions(fake_home, ("v1", "v2"), "v2")
result = CliRunner().invoke(sw_mod.switch_version, ["v1"], input="n\n")
assert result.exit_code == 0
assert "Cancelled" in result.output
assert (fake_home / ".xiaohe" / "runtime" / "current").resolve().name == "v2"
def test_no_versions_errors(self, fake_home: Path) -> None:
result = CliRunner().invoke(sw_mod.switch_version, ["v1", "--yes"])
assert result.exit_code != 0
assert "No installed runtime" in result.output
class TestSwitchAgent:
def test_bare_invocation_lists_agents(self) -> None:
result = CliRunner().invoke(
sw_mod.switch_agent, [], input="claude\n"
)
assert result.exit_code == 0, result.output
assert "Available agents" in result.output
assert "claude" in result.output
assert "kimi" in result.output
def test_switch_to_kimi(self) -> None:
result = CliRunner().invoke(sw_mod.switch_agent, ["kimi"])
assert result.exit_code == 0, result.output
assert "Switched" in result.output
def test_already_current_noop(self) -> None:
from myagents.settings import get_setting
current = get_setting("default_agent", "claude")
result = CliRunner().invoke(sw_mod.switch_agent, [current])
assert result.exit_code == 0
assert "Already on" in result.output
def test_unknown_agent_errors(self) -> None:
result = CliRunner().invoke(sw_mod.switch_agent, ["unknown-ai"])
assert result.exit_code != 0
assert "Unknown agent" in result.output
class TestSwitchGroup:
def test_bare_invocation_shows_options(self) -> None:
class TestSwitchHelp:
def test_group_mentions_upgrade(self) -> None:
result = CliRunner().invoke(sw_mod.switch_cmd, [])
assert result.exit_code == 0
assert "switch version" in result.output
assert "switch agent" in result.output
assert "switch provider" in result.output
def test_help_lists_subcommands(self) -> None:
result = CliRunner().invoke(sw_mod.switch_cmd, ["--help"])
assert result.exit_code == 0
assert "version" in result.output
assert "xiaohe upgrade" in result.output
assert "agent" in result.output
assert "provider" in result.output
-210
View File
@@ -1,210 +0,0 @@
"""Tests for myagents.commands.sync_workspace."""
import json
from pathlib import Path
import click
import pytest
from myagents.commands import sync_workspace as sync_mod
@pytest.fixture()
def runtime(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path:
rt = tmp_path / "runtime"
(rt / ".agents" / "skills" / "demo").mkdir(parents=True)
(rt / ".agents" / "skills" / "demo" / "SKILL.md").write_text("demo v1")
(rt / "agents.md").write_text("agent rules v1")
(rt / "VERSION").write_text("v0.0.0-test\n")
monkeypatch.setattr(sync_mod, "get_runtime_root", lambda: rt)
return rt
@pytest.fixture()
def workspace(tmp_path: Path) -> Path:
ws = tmp_path / "workspace"
ws.mkdir()
return ws
def _read_stamp(ws: Path) -> dict:
return json.loads((ws / sync_mod.STAMP_NAME).read_text())
class TestSyncWorkspace:
def test_new_files_copied_and_symlinks_made(
self, runtime: Path, workspace: Path
) -> None:
report = sync_mod.sync_workspace(workspace)
skill = workspace / ".agents" / "skills" / "demo" / "SKILL.md"
assert skill.read_text() == "demo v1"
assert (workspace / "agents.md").read_text() == "agent rules v1"
assert (workspace / "CLAUDE.md").is_symlink()
assert (workspace / ".claude").is_symlink()
assert report["added"] and not report["skipped"]
assert _read_stamp(workspace)["version"] == "v0.0.0-test"
def test_second_run_is_noop(self, runtime: Path, workspace: Path) -> None:
sync_mod.sync_workspace(workspace)
report = sync_mod.sync_workspace(workspace)
assert report == {"added": [], "updated": [], "skipped": [], "removed": []}
def test_upgrade_overwrites_untouched_file(
self, runtime: Path, workspace: Path
) -> None:
sync_mod.sync_workspace(workspace)
(runtime / "agents.md").write_text("agent rules v2")
report = sync_mod.sync_workspace(workspace)
assert (workspace / "agents.md").read_text() == "agent rules v2"
assert "agents.md" in report["updated"]
def test_user_modified_file_is_skipped(
self, runtime: Path, workspace: Path
) -> None:
sync_mod.sync_workspace(workspace)
(workspace / "agents.md").write_text("my local edits")
(runtime / "agents.md").write_text("agent rules v2")
report = sync_mod.sync_workspace(workspace)
assert (workspace / "agents.md").read_text() == "my local edits"
assert "agents.md" in report["skipped"]
def test_removed_from_runtime_is_removed_from_workspace(
self, runtime: Path, workspace: Path
) -> None:
sync_mod.sync_workspace(workspace)
(runtime / ".agents" / "skills" / "demo" / "SKILL.md").unlink()
report = sync_mod.sync_workspace(workspace)
assert not (workspace / ".agents" / "skills" / "demo" / "SKILL.md").exists()
assert report["removed"]
def test_git_checkout_refused_without_force(
self, runtime: Path, workspace: Path
) -> None:
(workspace / ".git").mkdir()
with pytest.raises(click.ClickException):
sync_mod.sync_workspace(workspace)
report = sync_mod.sync_workspace(workspace, force=True)
assert report["added"]
def test_symlinked_skill_dir_is_dereferenced(
self, runtime: Path, workspace: Path
) -> None:
# Skills living in contrib/ are linked into .agents/skills (relative
# symlink); the workspace must receive real self-contained files.
real = runtime / "contrib" / "mytoolkit" / ".claude" / "skills" / "mytoolkit"
real.mkdir(parents=True)
(real / "SKILL.md").write_text("toolkit skill v1")
import os
rel_target = os.path.relpath(real, runtime / ".agents" / "skills")
(runtime / ".agents" / "skills" / "mytoolkit").symlink_to(
rel_target, target_is_directory=True
)
report = sync_mod.sync_workspace(workspace)
copied = workspace / ".agents" / "skills" / "mytoolkit" / "SKILL.md"
assert copied.is_file() and not copied.is_symlink()
assert copied.read_text() == "toolkit skill v1"
assert ".agents/skills/mytoolkit/SKILL.md" in report["added"]
# Second run: content identical -> no spurious updates.
assert sync_mod.sync_workspace(workspace) == {
"added": [], "updated": [], "skipped": [], "removed": []
}
def test_existing_real_managed_symlink_target_is_kept(
self, runtime: Path, workspace: Path
) -> None:
# A pre-existing real CLAUDE.md must not be replaced by a symlink.
(workspace / "CLAUDE.md").write_text("my own claude rules")
sync_mod.sync_workspace(workspace)
claude_md = workspace / "CLAUDE.md"
assert not claude_md.is_symlink()
assert claude_md.read_text() == "my own claude rules"
assert (workspace / ".claude").is_symlink() # other links still made
class TestCreateLauncher:
@pytest.fixture()
def fake_home(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path:
home = tmp_path / "home"
(home / "Desktop").mkdir(parents=True)
monkeypatch.setattr(Path, "home", staticmethod(lambda: home))
return home
def test_macos_app_bundle_with_icon(
self, runtime: Path, fake_home: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
(runtime / "assets" / "icon").mkdir(parents=True)
(runtime / "assets" / "icon" / "XiaoheAgent.icns").write_bytes(b"icns")
legacy_cmd = fake_home / "Desktop" / "XiaoheAgent.command"
legacy_cmd.write_text("#!/bin/sh\n")
legacy_app = fake_home / "Desktop" / "XiaoheAgent.app"
legacy_app.mkdir()
monkeypatch.setattr(
sync_mod.os, "uname", lambda: type("U", (), {"sysname": "Darwin"})
)
sync_mod._create_launcher()
app = fake_home / "Desktop" / "Xiaohe Agent.app"
executable = app / "Contents" / "MacOS" / "XiaoheAgent"
plist = (app / "Contents" / "Info.plist").read_text()
assert "Xiaohe Agent" in plist
assert (app / "Contents" / "Resources" / "XiaoheAgent.icns").is_file()
assert executable.stat().st_mode & 0o111
assert "xiaohe" in executable.read_text()
assert not legacy_cmd.exists() # superseded .command removed
assert not legacy_app.exists() # renamed from XiaoheAgent.app
def test_linux_desktop_entry_with_icon(
self, runtime: Path, fake_home: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
(runtime / "assets" / "icon").mkdir(parents=True)
(runtime / "assets" / "icon" / "xiaohe-icon-512.png").write_bytes(b"png")
monkeypatch.setattr(
sync_mod.os, "uname", lambda: type("U", (), {"sysname": "Linux"})
)
sync_mod._create_launcher()
entry = (
fake_home / ".local" / "share" / "applications" / "Xiaohe Agent.desktop"
)
text = entry.read_text()
assert "Name=Xiaohe Agent" in text
assert "Icon=xiaohe-agent" in text
class TestInitCommand:
@pytest.fixture()
def fake_home(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path:
home = tmp_path / "home"
home.mkdir()
monkeypatch.setattr(Path, "home", staticmethod(lambda: home))
monkeypatch.setattr(sync_mod, "get_setting", lambda *a, **kw: "")
monkeypatch.setattr(sync_mod, "set_setting", lambda *a, **kw: None)
return home
def test_agent_tasks_created_from_template(
self, runtime: Path, workspace: Path, fake_home: Path
) -> None:
from click.testing import CliRunner
template = runtime / "assistant" / "agent-tasks.template.md"
template.parent.mkdir(parents=True)
template.write_text("# task board template")
result = CliRunner().invoke(sync_mod.init_cmd, [str(workspace)])
assert result.exit_code == 0, result.output
live = workspace / "assistant" / "agent-tasks.md"
assert live.read_text() == "# task board template"
assert (workspace / "tmp").is_dir() # skeleton dirs created
def test_existing_agent_tasks_untouched(
self, runtime: Path, workspace: Path, fake_home: Path
) -> None:
from click.testing import CliRunner
template = runtime / "assistant" / "agent-tasks.template.md"
template.parent.mkdir(parents=True)
template.write_text("# task board template")
live = workspace / "assistant" / "agent-tasks.md"
live.parent.mkdir(parents=True)
live.write_text("# my live board")
result = CliRunner().invoke(sync_mod.init_cmd, [str(workspace)])
assert result.exit_code == 0, result.output
assert live.read_text() == "# my live board"
+17 -280
View File
@@ -1,289 +1,26 @@
"""Tests for myagents.commands.upgrade."""
"""Tests for myagents.commands.upgrade (forwards to xiaohe upgrade)."""
import io
from pathlib import Path
from unittest.mock import patch
import pytest
from click.testing import CliRunner
from myagents.commands import upgrade as up_mod
@pytest.fixture()
def fake_home(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> Path:
home = tmp_path / "home"
home.mkdir()
monkeypatch.setattr(Path, "home", staticmethod(lambda: home))
return home
def _make_runtime(home: Path, version: str) -> Path:
target = home / ".xiaohe" / "runtime" / version
target.mkdir(parents=True)
current = target.parent / "current"
current.unlink(missing_ok=True)
current.symlink_to(target)
return target
class TestParseVersion:
def test_parses_quoted_version(self) -> None:
assert up_mod._parse_version('BASE_URL="x"\nVERSION="v1.2.3"\n') == "v1.2.3"
def test_returns_none_on_garbage(self) -> None:
assert up_mod._parse_version("no version here") is None
class TestPrune:
def test_keeps_two_newest(self, fake_home: Path) -> None:
root = fake_home / ".xiaohe" / "runtime"
for name in ("v1", "v2", "v3", "v4"):
d = root / name
d.mkdir(parents=True)
up_mod._prune()
remaining = sorted(p.name for p in root.iterdir())
assert remaining == ["v3", "v4"]
class TestUpgradeCommand:
def test_already_latest_shortcircuits(
self, fake_home: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
_make_runtime(fake_home, "v1.0.0")
monkeypatch.setattr(
up_mod, "_fetch_text", lambda *a: 'VERSION="v1.0.0"\n'
)
result = CliRunner().invoke(
up_mod.upgrade_cmd, ["--user", "u", "--password", "p"]
)
assert result.exit_code == 0
assert "Already up to date" in result.output
def test_forwards_to_xiaohe(self) -> None:
with (
patch.object(up_mod.shutil, "which", return_value="/bin/xiaohe"),
patch.object(up_mod.subprocess, "call", return_value=0) as call,
):
result = CliRunner().invoke(up_mod.upgrade_cmd, ["0.5.1", "--force"])
assert result.exit_code == 0
call.assert_called_once_with(
["/bin/xiaohe", "upgrade", "0.5.1", "--force"]
)
def test_missing_runtime_errors(self, fake_home: Path) -> None:
result = CliRunner().invoke(
up_mod.upgrade_cmd, ["--user", "u", "--password", "p"]
)
assert result.exit_code != 0
assert "install.sh" in result.output
def test_cancelled_by_user(
self, fake_home: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
_make_runtime(fake_home, "v1.0.0")
monkeypatch.setattr(
up_mod, "_fetch_text", lambda *a: 'VERSION="v2.0.0"\n'
)
result = CliRunner().invoke(
up_mod.upgrade_cmd, ["--user", "u", "--password", "p"], input="n\n"
)
assert result.exit_code == 0
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))}
self._buf = io.BytesIO(data)
def read(self, n: int = -1) -> bytes:
return self._buf.read(n)
def __enter__(self) -> "_FakeResponse":
return self
def __exit__(self, *args: object) -> None:
return None
class TestFetch:
def test_fetch_text_sends_basic_auth(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
seen: dict[str, str] = {}
def fake_urlopen(req, timeout=0):
seen["auth"] = req.headers["Authorization"]
return _FakeResponse(b"hello")
monkeypatch.setattr("urllib.request.urlopen", fake_urlopen)
assert up_mod._fetch_text("http://x/", "u", "p") == "hello"
assert seen["auth"].startswith("Basic ")
def test_401_maps_to_friendly_error(
self, monkeypatch: pytest.MonkeyPatch
) -> None:
import urllib.error
def fake_urlopen(req, timeout=0):
raise urllib.error.HTTPError("http://x/", 401, "Unauthorized", {}, None)
monkeypatch.setattr("urllib.request.urlopen", fake_urlopen)
with pytest.raises(Exception, match="401"):
up_mod._fetch_text("http://x/", "u", "p")
def test_download_writes_file(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
payload = b"x" * 100_000
monkeypatch.setattr(
"urllib.request.urlopen",
lambda req, timeout=0: _FakeResponse(payload),
)
dest = tmp_path / "out.tar.gz"
up_mod._download("http://x/f", "u", "p", dest)
assert dest.read_bytes() == payload
class TestPipInstallFlags:
def test_uses_break_system_packages_on_externally_managed(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
import sysconfig
# Force the plain-pip branch regardless of how pytest was launched
# (e.g. `uv run` sets VIRTUAL_ENV, which would select the uv branch).
monkeypatch.delenv("VIRTUAL_ENV", raising=False)
em = tmp_path / "EXTERNALLY-MANAGED"
em.write_text("[externally-managed]\n")
monkeypatch.setattr(
sysconfig, "get_path", lambda name: str(tmp_path) if name == "stdlib" else ""
)
seen: list[list[str]] = []
monkeypatch.setattr(
"subprocess.run",
lambda cmd, **kw: seen.append(cmd)
or type("R", (), {"returncode": 0, "stderr": ""})(),
)
up_mod._pip_install(tmp_path / "pkg")
assert "--break-system-packages" in seen[0]
def test_skips_flag_without_externally_managed(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
import sysconfig
monkeypatch.delenv("VIRTUAL_ENV", raising=False)
monkeypatch.setattr(
sysconfig, "get_path", lambda name: str(tmp_path) if name == "stdlib" else ""
)
seen: list[list[str]] = []
monkeypatch.setattr(
"subprocess.run",
lambda cmd, **kw: seen.append(cmd)
or type("R", (), {"returncode": 0, "stderr": ""})(),
)
up_mod._pip_install(tmp_path / "pkg")
assert "--break-system-packages" not in seen[0]
def test_prefers_uv_pip_inside_virtualenv(
self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.setenv("VIRTUAL_ENV", str(tmp_path / "venv"))
monkeypatch.setattr(up_mod.shutil, "which", lambda name: f"/usr/bin/{name}")
seen: list[list[str]] = []
monkeypatch.setattr(
"subprocess.run",
lambda cmd, **kw: seen.append(cmd)
or type("R", (), {"returncode": 0, "stderr": ""})(),
)
up_mod._pip_install(tmp_path / "pkg")
assert seen[0][:3] == ["uv", "pip", "install"]
assert "--break-system-packages" not in seen[0]
def test_missing_xiaohe(self) -> None:
with patch.object(up_mod.shutil, "which", return_value=None):
result = CliRunner().invoke(up_mod.upgrade_cmd, [])
assert result.exit_code != 0
assert "xiaohe upgrade" in result.output
+17 -15
View File
@@ -3,16 +3,14 @@
import subprocess
from pathlib import Path
from click.testing import CliRunner
from myagents.commands import version as ver_mod
class TestDescribeTree:
def test_installed_runtime_tree(self) -> None:
def test_legacy_runtime_tree(self) -> None:
tree = Path("/home/u/.xiaohe/runtime/v1.2.3/contrib/myagents")
mode, detail = ver_mod.describe_tree(tree)
assert mode == "installed"
assert mode == "legacy-runtime"
assert detail == "v1.2.3"
def test_development_checkout(self, tmp_path: Path) -> None:
@@ -33,16 +31,20 @@ class TestGitDescribe:
repo.mkdir()
subprocess.run(["git", "init", "-q"], cwd=repo, check=True)
subprocess.run(
["git", "-c", "user.email=t@t", "-c", "user.name=t",
"commit", "-q", "--allow-empty", "-m", "init"],
cwd=repo, check=True,
[
"git",
"-c",
"user.email=t@t",
"-c",
"user.name=t",
"commit",
"-q",
"--allow-empty",
"-m",
"init",
],
cwd=repo,
check=True,
)
result = ver_mod._git_describe(repo)
assert "branch" in result
class TestVersionCommand:
def test_runs_and_reports(self) -> None:
result = CliRunner().invoke(ver_mod.version_cmd)
assert result.exit_code == 0
assert "myagents" in result.output
assert result != "unknown"