Files
wehub-resource-sync 4b6817381b
CI (OpenClaw E2E) / openclaw test (push) Has been cancelled
CI / coverage-report (push) Has been cancelled
CI / test-kubernetes (push) Has been cancelled
CI / should-run-thorough (push) Has been cancelled
CI / test-thorough (cloudwatch-demo) (push) Has been cancelled
CI / test-thorough (flink-ecs) (push) Has been cancelled
CI / test-thorough (upstream-lambda) (push) Has been cancelled
CI / test-thorough (prefect-ecs-fargate) (push) Has been cancelled
Release / build-binaries (zip, opensre.exe, onefile, windows-latest, windows-x64) (push) Has been cancelled
Benchmark image — build + push to ECR (any adapter) / build + push (push) Has been cancelled
CI / quality (ubuntu-latest) (push) Has been cancelled
CI / test (tools-runtime) (push) Has been cancelled
CI / test (e2e-general) (push) Has been cancelled
CI / test (cli-runtime) (push) Has been cancelled
CI / test (e2e-provider-and-openclaw) (push) Has been cancelled
CI / test (integrations-and-misc) (push) Has been cancelled
Release / verify (push) Has been cancelled
Release / build-python-dist (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-15-intel, darwin-x64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, macos-latest, darwin-arm64) (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04, linux-x64) (push) Has been cancelled
Release / publish-release (push) Has been cancelled
Release / publish-main-release (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-checks (no-LLM) (push) Has been cancelled
CodeQL / Analyze (python) (push) Has been cancelled
Interactive Shell Live (PR + post-merge) / turn-live shard ${{ matrix.shard_index }} (push) Has been cancelled
Release / prepare (push) Has been cancelled
Release / build-binaries (tar.gz, opensre, onedir, ubuntu-22.04-arm, linux-arm64) (push) Has been cancelled
Synthetic Deterministic Tests / Synthetic offline (deterministic) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:10:45 +08:00

155 lines
4.4 KiB
Python

"""Tests for utils/smtp_delivery.py."""
from __future__ import annotations
from email.message import EmailMessage
import pytest
from integrations.smtp.delivery import (
format_background_rca_email,
send_smtp_report,
verify_smtp_connection,
)
class _FakeSMTP:
def __init__(self, host: str, port: int, timeout: float) -> None:
self.host = host
self.port = port
self.timeout = timeout
self.started_tls = False
self.logged_in: tuple[str, str] | None = None
self.sent: list[EmailMessage] = []
self.quit_called = False
def ehlo(self) -> None:
return None
def starttls(self) -> None:
self.started_tls = True
def login(self, username: str, password: str) -> None:
self.logged_in = (username, password)
def noop(self) -> None:
return None
def send_message(self, message: EmailMessage) -> None:
self.sent.append(message)
def quit(self) -> None:
self.quit_called = True
def close(self) -> None:
self.quit_called = True
class _FailingLoginSMTP(_FakeSMTP):
def login(self, username: str, password: str) -> None:
self.logged_in = (username, password)
raise RuntimeError("auth failed")
def _return_fake_client(fake_client: _FakeSMTP):
def _factory(*_args: object, **_kwargs: object) -> _FakeSMTP:
return fake_client
return _factory
def test_format_background_rca_email_includes_required_sections() -> None:
subject, body = format_background_rca_email(
task_id="bg-123",
command="/investigate checkout",
root_cause="postgres connection pool saturation",
top_analysis=("rds cpu spike", "error rate climbed"),
next_steps=("raise pool size",),
stats={"tool_call_count": 4, "investigation_loop_count": 2, "validity_score": 0.91},
)
assert subject == "OpenSRE RCA complete: bg-123"
assert "Root cause" in body
assert "Top analysis" in body
assert "What to do next" in body
assert "Internal stats" in body
def test_verify_smtp_connection_uses_starttls(monkeypatch: pytest.MonkeyPatch) -> None:
fake_client = _FakeSMTP("smtp.example.com", 587, 15)
monkeypatch.setattr("integrations.smtp.delivery.smtplib.SMTP", _return_fake_client(fake_client))
ok, detail = verify_smtp_connection(
{
"host": "smtp.example.com",
"port": 587,
"security": "starttls",
"username": "mailer",
"password": "secret",
}
)
assert ok is True
assert "successfully" in detail.lower()
assert fake_client.started_tls is True
assert fake_client.logged_in == ("mailer", "secret")
def test_send_smtp_report_sends_plain_text_email(monkeypatch: pytest.MonkeyPatch) -> None:
fake_client = _FakeSMTP("smtp.example.com", 465, 15)
monkeypatch.setattr(
"integrations.smtp.delivery.smtplib.SMTP_SSL",
_return_fake_client(fake_client),
)
ok, error = send_smtp_report(
report="hello world",
subject="RCA ready",
smtp_ctx={
"host": "smtp.example.com",
"port": 465,
"security": "ssl",
"from_address": "opensre@example.com",
"default_to": "team@example.com",
},
)
assert ok is True
assert error == ""
assert len(fake_client.sent) == 1
assert fake_client.sent[0]["Subject"] == "RCA ready"
assert fake_client.sent[0]["From"] == "opensre@example.com"
assert fake_client.sent[0]["To"] == "team@example.com"
assert "hello world" in fake_client.sent[0].get_content()
def test_send_smtp_report_requires_recipient() -> None:
ok, error = send_smtp_report(
report="hello",
subject="RCA ready",
smtp_ctx={"from_address": "opensre@example.com"},
)
assert ok is False
assert "recipient" in error.lower()
def test_verify_smtp_connection_closes_client_when_setup_fails(
monkeypatch: pytest.MonkeyPatch,
) -> None:
fake_client = _FailingLoginSMTP("smtp.example.com", 587, 15)
monkeypatch.setattr("integrations.smtp.delivery.smtplib.SMTP", _return_fake_client(fake_client))
ok, detail = verify_smtp_connection(
{
"host": "smtp.example.com",
"port": 587,
"security": "none",
"username": "mailer",
"password": "secret",
}
)
assert ok is False
assert "auth failed" in detail
assert fake_client.quit_called is True