mirror of
https://github.com/hanzoai/python-sdk.git
synced 2026-10-23 20:23:00 +00:00
Rename hanzo-repl → hanzo-dev to match @hanzo/dev (Rust/TS). New packages: - hanzo-lsp: async LSP client (diagnostics, go-to-def, references, context enrichment) - hanzo-hooks: pre/post tool use hooks (shell scripts, exit-code deny) - hanzo-sandbox: container detection, filesystem isolation, Linux unshare hanzo-dev TUI: - Vim keybindings (Normal/Insert/Visual/Command modes, 37 tests) - Git slash commands (/branch /commit /worktree /diff /stash, 13 tests) - All slash commands at parity: /model /permissions /clear /resume /memory /init /export /session /plan /solve /code /auto /fast /remote-control - Multi-provider auth: Anthropic OAuth, OpenAI device code, Hanzo PKCE, auto-detect Auth: - login_with_anthropic() — PKCE via console.anthropic.com - login_with_openai() — device code flow via auth.openai.com - login_auto() — env var detection + saved credential fallback - OAuthCredentialStore supports provider keys (hanzo/anthropic/openai) Parity test suite (38 tests): SSE, MCP normalization, permissions, compaction, token usage, PKCE RFC vector, config, backoff, session roundtrip, hooks. 348 tests total, 0 failures across Python + Rust + Universe E2E.
118 lines
3.5 KiB
Python
118 lines
3.5 KiB
Python
"""Tests for git slash commands using a temporary git repo."""
|
|
|
|
import os
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from hanzo_dev.git_commands import (
|
|
git_branch,
|
|
git_commit,
|
|
git_diff,
|
|
git_stash,
|
|
git_status,
|
|
git_worktree,
|
|
handle_git_command,
|
|
)
|
|
|
|
_ENV = {
|
|
**os.environ,
|
|
"GIT_AUTHOR_NAME": "test",
|
|
"GIT_AUTHOR_EMAIL": "t@t",
|
|
"GIT_COMMITTER_NAME": "test",
|
|
"GIT_COMMITTER_EMAIL": "t@t",
|
|
}
|
|
|
|
|
|
def _run(*args: str, cwd: Path) -> None:
|
|
subprocess.run(args, cwd=cwd, capture_output=True, text=True, env=_ENV, check=True)
|
|
|
|
|
|
@pytest.fixture()
|
|
def git_repo(tmp_path: Path) -> Path:
|
|
"""Create a temporary git repo with one committed file."""
|
|
_run("git", "init", "-b", "main", cwd=tmp_path)
|
|
(tmp_path / "README.md").write_text("# test\n")
|
|
_run("git", "add", "README.md", cwd=tmp_path)
|
|
_run("git", "commit", "-m", "init", cwd=tmp_path)
|
|
return tmp_path
|
|
|
|
|
|
class TestGitStatus:
|
|
def test_clean_repo(self, git_repo: Path) -> None:
|
|
out = git_status(git_repo)
|
|
assert "nothing to commit" in out.lower() or "clean" in out.lower()
|
|
|
|
def test_dirty_repo(self, git_repo: Path) -> None:
|
|
(git_repo / "new.txt").write_text("hello\n")
|
|
out = git_status(git_repo)
|
|
assert "new.txt" in out
|
|
|
|
|
|
class TestGitBranch:
|
|
def test_list_branches(self, git_repo: Path) -> None:
|
|
out = git_branch("", git_repo)
|
|
assert "main" in out
|
|
|
|
def test_create_branch(self, git_repo: Path) -> None:
|
|
out = git_branch("feature-x", git_repo)
|
|
assert "feature-x" in out
|
|
listing = git_branch("", git_repo)
|
|
assert "feature-x" in listing
|
|
|
|
|
|
class TestGitCommit:
|
|
def test_commit_staged(self, git_repo: Path) -> None:
|
|
(git_repo / "a.txt").write_text("a\n")
|
|
out = git_commit("add a.txt", git_repo)
|
|
assert "add a.txt" in out or "1 file changed" in out or "create mode" in out
|
|
|
|
def test_commit_nothing(self, git_repo: Path) -> None:
|
|
out = git_commit("empty", git_repo)
|
|
assert "nothing" in out.lower() or "no changes" in out.lower()
|
|
|
|
|
|
class TestGitDiff:
|
|
def test_no_diff(self, git_repo: Path) -> None:
|
|
out = git_diff(git_repo)
|
|
assert out.strip() == "" or "no changes" in out.lower()
|
|
|
|
def test_has_diff(self, git_repo: Path) -> None:
|
|
(git_repo / "README.md").write_text("# changed\n")
|
|
out = git_diff(git_repo)
|
|
assert "changed" in out
|
|
|
|
|
|
class TestGitStash:
|
|
def test_stash_and_pop(self, git_repo: Path) -> None:
|
|
(git_repo / "README.md").write_text("# stashed\n")
|
|
out = git_stash("", git_repo)
|
|
assert "saved" in out.lower() or "stash" in out.lower()
|
|
|
|
list_out = git_stash("list", git_repo)
|
|
assert "stash@" in list_out or "stash" in list_out.lower()
|
|
|
|
git_stash("pop", git_repo)
|
|
assert "stashed" in (git_repo / "README.md").read_text()
|
|
|
|
def test_stash_nothing(self, git_repo: Path) -> None:
|
|
out = git_stash("", git_repo)
|
|
assert "no local changes" in out.lower() or "nothing" in out.lower() or "no changes" in out.lower()
|
|
|
|
|
|
class TestGitWorktree:
|
|
def test_list_worktrees(self, git_repo: Path) -> None:
|
|
out = git_worktree("", git_repo)
|
|
assert str(git_repo) in out or "main" in out
|
|
|
|
|
|
class TestDispatcher:
|
|
def test_known_command(self, git_repo: Path) -> None:
|
|
out = handle_git_command("status", "", git_repo)
|
|
assert out is not None
|
|
|
|
def test_unknown_command(self, git_repo: Path) -> None:
|
|
out = handle_git_command("frobnicate", "", git_repo)
|
|
assert out is None
|