This repository has been archived on 2026-03-24. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Timmy-time-dashboard/tests/test_hands_git.py

203 lines
6.0 KiB
Python

"""Tests for the Git Hand.
Covers:
- Destructive operation gating
- Successful git operations (status, log)
- Convenience wrappers
- GitResult dataclass defaults
- Info summary
"""
import pytest
# ---------------------------------------------------------------------------
# Destructive operation gating
# ---------------------------------------------------------------------------
def test_is_destructive_detects_force_push():
"""Force-push should be flagged as destructive."""
from infrastructure.hands.git import GitHand
hand = GitHand()
assert hand._is_destructive("push --force origin main") is True
assert hand._is_destructive("push -f origin main") is True
def test_is_destructive_detects_hard_reset():
"""Hard reset should be flagged as destructive."""
from infrastructure.hands.git import GitHand
hand = GitHand()
assert hand._is_destructive("reset --hard HEAD~1") is True
def test_is_destructive_safe_ops():
"""Safe operations should not be flagged."""
from infrastructure.hands.git import GitHand
hand = GitHand()
assert hand._is_destructive("status") is False
assert hand._is_destructive("log --oneline -5") is False
assert hand._is_destructive("push origin main") is False
assert hand._is_destructive("commit -m 'test'") is False
@pytest.mark.asyncio
async def test_run_blocks_destructive_by_default():
"""Destructive ops should be blocked unless allow_destructive=True."""
from infrastructure.hands.git import GitHand
hand = GitHand()
result = await hand.run("push --force origin main")
assert result.success is False
assert result.requires_confirmation is True
assert "blocked" in result.error.lower()
@pytest.mark.asyncio
async def test_run_allows_destructive_with_flag():
"""Destructive ops should execute when allow_destructive=True."""
from infrastructure.hands.git import GitHand
# This will fail because there's no remote, but it should NOT be blocked
hand = GitHand()
result = await hand.run("push --force origin nonexistent-branch", allow_destructive=True)
# It will fail (no remote), but it was NOT blocked by the gate
assert result.requires_confirmation is False
# ---------------------------------------------------------------------------
# Successful operations
# ---------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_run_git_status():
"""git status should succeed in a git repo."""
from infrastructure.hands.git import GitHand
hand = GitHand()
result = await hand.run("status --short")
assert result.success is True
assert result.latency_ms > 0
@pytest.mark.asyncio
async def test_run_git_log():
"""git log should succeed in a git repo."""
from infrastructure.hands.git import GitHand
hand = GitHand()
result = await hand.run("log --oneline -3")
assert result.success is True
assert result.output # should have at least one commit
# ---------------------------------------------------------------------------
# Convenience wrappers
# ---------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_status_wrapper():
"""status() convenience wrapper should work."""
from infrastructure.hands.git import GitHand
hand = GitHand()
result = await hand.status()
assert result.success is True
assert result.operation == "git status --short"
@pytest.mark.asyncio
async def test_log_wrapper():
"""log() convenience wrapper should work."""
from infrastructure.hands.git import GitHand
hand = GitHand()
result = await hand.log(count=3)
assert result.success is True
assert "git log" in result.operation
@pytest.mark.asyncio
async def test_diff_wrapper():
"""diff() convenience wrapper should work."""
from infrastructure.hands.git import GitHand
hand = GitHand()
result = await hand.diff()
assert result.success is True
assert result.operation == "git diff"
@pytest.mark.asyncio
async def test_diff_staged_wrapper():
"""diff(staged=True) should pass --cached."""
from infrastructure.hands.git import GitHand
hand = GitHand()
result = await hand.diff(staged=True)
assert result.success is True
assert result.operation == "git diff --cached"
# ---------------------------------------------------------------------------
# GitResult dataclass
# ---------------------------------------------------------------------------
def test_git_result_defaults():
"""GitResult should have sensible defaults."""
from infrastructure.hands.git import GitResult
r = GitResult(operation="git status", success=True)
assert r.output == ""
assert r.error == ""
assert r.latency_ms == 0.0
assert r.requires_confirmation is False
assert r.metadata == {}
# ---------------------------------------------------------------------------
# Info summary
# ---------------------------------------------------------------------------
def test_info_returns_summary():
"""info() should return a dict with repo_dir and timeout."""
from infrastructure.hands.git import GitHand
hand = GitHand()
i = hand.info()
assert "repo_dir" in i
assert "default_timeout" in i
# ---------------------------------------------------------------------------
# Tool registration
# ---------------------------------------------------------------------------
def test_persona_hand_map():
"""Forge and Helm should have both shell and git access."""
from infrastructure.hands.tools import get_local_hands_for_persona
forge_hands = get_local_hands_for_persona("forge")
assert "hand_shell" in forge_hands
assert "hand_git" in forge_hands
helm_hands = get_local_hands_for_persona("helm")
assert "hand_shell" in helm_hands
assert "hand_git" in helm_hands
# Echo only gets git
echo_hands = get_local_hands_for_persona("echo")
assert "hand_git" in echo_hands
assert "hand_shell" not in echo_hands
# Quill gets neither
quill_hands = get_local_hands_for_persona("quill")
assert quill_hands == []