Files
Agents AI a527f14a57
Validate and publish container image / build (pull_request) Successful in 5m46s
signalモジュールのimport漏れを修正
2026-07-20 09:18:05 +09:00

179 lines
6.0 KiB
Python

import os
import signal
import app
import unittest
from unittest.mock import Mock, patch
from app import EmailMessage, EmailMonitor
EMAIL_ENV = {
"EMAIL_USER": "user@example.com",
"EMAIL_PASSWORD": "password",
}
class EmailMonitorConfigTests(unittest.TestCase):
@patch.dict(
os.environ,
{**EMAIL_ENV, "DISCORD_WEBHOOK_URL": "https://discord.example/webhook"},
clear=True,
)
def test_discord_only_configuration_remains_supported(self):
monitor = EmailMonitor()
self.assertEqual(monitor.discord_webhook_url, "https://discord.example/webhook")
self.assertIsNone(monitor.webhook_url)
@patch.dict(
os.environ,
{
**EMAIL_ENV,
"WEBHOOK_URL": "https://agents.example/webhook",
"WEBHOOK_TOKEN": "123:signed-token",
"DISCORD_CHANNEL_ID": "456",
},
clear=True,
)
def test_agents_webhook_only_configuration_is_supported(self):
monitor = EmailMonitor()
self.assertIsNone(monitor.discord_webhook_url)
self.assertEqual(monitor.discord_channel_id, 456)
@patch.dict(
os.environ,
{**EMAIL_ENV, "WEBHOOK_URL": "https://agents.example/webhook"},
clear=True,
)
def test_partial_agents_webhook_configuration_is_rejected(self):
with self.assertRaises(SystemExit):
EmailMonitor()
class EmailMonitorNotificationTests(unittest.TestCase):
def setUp(self):
self.email = EmailMessage(
subject="テスト件名",
sender="sender@example.com",
date="Fri, 18 Jul 2026 12:00:00 +0900",
body="メール本文",
uid="42",
)
@patch.dict(
os.environ,
{**EMAIL_ENV, "DISCORD_WEBHOOK_URL": "https://discord.example/webhook"},
clear=True,
)
@patch("app.requests.post")
def test_existing_discord_delivery_remains_supported(self, post):
post.return_value = Mock(status_code=204, text="")
monitor = EmailMonitor()
self.assertTrue(monitor.send_to_discord(self.email))
post.assert_called_once()
self.assertEqual(post.call_args.args[0], "https://discord.example/webhook")
embed = post.call_args.kwargs["json"]["embeds"][0]
self.assertEqual(embed["fields"][0]["value"], "テスト件名")
@patch.dict(
os.environ,
{
**EMAIL_ENV,
"WEBHOOK_URL": "https://agents.example/webhook",
"WEBHOOK_TOKEN": "123:signed-token",
"DISCORD_CHANNEL_ID": "456",
"WEBHOOK_SOURCE": "email-satellite",
},
clear=True,
)
@patch("app.requests.post")
def test_agents_webhook_payload_matches_agents_contract(self, post):
response = Mock(status_code=202, text="Accepted")
post.return_value = response
monitor = EmailMonitor()
self.assertTrue(monitor.send_to_agents_webhook(self.email))
post.assert_called_once()
url = post.call_args.args[0]
kwargs = post.call_args.kwargs
self.assertEqual(url, "https://agents.example/webhook")
self.assertEqual(kwargs["headers"]["Authorization"], "Bearer 123:signed-token")
self.assertEqual(kwargs["json"]["source"], "email-satellite")
self.assertEqual(kwargs["json"]["channel_id"], 456)
self.assertIn("テスト件名", kwargs["json"]["prompt"])
self.assertIn("メール本文", kwargs["json"]["prompt"])
self.assertIn("信頼できない外部入力", kwargs["json"]["prompt"])
@patch.dict(
os.environ,
{
**EMAIL_ENV,
"DISCORD_WEBHOOK_URL": "https://discord.example/webhook",
"WEBHOOK_URL": "https://agents.example/webhook",
"WEBHOOK_TOKEN": "123:signed-token",
"DISCORD_CHANNEL_ID": "456",
},
clear=True,
)
def test_dual_mode_requires_both_destinations_to_succeed(self):
monitor = EmailMonitor()
with patch.object(monitor, "send_to_discord", return_value=True), patch.object(
monitor, "send_to_agents_webhook", return_value=False
):
self.assertFalse(monitor.send_notifications(self.email))
with patch.object(monitor, "send_to_discord", return_value=True), patch.object(
monitor, "send_to_agents_webhook", return_value=True
):
self.assertTrue(monitor.send_notifications(self.email))
@patch.dict(
os.environ,
{**EMAIL_ENV, "DISCORD_WEBHOOK_URL": "https://discord.example/webhook"},
clear=True,
)
@patch("app.time.sleep")
def test_email_is_marked_read_only_after_notifications_succeed(self, _sleep):
monitor = EmailMonitor()
with patch.object(monitor, "send_notifications", return_value=False), patch.object(
monitor, "mark_email_as_read"
) as mark_read:
monitor.send_notifications_with_rate_limit([self.email], rate_limit=0)
mark_read.assert_not_called()
with patch.object(monitor, "send_notifications", return_value=True), patch.object(
monitor, "mark_email_as_read"
) as mark_read:
monitor.send_notifications_with_rate_limit([self.email], rate_limit=0)
mark_read.assert_called_once_with("42")
class ApplicationStartupTests(unittest.TestCase):
@patch.dict(os.environ, {**EMAIL_ENV, "DISCORD_WEBHOOK_URL": "https://discord.example/webhook"}, clear=True)
@patch("app.run_http_server")
@patch("app.threading.Thread")
@patch("app.signal.signal")
def test_main_registers_signal_handlers_and_starts_services(
self, register_signal, thread_class, run_http_server
):
monitor_thread = thread_class.return_value
app.main()
registered_signals = [call.args[0] for call in register_signal.call_args_list]
self.assertEqual(registered_signals, [signal.SIGINT, signal.SIGTERM])
monitor_thread.start.assert_called_once_with()
run_http_server.assert_called_once_with()
if __name__ == "__main__":
unittest.main()