feat(04-03): add new-project callback support to handle_callback
- Add new-project action handling in handle_callback - Supports Create New Project button from error cards - Updates user context when project is created via callback - Add 4 new tests in TestHandleCallback for new-project action Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -323,22 +323,38 @@ class Orchestrator:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def handle_callback(self, callback_data: Dict[str, Any]) -> Dict[str, Any]:
|
def handle_callback(self, callback_data: Dict[str, Any]) -> Dict[str, Any]:
|
||||||
"""Handle a button callback from an approval card.
|
"""Handle a button callback from a card.
|
||||||
|
|
||||||
This processes approval/reject actions and sends the
|
This processes various actions including:
|
||||||
response to the tmux session.
|
- approve/reject: For approval workflows
|
||||||
|
- new-project: For creating new projects from error cards
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
callback_data: Dictionary containing:
|
callback_data: Dictionary containing:
|
||||||
- action: "approve" or "reject"
|
- action: "approve", "reject", or "new-project"
|
||||||
- approval_id: The ID of the approval
|
- For approvals: approval_id, project
|
||||||
- project: The project name
|
- For new-project: name, user_id
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Feishu Card JSON dict as confirmation response.
|
Feishu Card JSON dict as confirmation response.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
action = callback_data.get("action", "")
|
action = callback_data.get("action", "")
|
||||||
|
|
||||||
|
# Handle new-project action from error card button
|
||||||
|
if action == "new-project":
|
||||||
|
project_name = callback_data.get("name", "")
|
||||||
|
user_id = callback_data.get("user_id", "")
|
||||||
|
|
||||||
|
if not project_name:
|
||||||
|
return self.formatter.format_error(
|
||||||
|
title="Invalid Request",
|
||||||
|
content="Project name is required"
|
||||||
|
)
|
||||||
|
|
||||||
|
return self._handle_new_project(user_id, project_name)
|
||||||
|
|
||||||
|
# Handle approval/reject actions
|
||||||
approval_id = callback_data.get("approval_id", "")
|
approval_id = callback_data.get("approval_id", "")
|
||||||
project_name = callback_data.get("project", "default")
|
project_name = callback_data.get("project", "default")
|
||||||
|
|
||||||
|
|||||||
@@ -340,6 +340,103 @@ class TestHandleCallback:
|
|||||||
# Verify error card was returned
|
# Verify error card was returned
|
||||||
mock_orchestrator.formatter.format_error.assert_called_once()
|
mock_orchestrator.formatter.format_error.assert_called_once()
|
||||||
|
|
||||||
|
def test_handle_callback_new_project_action(self, mock_orchestrator):
|
||||||
|
"""handle_callback should handle new-project action from error card button."""
|
||||||
|
from gsd_openclaw.tmux_session import TmuxSession
|
||||||
|
|
||||||
|
mock_session = MagicMock(spec=TmuxSession)
|
||||||
|
mock_session.session_id = "test-123"
|
||||||
|
mock_session.tmux_session = MagicMock()
|
||||||
|
mock_orchestrator.tmux_manager.create_session.return_value = mock_session
|
||||||
|
mock_orchestrator.tmux_manager.get_session.return_value = None
|
||||||
|
|
||||||
|
mock_orchestrator.executor.execute.return_value = CommandResult(
|
||||||
|
output="Project created successfully",
|
||||||
|
completed=True,
|
||||||
|
has_error=False,
|
||||||
|
error_type=None
|
||||||
|
)
|
||||||
|
mock_orchestrator.formatter.format_success.return_value = {"card": "success"}
|
||||||
|
|
||||||
|
result = mock_orchestrator.handle_callback({
|
||||||
|
"action": "new-project",
|
||||||
|
"name": "my-new-project",
|
||||||
|
"user_id": "user1"
|
||||||
|
})
|
||||||
|
|
||||||
|
# Verify session was created
|
||||||
|
mock_orchestrator.tmux_manager.create_session.assert_called_once_with("my-new-project")
|
||||||
|
# Verify command was executed
|
||||||
|
mock_orchestrator.executor.execute.assert_called_once()
|
||||||
|
# Verify success card was returned
|
||||||
|
mock_orchestrator.formatter.format_success.assert_called_once()
|
||||||
|
|
||||||
|
def test_handle_callback_new_project_returns_success_card(self, mock_orchestrator):
|
||||||
|
"""handle_callback new-project action should return success card."""
|
||||||
|
from gsd_openclaw.tmux_session import TmuxSession
|
||||||
|
|
||||||
|
mock_session = MagicMock(spec=TmuxSession)
|
||||||
|
mock_session.session_id = "test-123"
|
||||||
|
mock_session.tmux_session = MagicMock()
|
||||||
|
mock_orchestrator.tmux_manager.create_session.return_value = mock_session
|
||||||
|
mock_orchestrator.tmux_manager.get_session.return_value = None
|
||||||
|
|
||||||
|
mock_orchestrator.executor.execute.return_value = CommandResult(
|
||||||
|
output="Project initialized",
|
||||||
|
completed=True,
|
||||||
|
has_error=False,
|
||||||
|
error_type=None
|
||||||
|
)
|
||||||
|
mock_orchestrator.formatter.format_success.return_value = {"card": "success"}
|
||||||
|
|
||||||
|
result = mock_orchestrator.handle_callback({
|
||||||
|
"action": "new-project",
|
||||||
|
"name": "test-project",
|
||||||
|
"user_id": "user1"
|
||||||
|
})
|
||||||
|
|
||||||
|
mock_orchestrator.formatter.format_success.assert_called_once()
|
||||||
|
|
||||||
|
def test_handle_callback_new_project_with_missing_name_returns_error(self, mock_orchestrator):
|
||||||
|
"""handle_callback new-project with missing name should return error."""
|
||||||
|
mock_orchestrator.formatter.format_error.return_value = {"card": "error"}
|
||||||
|
|
||||||
|
result = mock_orchestrator.handle_callback({
|
||||||
|
"action": "new-project",
|
||||||
|
"user_id": "user1"
|
||||||
|
# Missing "name" field
|
||||||
|
})
|
||||||
|
|
||||||
|
mock_orchestrator.formatter.format_error.assert_called_once()
|
||||||
|
|
||||||
|
def test_handle_callback_new_project_updates_user_context(self, mock_orchestrator):
|
||||||
|
"""handle_callback new-project should update user context."""
|
||||||
|
from gsd_openclaw.tmux_session import TmuxSession
|
||||||
|
|
||||||
|
mock_session = MagicMock(spec=TmuxSession)
|
||||||
|
mock_session.session_id = "test-123"
|
||||||
|
mock_session.tmux_session = MagicMock()
|
||||||
|
mock_orchestrator.tmux_manager.create_session.return_value = mock_session
|
||||||
|
mock_orchestrator.tmux_manager.get_session.return_value = None
|
||||||
|
|
||||||
|
mock_orchestrator.executor.execute.return_value = CommandResult(
|
||||||
|
output="Project created",
|
||||||
|
completed=True,
|
||||||
|
has_error=False,
|
||||||
|
error_type=None
|
||||||
|
)
|
||||||
|
mock_orchestrator.formatter.format_success.return_value = {"card": "success"}
|
||||||
|
|
||||||
|
result = mock_orchestrator.handle_callback({
|
||||||
|
"action": "new-project",
|
||||||
|
"name": "context-project",
|
||||||
|
"user_id": "user1"
|
||||||
|
})
|
||||||
|
|
||||||
|
# Verify user context was updated
|
||||||
|
user_ctx = mock_orchestrator._get_user_context_manager("user1")
|
||||||
|
assert user_ctx.get_current_project() == "context-project"
|
||||||
|
|
||||||
|
|
||||||
class TestIntegrationFlows:
|
class TestIntegrationFlows:
|
||||||
"""Integration-style tests for complete flows."""
|
"""Integration-style tests for complete flows."""
|
||||||
|
|||||||
Reference in New Issue
Block a user