Files
wehub-resource-sync 7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

221 lines
6.9 KiB
Python

"""
Deep coverage tests for notifications/templates.py.
Targets uncovered paths:
- _get_jinja_env: template dir missing, Environment init exception
- format: Jinja2 render exception fallback, RATE_LIMIT_WARNING (no template file)
- get_required_context: meta parsing, loader=None branch, exception branch
"""
from unittest.mock import patch, MagicMock
class TestGetJinjaEnvMissingTemplateDir:
"""Cover _get_jinja_env when template directory doesn't exist."""
def test_returns_none_when_template_dir_missing(self):
from local_deep_research.notifications.templates import (
NotificationTemplate,
)
from pathlib import Path
NotificationTemplate._jinja_env = None
# Patch os.path operations at the Path level
original_exists = Path.exists
def fake_exists(self):
if str(self).endswith("templates"):
return False
return original_exists(self)
try:
with patch.object(Path, "exists", fake_exists):
result = NotificationTemplate._get_jinja_env()
assert result is None
finally:
NotificationTemplate._jinja_env = None
class TestGetJinjaEnvInitException:
"""Cover _get_jinja_env when Environment() raises."""
def test_returns_none_on_environment_init_error(self):
from local_deep_research.notifications.templates import (
NotificationTemplate,
)
NotificationTemplate._jinja_env = None
with patch(
"local_deep_research.notifications.templates.Environment",
side_effect=Exception("Jinja2 init failed"),
):
result = NotificationTemplate._get_jinja_env()
assert result is None
# Cleanup
NotificationTemplate._jinja_env = None
class TestFormatJinja2RenderException:
"""Cover format() when Jinja2 template rendering raises."""
def test_falls_back_when_jinja2_render_fails(self):
from local_deep_research.notifications.templates import (
NotificationTemplate,
EventType,
)
NotificationTemplate._jinja_env = None
mock_env = MagicMock()
mock_template = MagicMock()
mock_template.render.side_effect = Exception("render failed")
mock_env.get_template.return_value = mock_template
with patch.object(
NotificationTemplate, "_get_jinja_env", return_value=mock_env
):
result = NotificationTemplate.format(
EventType.RESEARCH_COMPLETED, {"query": "test"}
)
# Should fall back to _get_fallback_template
assert "title" in result
assert "body" in result
assert "Research Completed" in result["title"]
NotificationTemplate._jinja_env = None
class TestFormatNoTemplateFile:
"""Cover format() when event_type not in TEMPLATE_FILES."""
def test_returns_generic_notification_for_unmapped_event(self):
from local_deep_research.notifications.templates import (
NotificationTemplate,
EventType,
)
# RATE_LIMIT_WARNING is not in TEMPLATE_FILES
result = NotificationTemplate.format(
EventType.RATE_LIMIT_WARNING, {"detail": "too many requests"}
)
assert "title" in result
assert "body" in result
assert "rate_limit_warning" in result["title"]
class TestFormatJinja2Unavailable:
"""Cover format() when _get_jinja_env returns None."""
def test_uses_fallback_when_jinja2_unavailable(self):
from local_deep_research.notifications.templates import (
NotificationTemplate,
EventType,
)
with patch.object(
NotificationTemplate, "_get_jinja_env", return_value=None
):
result = NotificationTemplate.format(
EventType.RESEARCH_FAILED, {"error": "timeout"}
)
assert "title" in result
assert "body" in result
assert "Research Failed" in result["title"]
assert "Details" in result["body"]
class TestFormatCustomTemplateSafeContext:
"""Cover safe_context conversion in custom_template branch."""
def test_none_values_become_empty_string(self):
from local_deep_research.notifications.templates import (
NotificationTemplate,
EventType,
)
custom_template = {
"title": "Title: {value}",
"body": "Body: {other}",
}
context = {"value": None, "other": 42}
result = NotificationTemplate.format(
EventType.TEST, context, custom_template=custom_template
)
assert result["title"] == "Title: "
assert result["body"] == "Body: 42"
class TestGetRequiredContextMetaParsing:
"""Cover get_required_context Jinja2 meta parsing."""
def test_extracts_variables_from_template(self):
from local_deep_research.notifications.templates import (
NotificationTemplate,
EventType,
)
# Use a real event type that has a template
result = NotificationTemplate.get_required_context(
EventType.RESEARCH_COMPLETED
)
# Should return a list (possibly empty if template dir missing)
assert isinstance(result, list)
def test_returns_empty_when_jinja_env_none(self):
from local_deep_research.notifications.templates import (
NotificationTemplate,
EventType,
)
with patch.object(
NotificationTemplate, "_get_jinja_env", return_value=None
):
result = NotificationTemplate.get_required_context(EventType.TEST)
assert result == []
def test_returns_empty_on_template_parse_exception(self):
from local_deep_research.notifications.templates import (
NotificationTemplate,
EventType,
)
mock_env = MagicMock()
mock_env.get_template.side_effect = Exception("template parse error")
with patch.object(
NotificationTemplate, "_get_jinja_env", return_value=mock_env
):
result = NotificationTemplate.get_required_context(
EventType.RESEARCH_COMPLETED
)
assert result == []
def test_meta_parsing_with_loader_none(self):
"""Cover the branch where template.environment.loader is None."""
from local_deep_research.notifications.templates import (
NotificationTemplate,
EventType,
)
mock_env = MagicMock()
mock_template = MagicMock()
mock_template.environment.loader = None
mock_env.get_template.return_value = mock_template
with patch.object(
NotificationTemplate, "_get_jinja_env", return_value=mock_env
):
result = NotificationTemplate.get_required_context(
EventType.RESEARCH_COMPLETED
)
assert isinstance(result, list)