Fix typing: make Message a dict instead of dict | str (#13144)

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
Rohit Malhotra
2026-03-02 19:30:22 -05:00
committed by GitHub
parent 17347a95f8
commit f8bbd352a9
21 changed files with 151 additions and 163 deletions

View File

@@ -200,22 +200,26 @@ class GithubManager(Manager):
self._add_reaction(github_view, 'eyes', installation_token)
await self.start_job(github_view)
async def send_message(self, message: Message, github_view: ResolverViewInterface):
installation_token = await self.token_manager.load_org_token(
async def send_message(self, message: str, github_view: ResolverViewInterface):
"""Send a message to GitHub.
Args:
message: The message content to send (plain text string)
github_view: The GitHub view object containing issue/PR/comment info
"""
installation_token = self.token_manager.load_org_token(
github_view.installation_id
)
if not installation_token:
logger.warning('Missing installation token')
return
outgoing_message = message.message
if isinstance(github_view, GithubInlinePRComment):
with Github(auth=Auth.Token(installation_token)) as github_client:
repo = github_client.get_repo(github_view.full_repo_name)
pr = repo.get_pull(github_view.issue_number)
pr.create_review_comment_reply(
comment_id=github_view.comment_id, body=outgoing_message
comment_id=github_view.comment_id, body=message
)
elif (
@@ -226,7 +230,7 @@ class GithubManager(Manager):
with Github(auth=Auth.Token(installation_token)) as github_client:
repo = github_client.get_repo(github_view.full_repo_name)
issue = repo.get_issue(number=github_view.issue_number)
issue.create_comment(outgoing_message)
issue.create_comment(message)
else:
logger.warning('Unsupported location')
@@ -245,7 +249,7 @@ class GithubManager(Manager):
)
try:
msg_info = None
msg_info: str = ''
try:
user_info = github_view.user_info
@@ -361,15 +365,13 @@ class GithubManager(Manager):
msg_info = get_session_expired_message(user_info.username)
msg = self.create_outgoing_message(msg_info)
await self.send_message(msg, github_view)
await self.send_message(msg_info, github_view)
except Exception:
logger.exception('[Github]: Error starting job')
msg = self.create_outgoing_message(
msg='Uh oh! There was an unexpected error starting the job :('
await self.send_message(
'Uh oh! There was an unexpected error starting the job :(', github_view
)
await self.send_message(msg, github_view)
try:
await self.data_collector.save_data(github_view)

View File

@@ -121,12 +121,11 @@ class GitlabManager(Manager):
# Check if the user has write access to the repository
return has_write_access
async def send_message(self, message: Message, gitlab_view: ResolverViewInterface):
"""
Send a message to GitLab based on the view type.
async def send_message(self, message: str, gitlab_view: ResolverViewInterface):
"""Send a message to GitLab based on the view type.
Args:
message: The message to send
message: The message content to send (plain text string)
gitlab_view: The GitLab view object containing issue/PR/comment info
"""
keycloak_user_id = gitlab_view.user_info.keycloak_user_id
@@ -138,8 +137,6 @@ class GitlabManager(Manager):
external_auth_id=keycloak_user_id
)
outgoing_message = message.message
if isinstance(gitlab_view, GitlabInlineMRComment) or isinstance(
gitlab_view, GitlabMRComment
):
@@ -147,7 +144,7 @@ class GitlabManager(Manager):
gitlab_view.project_id,
gitlab_view.issue_number,
gitlab_view.discussion_id,
message.message,
message,
)
elif isinstance(gitlab_view, GitlabIssueComment):
@@ -155,14 +152,14 @@ class GitlabManager(Manager):
gitlab_view.project_id,
gitlab_view.issue_number,
gitlab_view.discussion_id,
outgoing_message,
message,
)
elif isinstance(gitlab_view, GitlabIssue):
await gitlab_service.reply_to_issue(
gitlab_view.project_id,
gitlab_view.issue_number,
None, # no discussion id, issue is tagged
outgoing_message,
message,
)
else:
logger.warning(
@@ -262,12 +259,10 @@ class GitlabManager(Manager):
msg_info = get_session_expired_message(user_info.username)
# Send the acknowledgment message
msg = self.create_outgoing_message(msg_info)
await self.send_message(msg, gitlab_view)
await self.send_message(msg_info, gitlab_view)
except Exception as e:
logger.exception(f'[GitLab] Error starting job: {str(e)}')
msg = self.create_outgoing_message(
msg='Uh oh! There was an unexpected error starting the job :('
await self.send_message(
'Uh oh! There was an unexpected error starting the job :(', gitlab_view
)
await self.send_message(msg, gitlab_view)

View File

@@ -341,17 +341,25 @@ class JiraManager(Manager):
async def send_message(
self,
message: Message,
message: str,
issue_key: str,
jira_cloud_id: str,
svc_acc_email: str,
svc_acc_api_key: str,
):
"""Send a comment to a Jira issue."""
"""Send a comment to a Jira issue.
Args:
message: The message content to send (plain text string)
issue_key: The Jira issue key (e.g., 'PROJ-123')
jira_cloud_id: The Jira Cloud ID
svc_acc_email: Service account email for authentication
svc_acc_api_key: Service account API key for authentication
"""
url = (
f'{JIRA_CLOUD_API_URL}/{jira_cloud_id}/rest/api/2/issue/{issue_key}/comment'
)
data = {'body': message.message}
data = {'body': message}
async with httpx.AsyncClient(verify=httpx_verify_option()) as client:
response = await client.post(
url, auth=(svc_acc_email, svc_acc_api_key), json=data
@@ -366,7 +374,7 @@ class JiraManager(Manager):
view.jira_workspace.svc_acc_api_key
)
await self.send_message(
self.create_outgoing_message(msg=msg),
msg,
issue_key=view.payload.issue_key,
jira_cloud_id=view.jira_workspace.jira_cloud_id,
svc_acc_email=view.jira_workspace.svc_acc_email,
@@ -388,7 +396,7 @@ class JiraManager(Manager):
try:
api_key = self.token_manager.decrypt_text(workspace.svc_acc_api_key)
await self.send_message(
self.create_outgoing_message(msg=error_msg),
error_msg,
issue_key=payload.issue_key,
jira_cloud_id=workspace.jira_cloud_id,
svc_acc_email=workspace.svc_acc_email,

View File

@@ -418,7 +418,7 @@ class JiraDcManager(Manager):
jira_dc_view.jira_dc_workspace.svc_acc_api_key
)
await self.send_message(
self.create_outgoing_message(msg=msg_info),
msg_info,
issue_key=jira_dc_view.job_context.issue_key,
base_api_url=jira_dc_view.job_context.base_api_url,
svc_acc_api_key=api_key,
@@ -456,12 +456,19 @@ class JiraDcManager(Manager):
return title, description
async def send_message(
self, message: Message, issue_key: str, base_api_url: str, svc_acc_api_key: str
self, message: str, issue_key: str, base_api_url: str, svc_acc_api_key: str
):
"""Send message/comment to Jira DC issue."""
"""Send message/comment to Jira DC issue.
Args:
message: The message content to send (plain text string)
issue_key: The Jira issue key (e.g., 'PROJ-123')
base_api_url: The base API URL for the Jira DC instance
svc_acc_api_key: Service account API key for authentication
"""
url = f'{base_api_url}/rest/api/2/issue/{issue_key}/comment'
headers = {'Authorization': f'Bearer {svc_acc_api_key}'}
data = {'body': message.message}
data = {'body': message}
async with httpx.AsyncClient(verify=httpx_verify_option()) as client:
response = await client.post(url, headers=headers, json=data)
response.raise_for_status()
@@ -481,7 +488,7 @@ class JiraDcManager(Manager):
try:
api_key = self.token_manager.decrypt_text(workspace.svc_acc_api_key)
await self.send_message(
self.create_outgoing_message(msg=error_msg),
error_msg,
issue_key=job_context.issue_key,
base_api_url=job_context.base_api_url,
svc_acc_api_key=api_key,
@@ -502,7 +509,7 @@ class JiraDcManager(Manager):
)
await self.send_message(
self.create_outgoing_message(msg=comment_msg),
comment_msg,
issue_key=jira_dc_view.job_context.issue_key,
base_api_url=jira_dc_view.job_context.base_api_url,
svc_acc_api_key=api_key,

View File

@@ -408,7 +408,7 @@ class LinearManager(Manager):
linear_view.linear_workspace.svc_acc_api_key
)
await self.send_message(
self.create_outgoing_message(msg=msg_info),
msg_info,
linear_view.job_context.issue_id,
api_key,
)
@@ -473,8 +473,14 @@ class LinearManager(Manager):
return title, description
async def send_message(self, message: Message, issue_id: str, api_key: str):
"""Send message/comment to Linear issue."""
async def send_message(self, message: str, issue_id: str, api_key: str):
"""Send message/comment to Linear issue.
Args:
message: The message content to send (plain text string)
issue_id: The Linear issue ID to comment on
api_key: The Linear API key for authentication
"""
query = """
mutation CommentCreate($input: CommentCreateInput!) {
commentCreate(input: $input) {
@@ -485,7 +491,7 @@ class LinearManager(Manager):
}
}
"""
variables = {'input': {'issueId': issue_id, 'body': message.message}}
variables = {'input': {'issueId': issue_id, 'body': message}}
return await self._query_api(query, variables, api_key)
async def _send_error_comment(
@@ -498,9 +504,7 @@ class LinearManager(Manager):
try:
api_key = self.token_manager.decrypt_text(workspace.svc_acc_api_key)
await self.send_message(
self.create_outgoing_message(msg=error_msg), issue_id, api_key
)
await self.send_message(error_msg, issue_id, api_key)
except Exception as e:
logger.error(f'[Linear] Failed to send error comment: {str(e)}')
@@ -517,7 +521,7 @@ class LinearManager(Manager):
)
await self.send_message(
self.create_outgoing_message(msg=comment_msg),
comment_msg,
linear_view.job_context.issue_id,
api_key,
)

View File

@@ -1,4 +1,5 @@
from abc import ABC, abstractmethod
from typing import Any
from integrations.models import Message, SourceType
@@ -12,14 +13,15 @@ class Manager(ABC):
raise NotImplementedError
@abstractmethod
def send_message(self, message: Message):
"Send message to integration from Openhands server"
def send_message(self, message: str, *args: Any, **kwargs: Any):
"""Send message to integration from OpenHands server.
Args:
message: The message content to send (plain text string).
"""
raise NotImplementedError
@abstractmethod
def start_job(self):
"Kick off a job with openhands agent"
raise NotImplementedError
def create_outgoing_message(self, msg: str | dict, ephemeral: bool = False):
return Message(source=SourceType.OPENHANDS, message=msg, ephemeral=ephemeral)

View File

@@ -1,4 +1,5 @@
from enum import Enum
from typing import Any
from pydantic import BaseModel
@@ -16,8 +17,16 @@ class SourceType(str, Enum):
class Message(BaseModel):
"""Message model for incoming webhook payloads from integrations.
Note: This model is intended for INCOMING messages only.
For outgoing messages (e.g., sending comments to GitHub/GitLab),
pass strings directly to the send_message methods instead of
wrapping them in a Message object.
"""
source: SourceType
message: str | dict
message: dict[str, Any]
ephemeral: bool = False

View File

@@ -1,4 +1,5 @@
import re
from typing import Any
import jwt
from integrations.manager import Manager
@@ -202,9 +203,7 @@ class SlackManager(Manager):
msg = self.login_link.format(link)
logger.info('slack_not_yet_authenticated')
await self.send_message(
self.create_outgoing_message(msg, ephemeral=True), slack_view
)
await self.send_message(msg, slack_view, ephemeral=True)
return
if not await self.is_job_requested(message, slack_view):
@@ -212,27 +211,40 @@ class SlackManager(Manager):
await self.start_job(slack_view)
async def send_message(self, message: Message, slack_view: SlackViewInterface):
async def send_message(
self,
message: str | dict[str, Any],
slack_view: SlackViewInterface,
ephemeral: bool = False,
):
"""Send a message to Slack.
Args:
message: The message content. Can be a string (for simple text) or
a dict with 'text' and 'blocks' keys (for structured messages).
slack_view: The Slack view object containing channel/thread info.
ephemeral: If True, send as an ephemeral message visible only to the user.
"""
client = AsyncWebClient(token=slack_view.bot_access_token)
if message.ephemeral and isinstance(message.message, str):
if ephemeral and isinstance(message, str):
await client.chat_postEphemeral(
channel=slack_view.channel_id,
markdown_text=message.message,
markdown_text=message,
user=slack_view.slack_user_id,
thread_ts=slack_view.thread_ts,
)
elif message.ephemeral and isinstance(message.message, dict):
elif ephemeral and isinstance(message, dict):
await client.chat_postEphemeral(
channel=slack_view.channel_id,
user=slack_view.slack_user_id,
thread_ts=slack_view.thread_ts,
text=message.message['text'],
blocks=message.message['blocks'],
text=message['text'],
blocks=message['blocks'],
)
else:
await client.chat_postMessage(
channel=slack_view.channel_id,
markdown_text=message.message,
markdown_text=message,
thread_ts=slack_view.message_ts,
)
@@ -279,10 +291,7 @@ class SlackManager(Manager):
repos, slack_view.message_ts, slack_view.thread_ts
),
}
await self.send_message(
self.create_outgoing_message(repo_selection_msg, ephemeral=True),
slack_view,
)
await self.send_message(repo_selection_msg, slack_view, ephemeral=True)
return False
@@ -368,9 +377,10 @@ class SlackManager(Manager):
except StartingConvoException as e:
msg_info = str(e)
await self.send_message(self.create_outgoing_message(msg_info), slack_view)
await self.send_message(msg_info, slack_view)
except Exception:
logger.exception('[Slack]: Error starting job')
msg = 'Uh oh! There was an unexpected error starting the job :('
await self.send_message(self.create_outgoing_message(msg), slack_view)
await self.send_message(
'Uh oh! There was an unexpected error starting the job :(', slack_view
)

View File

@@ -3,7 +3,6 @@ from datetime import datetime
from integrations.github.github_manager import GithubManager
from integrations.github.github_view import GithubViewType
from integrations.models import Message, SourceType
from integrations.utils import (
extract_summary_from_conversation_manager,
get_summary_instruction,
@@ -35,16 +34,12 @@ class GithubCallbackProcessor(ConversationCallbackProcessor):
send_summary_instruction: bool = True
async def _send_message_to_github(self, message: str) -> None:
"""
Send a message to GitHub.
"""Send a message to GitHub.
Args:
message: The message content to send to GitHub
"""
try:
# Create a message object for GitHub
message_obj = Message(source=SourceType.OPENHANDS, message=message)
# Get the token manager
token_manager = TokenManager()
@@ -53,8 +48,8 @@ class GithubCallbackProcessor(ConversationCallbackProcessor):
github_manager = GithubManager(token_manager, GitHubDataCollector())
# Send the message
await github_manager.send_message(message_obj, self.github_view)
# Send the message directly as a string
await github_manager.send_message(message, self.github_view)
logger.info(
f'[GitHub] Sent summary message to {self.github_view.full_repo_name}#{self.github_view.issue_number}'

View File

@@ -3,7 +3,6 @@ from datetime import datetime
from integrations.gitlab.gitlab_manager import GitlabManager
from integrations.gitlab.gitlab_view import GitlabViewType
from integrations.models import Message, SourceType
from integrations.utils import (
extract_summary_from_conversation_manager,
get_summary_instruction,
@@ -28,8 +27,7 @@ gitlab_manager = GitlabManager(token_manager)
class GitlabCallbackProcessor(ConversationCallbackProcessor):
"""
Processor for sending conversation summaries to GitLab.
"""Processor for sending conversation summaries to GitLab.
This processor is used to send summaries of conversations to GitLab
when agent state changes occur.
@@ -39,22 +37,18 @@ class GitlabCallbackProcessor(ConversationCallbackProcessor):
send_summary_instruction: bool = True
async def _send_message_to_gitlab(self, message: str) -> None:
"""
Send a message to GitLab.
"""Send a message to GitLab.
Args:
message: The message content to send to GitLab
"""
try:
# Create a message object for GitHub
message_obj = Message(source=SourceType.OPENHANDS, message=message)
# Get the token manager
token_manager = TokenManager()
gitlab_manager = GitlabManager(token_manager)
# Send the message
await gitlab_manager.send_message(message_obj, self.gitlab_view)
# Send the message directly as a string
await gitlab_manager.send_message(message, self.gitlab_view)
logger.info(
f'[GitLab] Sent summary message to {self.gitlab_view.full_repo_name}#{self.gitlab_view.issue_number}'

View File

@@ -37,8 +37,7 @@ class JiraCallbackProcessor(ConversationCallbackProcessor):
workspace_name: str
async def _send_comment_to_jira(self, message: str) -> None:
"""
Send a comment to Jira issue.
"""Send a comment to Jira issue.
Args:
message: The message content to send to Jira
@@ -59,8 +58,9 @@ class JiraCallbackProcessor(ConversationCallbackProcessor):
# Decrypt API key
api_key = jira_manager.token_manager.decrypt_text(workspace.svc_acc_api_key)
# Send comment directly as a string
await jira_manager.send_message(
jira_manager.create_outgoing_message(msg=message),
message,
issue_key=self.issue_key,
jira_cloud_id=workspace.jira_cloud_id,
svc_acc_email=workspace.svc_acc_email,

View File

@@ -37,8 +37,7 @@ class JiraDcCallbackProcessor(ConversationCallbackProcessor):
base_api_url: str
async def _send_comment_to_jira_dc(self, message: str) -> None:
"""
Send a comment to Jira DC issue.
"""Send a comment to Jira DC issue.
Args:
message: The message content to send to Jira DC
@@ -61,8 +60,9 @@ class JiraDcCallbackProcessor(ConversationCallbackProcessor):
workspace.svc_acc_api_key
)
# Send comment directly as a string
await jira_dc_manager.send_message(
jira_dc_manager.create_outgoing_message(msg=message),
message,
issue_key=self.issue_key,
base_api_url=self.base_api_url,
svc_acc_api_key=api_key,

View File

@@ -36,8 +36,7 @@ class LinearCallbackProcessor(ConversationCallbackProcessor):
workspace_name: str
async def _send_comment_to_linear(self, message: str) -> None:
"""
Send a comment to Linear issue.
"""Send a comment to Linear issue.
Args:
message: The message content to send to Linear
@@ -60,9 +59,9 @@ class LinearCallbackProcessor(ConversationCallbackProcessor):
workspace.svc_acc_api_key
)
# Send comment
# Send comment directly as a string
await linear_manager.send_message(
linear_manager.create_outgoing_message(msg=message),
message,
self.issue_id,
api_key,
)

View File

@@ -26,8 +26,7 @@ slack_manager = SlackManager(token_manager)
class SlackCallbackProcessor(ConversationCallbackProcessor):
"""
Processor for sending conversation summaries to Slack.
"""Processor for sending conversation summaries to Slack.
This processor is used to send summaries of conversations to Slack channels
when agent state changes occur.
@@ -41,14 +40,13 @@ class SlackCallbackProcessor(ConversationCallbackProcessor):
last_user_msg_id: int | None = None
async def _send_message_to_slack(self, message: str) -> None:
"""
Send a message to Slack using the conversation_manager's send_to_event_stream method.
"""Send a message to Slack.
Args:
message: The message content to send to Slack
"""
try:
# Create a message object for Slack
# Create a message object for Slack view creation (incoming message format)
message_obj = Message(
source=SourceType.SLACK,
message={
@@ -67,9 +65,8 @@ class SlackCallbackProcessor(ConversationCallbackProcessor):
slack_view = SlackFactory.create_slack_view_from_payload(
message_obj, slack_user, saas_user_auth
)
await slack_manager.send_message(
slack_manager.create_outgoing_message(message), slack_view
)
# Send the message directly as a string
await slack_manager.send_message(message, slack_view)
logger.info(
f'[Slack] Sent summary message to channel {self.channel_id} '

View File

@@ -7,7 +7,6 @@ from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from integrations.jira.jira_manager import JiraManager
from integrations.jira.jira_payload import JiraEventType, JiraWebhookPayload
from integrations.models import Message, SourceType
from openhands.server.types import (
LLMAuthenticationError,
@@ -274,9 +273,8 @@ class TestSendMessage:
return_value=mock_response
)
message = Message(source=SourceType.JIRA, message='Test message')
result = await jira_manager.send_message(
message,
'Test message',
'PROJ-123',
'cloud-123',
'service@test.com',

View File

@@ -738,7 +738,7 @@ class TestStartJob:
# Should send error message about re-login
jira_dc_manager.send_message.assert_called_once()
call_args = jira_dc_manager.send_message.call_args[0]
assert 'Please re-login' in call_args[0].message
assert 'Please re-login' in call_args[0]
@pytest.mark.asyncio
async def test_start_job_llm_authentication_error(
@@ -763,7 +763,7 @@ class TestStartJob:
# Should send error message about LLM API key
jira_dc_manager.send_message.assert_called_once()
call_args = jira_dc_manager.send_message.call_args[0]
assert 'valid LLM API key' in call_args[0].message
assert 'valid LLM API key' in call_args[0]
@pytest.mark.asyncio
async def test_start_job_session_expired_error(
@@ -788,8 +788,8 @@ class TestStartJob:
# Should send error message about session expired
jira_dc_manager.send_message.assert_called_once()
call_args = jira_dc_manager.send_message.call_args[0]
assert 'session has expired' in call_args[0].message
assert 'login again' in call_args[0].message
assert 'session has expired' in call_args[0]
assert 'login again' in call_args[0]
@pytest.mark.asyncio
async def test_start_job_unexpected_error(
@@ -814,7 +814,7 @@ class TestStartJob:
# Should send generic error message
jira_dc_manager.send_message.assert_called_once()
call_args = jira_dc_manager.send_message.call_args[0]
assert 'unexpected error' in call_args[0].message
assert 'unexpected error' in call_args[0]
@pytest.mark.asyncio
async def test_start_job_send_message_fails(
@@ -943,9 +943,8 @@ class TestSendMessage:
return_value=mock_response
)
message = Message(source=SourceType.JIRA_DC, message='Test message')
result = await jira_dc_manager.send_message(
message, 'PROJ-123', 'https://jira.company.com', 'bearer_token'
'Test message', 'PROJ-123', 'https://jira.company.com', 'bearer_token'
)
assert result == {'id': 'comment_id'}
@@ -1014,7 +1013,7 @@ class TestSendRepoSelectionComment:
jira_dc_manager.send_message.assert_called_once()
call_args = jira_dc_manager.send_message.call_args[0]
assert 'which repository to work with' in call_args[0].message
assert 'which repository to work with' in call_args[0]
@pytest.mark.asyncio
async def test_send_repo_selection_comment_send_fails(

View File

@@ -802,7 +802,7 @@ class TestStartJob:
# Should send error message about re-login
linear_manager.send_message.assert_called_once()
call_args = linear_manager.send_message.call_args[0]
assert 'Please re-login' in call_args[0].message
assert 'Please re-login' in call_args[0]
@pytest.mark.asyncio
async def test_start_job_llm_authentication_error(
@@ -828,7 +828,7 @@ class TestStartJob:
# Should send error message about LLM API key
linear_manager.send_message.assert_called_once()
call_args = linear_manager.send_message.call_args[0]
assert 'valid LLM API key' in call_args[0].message
assert 'valid LLM API key' in call_args[0]
@pytest.mark.asyncio
async def test_start_job_session_expired_error(
@@ -854,8 +854,8 @@ class TestStartJob:
# Should send error message about session expired
linear_manager.send_message.assert_called_once()
call_args = linear_manager.send_message.call_args[0]
assert 'session has expired' in call_args[0].message
assert 'login again' in call_args[0].message
assert 'session has expired' in call_args[0]
assert 'login again' in call_args[0]
@pytest.mark.asyncio
async def test_start_job_unexpected_error(
@@ -881,7 +881,7 @@ class TestStartJob:
# Should send generic error message
linear_manager.send_message.assert_called_once()
call_args = linear_manager.send_message.call_args[0]
assert 'unexpected error' in call_args[0].message
assert 'unexpected error' in call_args[0]
@pytest.mark.asyncio
async def test_start_job_send_message_fails(
@@ -1049,8 +1049,9 @@ class TestSendMessage:
linear_manager._query_api = AsyncMock(return_value=mock_response)
message = Message(source=SourceType.LINEAR, message='Test message')
result = await linear_manager.send_message(message, 'issue_id', 'api_key')
result = await linear_manager.send_message(
'Test message', 'issue_id', 'api_key'
)
assert result == mock_response
linear_manager._query_api.assert_called_once()
@@ -1114,7 +1115,7 @@ class TestSendRepoSelectionComment:
linear_manager.send_message.assert_called_once()
call_args = linear_manager.send_message.call_args[0]
assert 'which repository to work with' in call_args[0].message
assert 'which repository to work with' in call_args[0]
@pytest.mark.asyncio
async def test_send_repo_selection_comment_send_fails(

View File

@@ -34,7 +34,6 @@ async def test_send_comment_to_jira_success(mock_jira_manager, processor):
)
mock_jira_manager.token_manager.decrypt_text.return_value = 'decrypted_key'
mock_jira_manager.send_message = AsyncMock()
mock_jira_manager.create_outgoing_message.return_value = MagicMock()
# Action
await processor._send_comment_to_jira('This is a summary.')
@@ -130,7 +129,6 @@ async def test_call_sends_summary_to_jira(
return_value=mock_workspace
)
mock_jira_manager.send_message = AsyncMock()
mock_jira_manager.create_outgoing_message.return_value = MagicMock()
with patch(
'server.conversation_callback_processor.jira_callback_processor.asyncio.create_task'
@@ -200,7 +198,6 @@ async def test_send_comment_to_jira_api_error(mock_jira_manager, processor):
)
mock_jira_manager.token_manager.decrypt_text.return_value = 'decrypted_key'
mock_jira_manager.send_message = AsyncMock(side_effect=Exception('API Error'))
mock_jira_manager.create_outgoing_message.return_value = MagicMock()
# Action - should not raise exception, but handle it gracefully
await processor._send_comment_to_jira('This is a summary.')
@@ -328,18 +325,15 @@ async def test_send_comment_to_jira_message_construction(mock_jira_manager, proc
)
mock_jira_manager.token_manager.decrypt_text.return_value = 'decrypted_key'
mock_jira_manager.send_message = AsyncMock()
mock_outgoing_message = MagicMock()
mock_jira_manager.create_outgoing_message.return_value = mock_outgoing_message
test_message = 'This is a test summary message.'
# Action
await processor._send_comment_to_jira(test_message)
# Assert
mock_jira_manager.create_outgoing_message.assert_called_once_with(msg=test_message)
# Assert - send_message now receives the string directly
mock_jira_manager.send_message.assert_called_once_with(
mock_outgoing_message,
test_message,
issue_key='TEST-123',
jira_cloud_id='cloud123',
svc_acc_email='service@test.com',
@@ -386,7 +380,6 @@ async def test_call_creates_background_task_for_sending(
return_value=mock_workspace
)
mock_jira_manager.send_message = AsyncMock()
mock_jira_manager.create_outgoing_message.return_value = MagicMock()
with patch(
'server.conversation_callback_processor.jira_callback_processor.asyncio.create_task'

View File

@@ -32,7 +32,6 @@ async def test_send_comment_to_jira_dc_success(mock_jira_dc_manager, processor):
)
mock_jira_dc_manager.token_manager.decrypt_text.return_value = 'decrypted_key'
mock_jira_dc_manager.send_message = AsyncMock()
mock_jira_dc_manager.create_outgoing_message.return_value = MagicMock()
# Action
await processor._send_comment_to_jira_dc('This is a summary.')
@@ -125,7 +124,6 @@ async def test_call_sends_summary_to_jira_dc(
return_value=mock_workspace
)
mock_jira_dc_manager.send_message = AsyncMock()
mock_jira_dc_manager.create_outgoing_message.return_value = MagicMock()
with patch(
'server.conversation_callback_processor.jira_dc_callback_processor.asyncio.create_task'
@@ -200,7 +198,6 @@ async def test_send_comment_to_jira_dc_api_error(mock_jira_dc_manager, processor
)
mock_jira_dc_manager.token_manager.decrypt_text.return_value = 'decrypted_key'
mock_jira_dc_manager.send_message = AsyncMock(side_effect=Exception('API Error'))
mock_jira_dc_manager.create_outgoing_message.return_value = MagicMock()
# Action - should not raise exception, but handle it gracefully
await processor._send_comment_to_jira_dc('This is a summary.')
@@ -328,20 +325,15 @@ async def test_send_comment_to_jira_dc_message_construction(
)
mock_jira_dc_manager.token_manager.decrypt_text.return_value = 'decrypted_key'
mock_jira_dc_manager.send_message = AsyncMock()
mock_outgoing_message = MagicMock()
mock_jira_dc_manager.create_outgoing_message.return_value = mock_outgoing_message
test_message = 'This is a test summary message.'
# Action
await processor._send_comment_to_jira_dc(test_message)
# Assert
mock_jira_dc_manager.create_outgoing_message.assert_called_once_with(
msg=test_message
)
# Assert - send_message now receives the string directly
mock_jira_dc_manager.send_message.assert_called_once_with(
mock_outgoing_message,
test_message,
issue_key='TEST-123',
base_api_url='https://test-jira-dc.company.com',
svc_acc_api_key='decrypted_key',
@@ -384,7 +376,6 @@ async def test_call_creates_background_task_for_sending(
return_value=mock_workspace
)
mock_jira_dc_manager.send_message = AsyncMock()
mock_jira_dc_manager.create_outgoing_message.return_value = MagicMock()
with patch(
'server.conversation_callback_processor.jira_dc_callback_processor.asyncio.create_task'

View File

@@ -32,7 +32,6 @@ async def test_send_comment_to_linear_success(mock_linear_manager, processor):
)
mock_linear_manager.token_manager.decrypt_text.return_value = 'decrypted_key'
mock_linear_manager.send_message = AsyncMock()
mock_linear_manager.create_outgoing_message.return_value = MagicMock()
# Action
await processor._send_comment_to_linear('This is a summary.')
@@ -125,7 +124,6 @@ async def test_call_sends_summary_to_linear(
return_value=mock_workspace
)
mock_linear_manager.send_message = AsyncMock()
mock_linear_manager.create_outgoing_message.return_value = MagicMock()
with patch(
'server.conversation_callback_processor.linear_callback_processor.asyncio.create_task'
@@ -200,7 +198,6 @@ async def test_send_comment_to_linear_api_error(mock_linear_manager, processor):
)
mock_linear_manager.token_manager.decrypt_text.return_value = 'decrypted_key'
mock_linear_manager.send_message = AsyncMock(side_effect=Exception('API Error'))
mock_linear_manager.create_outgoing_message.return_value = MagicMock()
# Action - should not raise exception, but handle it gracefully
await processor._send_comment_to_linear('This is a summary.')
@@ -328,20 +325,15 @@ async def test_send_comment_to_linear_message_construction(
)
mock_linear_manager.token_manager.decrypt_text.return_value = 'decrypted_key'
mock_linear_manager.send_message = AsyncMock()
mock_outgoing_message = MagicMock()
mock_linear_manager.create_outgoing_message.return_value = mock_outgoing_message
test_message = 'This is a test summary message.'
# Action
await processor._send_comment_to_linear(test_message)
# Assert
mock_linear_manager.create_outgoing_message.assert_called_once_with(
msg=test_message
)
# Assert - send_message now receives the string directly
mock_linear_manager.send_message.assert_called_once_with(
mock_outgoing_message,
test_message,
'TEST-123', # issue_id
'decrypted_key', # api_key
)
@@ -383,7 +375,6 @@ async def test_call_creates_background_task_for_sending(
return_value=mock_workspace
)
mock_linear_manager.send_message = AsyncMock()
mock_linear_manager.create_outgoing_message.return_value = MagicMock()
with patch(
'server.conversation_callback_processor.linear_callback_processor.asyncio.create_task'

View File

@@ -234,7 +234,6 @@ class TestSlackCallbackProcessor:
mock_slack_user = MagicMock()
mock_saas_user_auth = MagicMock()
mock_slack_view = MagicMock()
mock_outgoing_message = MagicMock()
# Mock the authenticate_user method on slack_manager
mock_slack_manager.authenticate_user = AsyncMock(
@@ -248,9 +247,6 @@ class TestSlackCallbackProcessor:
mock_slack_factory.create_slack_view_from_payload.return_value = (
mock_slack_view
)
mock_slack_manager.create_outgoing_message.return_value = (
mock_outgoing_message
)
mock_slack_manager.send_message = AsyncMock()
# Call the method
@@ -283,12 +279,9 @@ class TestSlackCallbackProcessor:
)
assert message_call.message['team_id'] == slack_callback_processor.team_id
# Verify the slack manager methods were called correctly
mock_slack_manager.create_outgoing_message.assert_called_once_with(
'Test message'
)
# Verify send_message was called with the string directly
mock_slack_manager.send_message.assert_called_once_with(
mock_outgoing_message, mock_slack_view
'Test message', mock_slack_view
)
@patch('server.conversation_callback_processor.slack_callback_processor.logger')