This repository has been archived on 2026-08-07. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
kb-skills/gsd-openclaw/tests/test_cli.py
starmen1265 96f06f3751
PR Test / lint (pull_request) Successful in 2s
feat: 添加 gsd-openclaw 技能
2026-03-11 10:41:01 +08:00

311 lines
11 KiB
Python

"""Tests for CLI entry point."""
import pytest
import sys
from unittest.mock import MagicMock, patch
from io import StringIO
from gsd_openclaw.tmux_session import TmuxSession, TmuxSessionError, CleanupResult, SessionInfo
from gsd_openclaw.cli import main, cmd_create, cmd_list, cmd_destroy, cmd_resume, cmd_cleanup
class TestCliCreate:
"""Tests for CLI create command."""
def test_cli_create_creates_session(self):
"""`gsd-ocl create <project>` creates session."""
mock_session = MagicMock()
mock_session.tmux_session_name = "gsd-test-project"
mock_session.session_id = "test-123"
mock_session.project_name = "test-project"
with patch('gsd_openclaw.cli.TmuxSessionManager') as MockManager:
mock_manager = MagicMock()
mock_manager.create_session.return_value = mock_session
MockManager.return_value = mock_manager
with patch.object(sys, 'argv', ['gsd-ocl', 'create', 'test-project']):
main()
mock_manager.create_session.assert_called_once_with(
project_name='test-project',
display_name=None
)
def test_cli_create_with_display_name(self):
"""`gsd-ocl create <project> --display-name` sets display name."""
mock_session = MagicMock()
mock_session.tmux_session_name = "gsd-test-project"
mock_session.session_id = "test-123"
mock_session.project_name = "test-project"
with patch('gsd_openclaw.cli.TmuxSessionManager') as MockManager:
mock_manager = MagicMock()
mock_manager.create_session.return_value = mock_session
MockManager.return_value = mock_manager
with patch.object(sys, 'argv', ['gsd-ocl', 'create', 'test-project', '-d', 'My Project']):
main()
mock_manager.create_session.assert_called_once_with(
project_name='test-project',
display_name='My Project'
)
def test_cli_create_exits_on_error(self):
"""CLI exits with code 1 on error."""
with patch('gsd_openclaw.cli.TmuxSessionManager') as MockManager:
mock_manager = MagicMock()
mock_manager.create_session.side_effect = Exception("Create failed")
MockManager.return_value = mock_manager
with patch.object(sys, 'argv', ['gsd-ocl', 'create', 'test-project']):
with pytest.raises(SystemExit) as exc_info:
main()
assert exc_info.value.code == 1
class TestCliList:
"""Tests for CLI list command."""
def test_cli_list_shows_sessions(self):
"""`gsd-ocl list` shows sessions."""
mock_session_info = SessionInfo(
tmux_name="gsd-test-project",
project_name="test-project",
session_id="test-123",
created_at="2026-03-10T10:00:00",
last_active_at="2026-03-10T12:00:00",
command_count=5,
display_name="Test Project",
is_orphan=False
)
with patch('gsd_openclaw.cli.TmuxSessionManager') as MockManager:
mock_manager = MagicMock()
mock_manager.list_gsd_sessions.return_value = [mock_session_info]
MockManager.return_value = mock_manager
stdout = StringIO()
with patch.object(sys, 'argv', ['gsd-ocl', 'list']):
with patch('sys.stdout', stdout):
main()
output = stdout.getvalue()
assert "test-project" in output or "Test Project" in output
assert "test-123" in output
assert "active" in output
def test_cli_list_shows_no_sessions_message(self):
"""`gsd-ocl list` shows message when no sessions."""
with patch('gsd_openclaw.cli.TmuxSessionManager') as MockManager:
mock_manager = MagicMock()
mock_manager.list_gsd_sessions.return_value = []
MockManager.return_value = mock_manager
stdout = StringIO()
with patch.object(sys, 'argv', ['gsd-ocl', 'list']):
with patch('sys.stdout', stdout):
main()
output = stdout.getvalue()
assert "No active sessions" in output
def test_cli_list_shows_orphan_status(self):
"""`gsd-ocl list` shows ORPHAN status for orphan sessions."""
mock_session_info = SessionInfo(
tmux_name="gsd-orphan",
project_name="orphan",
session_id="?",
created_at="?",
last_active_at="?",
command_count=0,
display_name=None,
is_orphan=True
)
with patch('gsd_openclaw.cli.TmuxSessionManager') as MockManager:
mock_manager = MagicMock()
mock_manager.list_gsd_sessions.return_value = [mock_session_info]
MockManager.return_value = mock_manager
stdout = StringIO()
with patch.object(sys, 'argv', ['gsd-ocl', 'list']):
with patch('sys.stdout', stdout):
main()
output = stdout.getvalue()
assert "ORPHAN" in output
class TestCliDestroy:
"""Tests for CLI destroy command."""
def test_cli_destroy_removes_session(self):
"""`gsd-ocl destroy <project>` removes session."""
with patch('gsd_openclaw.cli.TmuxSessionManager') as MockManager:
mock_manager = MagicMock()
mock_manager.destroy_session.return_value = True
MockManager.return_value = mock_manager
with patch.object(sys, 'argv', ['gsd-ocl', 'destroy', 'test-project']):
main()
mock_manager.destroy_session.assert_called_once_with(
project_name='test-project',
cleanup_workspace=False
)
def test_cli_destroy_with_cleanup(self):
"""`gsd-ocl destroy <project> --cleanup` removes workspace too."""
with patch('gsd_openclaw.cli.TmuxSessionManager') as MockManager:
mock_manager = MagicMock()
mock_manager.destroy_session.return_value = True
MockManager.return_value = mock_manager
with patch.object(sys, 'argv', ['gsd-ocl', 'destroy', 'test-project', '-c']):
main()
mock_manager.destroy_session.assert_called_once_with(
project_name='test-project',
cleanup_workspace=True
)
def test_cli_destroy_exits_if_not_found(self):
"""`gsd-ocl destroy` exits with code 1 if session not found."""
with patch('gsd_openclaw.cli.TmuxSessionManager') as MockManager:
mock_manager = MagicMock()
mock_manager.destroy_session.return_value = False
MockManager.return_value = mock_manager
with patch.object(sys, 'argv', ['gsd-ocl', 'destroy', 'nonexistent']):
with pytest.raises(SystemExit) as exc_info:
main()
assert exc_info.value.code == 1
class TestCliResume:
"""Tests for CLI resume command."""
def test_cli_resume_resumes_session(self):
"""`gsd-ocl resume <project>` resumes session."""
mock_session = MagicMock()
mock_session.tmux_session_name = "gsd-test-project"
mock_session.session_id = "test-123"
mock_session.project_name = "test-project"
with patch('gsd_openclaw.cli.TmuxSessionManager') as MockManager:
mock_manager = MagicMock()
mock_manager.resume_session.return_value = mock_session
MockManager.return_value = mock_manager
with patch.object(sys, 'argv', ['gsd-ocl', 'resume', 'test-project']):
main()
mock_manager.resume_session.assert_called_once_with(project_name='test-project')
def test_cli_resume_exits_on_error(self):
"""`gsd-ocl resume` exits with code 1 on error."""
with patch('gsd_openclaw.cli.TmuxSessionManager') as MockManager:
mock_manager = MagicMock()
mock_manager.resume_session.side_effect = TmuxSessionError("No session found")
MockManager.return_value = mock_manager
with patch.object(sys, 'argv', ['gsd-ocl', 'resume', 'nonexistent']):
with pytest.raises(SystemExit) as exc_info:
main()
assert exc_info.value.code == 1
class TestCliCleanup:
"""Tests for CLI cleanup command."""
def test_cli_cleanup_removes_orphans(self):
"""`gsd-ocl cleanup` removes orphan sessions."""
result = CleanupResult(
cleaned_sessions=["gsd-orphan1", "gsd-orphan2"],
errors=[]
)
with patch('gsd_openclaw.cli.TmuxSessionManager') as MockManager:
mock_manager = MagicMock()
mock_manager.cleanup_orphans.return_value = result
MockManager.return_value = mock_manager
stdout = StringIO()
with patch.object(sys, 'argv', ['gsd-ocl', 'cleanup']):
with patch('sys.stdout', stdout):
main()
output = stdout.getvalue()
assert "2" in output
assert "gsd-orphan1" in output
assert "gsd-orphan2" in output
def test_cli_cleanup_shows_errors(self):
"""`gsd-ocl cleanup` shows errors."""
result = CleanupResult(
cleaned_sessions=[],
errors=["Failed to kill gsd-failing: error"]
)
with patch('gsd_openclaw.cli.TmuxSessionManager') as MockManager:
mock_manager = MagicMock()
mock_manager.cleanup_orphans.return_value = result
MockManager.return_value = mock_manager
stdout = StringIO()
with patch.object(sys, 'argv', ['gsd-ocl', 'cleanup']):
with patch('sys.stdout', stdout):
main()
output = stdout.getvalue()
assert "error" in output.lower() or "Error" in output
def test_cli_cleanup_no_orphans_found(self):
"""`gsd-ocl cleanup` shows message when no orphans found."""
result = CleanupResult(
cleaned_sessions=[],
errors=[]
)
with patch('gsd_openclaw.cli.TmuxSessionManager') as MockManager:
mock_manager = MagicMock()
mock_manager.cleanup_orphans.return_value = result
MockManager.return_value = mock_manager
stdout = StringIO()
with patch.object(sys, 'argv', ['gsd-ocl', 'cleanup']):
with patch('sys.stdout', stdout):
main()
output = stdout.getvalue()
assert "No orphan sessions found" in output
class TestCliHelp:
"""Tests for CLI help."""
def test_cli_help_works(self):
"""`gsd-ocl --help` shows help message."""
stdout = StringIO()
with patch.object(sys, 'argv', ['gsd-ocl', '--help']):
with patch('sys.stdout', stdout):
with pytest.raises(SystemExit) as exc_info:
main()
# --help exits with code 0
assert exc_info.value.code == 0
output = stdout.getvalue()
assert "gsd-ocl" in output
def test_cli_requires_subcommand(self):
"""CLI requires a subcommand."""
with patch.object(sys, 'argv', ['gsd-ocl']):
with pytest.raises(SystemExit) as exc_info:
main()
assert exc_info.value.code != 0