Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions changedetectionio/notification_service.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,10 @@ def __init__(self, initial_data=None, **kwargs):
# Always the raw +/- diff regardless of LLM summary override (populated in handler.py from {{diff}})
'raw_diff': FormattableDiff('', ''),
'markup_text_links_to_html_links': False, # If automatic conversion of plaintext to HTML should happen
# Safe-empty default so restock tokens ({{ restock.price }} etc.) are a valid,
# non-crashing token for every watch. Restock watches override this via
# processors/restock_diff extra_notification_token_values().
'restock': {},
Comment thread
dgtlmoon marked this conversation as resolved.
Outdated
'notification_timestamp': time.time(),
'prev_snapshot': None,
'preview_url': None,
Expand Down
43 changes: 43 additions & 0 deletions changedetectionio/tests/test_notification_restock_token.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""
Regression tests for issue #3490 - "'restock' is undefined".

A `{{ restock.price }}` token is accepted in a per-watch notification body (restock
watches inject the value via processors/restock_diff extra_notification_token_values()),
but the same token in a system-wide / non-restock context had no default. That made it:

1. crash rendering at send time for a non-restock watch (UndefinedError), and
2. fail save-time validation of a system-wide notification body (ValidationError),

because `restock` was absent from NotificationContextData's default token set.

These tests exercise the real send-time (jinja2_custom.render) and save-time
(ValidateJinja2Template) code paths. They fail on a tree without the safe-empty
default and pass with it.
"""
from changedetectionio.notification_service import NotificationContextData


def test_restock_token_present_in_default_context():
assert 'restock' in NotificationContextData()


def test_restock_token_renders_safely_for_non_restock_watch():
"""Send time: a non-restock watch must not crash on {{ restock.price }}."""
from changedetectionio.jinja2_custom import render as jinja_render

ctx = NotificationContextData() # a plain, non-restock watch context
rendered = jinja_render(template_str="Price is {{ restock.price }}", **ctx)
# The undefined price renders as empty rather than raising UndefinedError.
assert rendered == "Price is "


def test_restock_token_validates_in_system_settings():
"""Save time: a system-wide body using {{ restock.price }} must validate."""
from changedetectionio.forms import ValidateJinja2Template

class _Field:
def __init__(self, data):
self.data = data

# Raised ValidationError before the fix; must not raise now.
ValidateJinja2Template()(None, _Field("Price is {{ restock.price }}"))
Loading