feat: auto-email sending via SMTP (--send flag)
Closes #45 - New redmine_reporter/mailer.py: SMTP email sending with {author}/{period} template substitution, MIME attachment with correct content-type per file extension - Config.get_email_config(): returns EmailConfig from YAML or None when not configured - CLI --send flag: sends report after generation, works with --output, --commit, or standalone (saves to template path) - 31 new tests (22 mailer + 6 CLI + 3 config) - 249/249 tests passing, ruff clean, mypy clean
This commit is contained in:
@@ -557,3 +557,53 @@ class TestDefaultDateRangeWithLastUsed:
|
||||
Config._app = AppConfig.from_yaml(yaml_path)
|
||||
result = Config.get_default_date_range()
|
||||
assert result == "2026-04-01--2026-04-15"
|
||||
|
||||
|
||||
class TestGetEmailConfig:
|
||||
"""Tests for Config.get_email_config()."""
|
||||
|
||||
@mock.patch.dict(os.environ, {}, clear=True)
|
||||
def test_get_email_config_from_yaml(self):
|
||||
"""get_email_config() возвращает EmailConfig из YAML."""
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
yaml_path = Path(tmp) / "config.yml"
|
||||
yaml_path.write_text(
|
||||
"email:\n"
|
||||
" smtp:\n"
|
||||
" host: smtp.example.com\n"
|
||||
" port: 587\n"
|
||||
" user: bot@example.com\n"
|
||||
" password: secret\n"
|
||||
" tls: true\n"
|
||||
" from: bot@example.com\n"
|
||||
" to:\n"
|
||||
" - boss@example.com\n"
|
||||
)
|
||||
|
||||
Config._app = AppConfig.from_yaml(yaml_path)
|
||||
cfg = Config.get_email_config()
|
||||
|
||||
assert cfg is not None
|
||||
assert cfg.smtp.host == "smtp.example.com"
|
||||
assert cfg.smtp.port == 587
|
||||
assert cfg.smtp.user == "bot@example.com"
|
||||
assert cfg.smtp.password == "secret"
|
||||
assert cfg.smtp.tls is True
|
||||
assert cfg.from_ == "bot@example.com"
|
||||
assert cfg.to == ["boss@example.com"]
|
||||
|
||||
@mock.patch.dict(os.environ, {}, clear=True)
|
||||
def test_get_email_config_returns_none_when_no_yaml(self):
|
||||
"""Без YAML-конфига get_email_config() возвращает None."""
|
||||
Config._app = None
|
||||
assert Config.get_email_config() is None
|
||||
|
||||
@mock.patch.dict(os.environ, {}, clear=True)
|
||||
def test_get_email_config_returns_none_when_no_host(self):
|
||||
"""С YAML но без smtp.host — возвращает None."""
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
yaml_path = Path(tmp) / "config.yml"
|
||||
yaml_path.write_text("email:\n smtp:\n host: ''\n")
|
||||
|
||||
Config._app = AppConfig.from_yaml(yaml_path)
|
||||
assert Config.get_email_config() is None
|
||||
|
||||
Reference in New Issue
Block a user