mirror of
https://github.com/open-jarvis/OpenJarvis.git
synced 2026-07-28 14:07:55 +00:00
196 lines
6.2 KiB
Python
196 lines
6.2 KiB
Python
"""Tests for the GmailChannel adapter."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import base64
|
|
import os
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from openjarvis.channels._stubs import ChannelStatus
|
|
from openjarvis.channels.gmail import GmailChannel
|
|
from openjarvis.core.events import EventBus, EventType
|
|
from openjarvis.core.registry import ChannelRegistry
|
|
from tests.channels.channel_test_helpers import make_common_channel_tests
|
|
|
|
|
|
@pytest.fixture(autouse=True)
|
|
def _register_gmail():
|
|
"""Re-register after any registry clear."""
|
|
if not ChannelRegistry.contains("gmail"):
|
|
ChannelRegistry.register_value("gmail", GmailChannel)
|
|
|
|
|
|
TestCommonChannel = make_common_channel_tests(GmailChannel, "gmail")
|
|
# Gmail overrides list_channels() to return ["inbox"], so remove the
|
|
# generic assertion and keep the channel-specific TestListChannels below.
|
|
del TestCommonChannel.test_list_channels
|
|
|
|
|
|
class TestNoCredentials:
|
|
def test_gmail_no_credentials_status(self):
|
|
ch = GmailChannel()
|
|
ch.connect()
|
|
assert ch.status() in (ChannelStatus.ERROR, ChannelStatus.DISCONNECTED)
|
|
|
|
def test_gmail_send_no_credentials_returns_false(self):
|
|
ch = GmailChannel()
|
|
result = ch.send("recipient@example.com", "Hello!")
|
|
assert result is False
|
|
|
|
|
|
class TestSend:
|
|
def test_gmail_send_constructs_mime(self):
|
|
ch = GmailChannel()
|
|
mock_service = MagicMock()
|
|
ch._service = mock_service
|
|
ch._status = ChannelStatus.CONNECTED
|
|
|
|
result = ch.send("recipient@example.com", "Hello Gmail!")
|
|
assert result is True
|
|
|
|
# Verify send was called
|
|
mock_service.users().messages().send.assert_called_once()
|
|
call_kwargs = mock_service.users().messages().send.call_args[1]
|
|
assert call_kwargs["userId"] == "me"
|
|
|
|
# Verify the body contains base64-encoded MIME message
|
|
body = call_kwargs["body"]
|
|
assert "raw" in body
|
|
decoded = base64.urlsafe_b64decode(body["raw"]).decode("utf-8")
|
|
assert "recipient@example.com" in decoded
|
|
assert "Hello Gmail!" in decoded
|
|
|
|
def test_gmail_send_with_thread_id(self):
|
|
ch = GmailChannel()
|
|
mock_service = MagicMock()
|
|
ch._service = mock_service
|
|
ch._status = ChannelStatus.CONNECTED
|
|
|
|
result = ch.send(
|
|
"recipient@example.com",
|
|
"Thread reply",
|
|
conversation_id="thread-abc123",
|
|
)
|
|
assert result is True
|
|
|
|
call_kwargs = mock_service.users().messages().send.call_args[1]
|
|
body = call_kwargs["body"]
|
|
assert body["threadId"] == "thread-abc123"
|
|
|
|
def test_gmail_send_with_subject_metadata(self):
|
|
ch = GmailChannel()
|
|
mock_service = MagicMock()
|
|
ch._service = mock_service
|
|
ch._status = ChannelStatus.CONNECTED
|
|
|
|
result = ch.send(
|
|
"recipient@example.com",
|
|
"Body text",
|
|
metadata={"subject": "Custom Subject"},
|
|
)
|
|
assert result is True
|
|
|
|
call_kwargs = mock_service.users().messages().send.call_args[1]
|
|
decoded = base64.urlsafe_b64decode(
|
|
call_kwargs["body"]["raw"],
|
|
).decode("utf-8")
|
|
assert "Custom Subject" in decoded
|
|
|
|
def test_gmail_send_exception_returns_false(self):
|
|
ch = GmailChannel()
|
|
mock_service = MagicMock()
|
|
mock_service.users().messages().send().execute.side_effect = RuntimeError(
|
|
"API error"
|
|
)
|
|
ch._service = mock_service
|
|
ch._status = ChannelStatus.CONNECTED
|
|
|
|
result = ch.send("recipient@example.com", "Hello!")
|
|
assert result is False
|
|
|
|
|
|
class TestListChannels:
|
|
def test_gmail_list_channels(self):
|
|
ch = GmailChannel()
|
|
assert ch.list_channels() == ["inbox"]
|
|
|
|
|
|
class TestEventBus:
|
|
def test_gmail_event_bus_integration(self):
|
|
bus = EventBus(record_history=True)
|
|
ch = GmailChannel(bus=bus)
|
|
mock_service = MagicMock()
|
|
ch._service = mock_service
|
|
ch._status = ChannelStatus.CONNECTED
|
|
|
|
ch.send("recipient@example.com", "Hello!")
|
|
|
|
event_types = [e.event_type for e in bus.history]
|
|
assert EventType.CHANNEL_MESSAGE_SENT in event_types
|
|
|
|
|
|
class TestStatus:
|
|
def test_status_error_when_connected_but_no_service(self):
|
|
ch = GmailChannel()
|
|
ch._status = ChannelStatus.CONNECTED
|
|
ch._service = None
|
|
assert ch.status() == ChannelStatus.ERROR
|
|
|
|
|
|
class TestDisconnect:
|
|
def test_disconnect(self):
|
|
ch = GmailChannel()
|
|
ch._service = MagicMock()
|
|
ch._status = ChannelStatus.CONNECTED
|
|
ch.disconnect()
|
|
assert ch.status() == ChannelStatus.DISCONNECTED
|
|
assert ch._service is None
|
|
|
|
|
|
class TestEnvVarFallback:
|
|
def test_credentials_path_env_var(self):
|
|
with patch.dict(os.environ, {"GMAIL_CREDENTIALS_PATH": "/tmp/creds.json"}):
|
|
ch = GmailChannel()
|
|
assert ch._credentials_path == "/tmp/creds.json"
|
|
|
|
def test_token_path_env_var(self):
|
|
with patch.dict(os.environ, {"GMAIL_TOKEN_PATH": "/tmp/token.json"}):
|
|
ch = GmailChannel()
|
|
assert ch._token_path == "/tmp/token.json"
|
|
|
|
def test_constructor_overrides_env(self):
|
|
with patch.dict(os.environ, {"GMAIL_CREDENTIALS_PATH": "/env/creds.json"}):
|
|
ch = GmailChannel(credentials_path="/explicit/creds.json")
|
|
assert ch._credentials_path == "/explicit/creds.json"
|
|
|
|
|
|
@pytest.mark.live_channel
|
|
class TestLive:
|
|
def test_gmail_send_live(self):
|
|
creds_path = os.environ.get("GMAIL_CREDENTIALS_PATH", "")
|
|
token_path = os.environ.get("GMAIL_TOKEN_PATH", "")
|
|
recipient = os.environ.get("GMAIL_TEST_RECIPIENT", "")
|
|
|
|
if not creds_path and not token_path:
|
|
pytest.skip("No Gmail credentials configured")
|
|
if not recipient:
|
|
pytest.skip("No GMAIL_TEST_RECIPIENT configured")
|
|
|
|
ch = GmailChannel(
|
|
credentials_path=creds_path,
|
|
token_path=token_path,
|
|
)
|
|
ch.connect()
|
|
if ch.status() != ChannelStatus.CONNECTED:
|
|
pytest.skip("Gmail channel failed to connect")
|
|
|
|
result = ch.send(
|
|
recipient,
|
|
"OpenJarvis Gmail channel test message",
|
|
metadata={"subject": "OpenJarvis Test"},
|
|
)
|
|
assert result is True
|
|
ch.disconnect()
|