mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-05-29 19:35:20 +02:00
test(automations/templating): lock render, filters, environment, context
render.py (4): variable substitution, StrictUndefined raises on missing
keys, evaluate_predicate coerces to bool, render_value walks dicts/lists
and renders string leaves.
filters.py (4): slugify produces URL-safe output, date formats datetime
with strftime, date(None) → "" so templates can write
{{ inputs.last_fired_at | date }} on first run, date(str) passes through.
environment.py (4): the sandbox boundary — disallowed Jinja built-ins
(e.g. pprint) raise, and the finalize hook coerces non-string outputs
to predictable wire shapes (datetime → ISO, None → "", dict → JSON).
context.py (1): build_run_context exposes {run, inputs, steps} with the
exact shape every plan template body relies on.
13 tests total, all pure unit.
This commit is contained in:
parent
49af95b652
commit
db4eef651f
5 changed files with 205 additions and 0 deletions
|
|
@ -0,0 +1,59 @@
|
|||
"""Lock the public template-rendering surface: render, predicate, recursive."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
from jinja2 import UndefinedError
|
||||
|
||||
from app.automations.templating.render import (
|
||||
evaluate_predicate,
|
||||
render_template,
|
||||
render_value,
|
||||
)
|
||||
|
||||
pytestmark = pytest.mark.unit
|
||||
|
||||
|
||||
def test_render_template_substitutes_context_variables() -> None:
|
||||
"""A template referencing a context variable produces the substituted
|
||||
string. Most basic contract of the template engine."""
|
||||
result = render_template("Hello {{ name }}!", {"name": "World"})
|
||||
|
||||
assert result == "Hello World!"
|
||||
|
||||
|
||||
def test_render_template_raises_on_undefined_variable() -> None:
|
||||
"""Referencing a variable that isn't in the context raises rather than
|
||||
rendering the empty string. Locks the StrictUndefined safety net so
|
||||
template typos surface as run failures instead of silent corruption."""
|
||||
with pytest.raises(UndefinedError):
|
||||
render_template("Hello {{ missing }}!", {})
|
||||
|
||||
|
||||
def test_evaluate_predicate_returns_truthy_outcome_of_expression() -> None:
|
||||
"""``evaluate_predicate`` compiles a Jinja **expression** (not template
|
||||
body) and coerces the value to ``bool``. Drives ``step.when`` gating."""
|
||||
assert evaluate_predicate("inputs.count > 0", {"inputs": {"count": 3}}) is True
|
||||
assert evaluate_predicate("inputs.count > 0", {"inputs": {"count": 0}}) is False
|
||||
|
||||
|
||||
def test_render_value_renders_strings_recursively_through_dicts_and_lists() -> None:
|
||||
"""``render_value`` walks dicts and lists, renders string leaves through
|
||||
the template engine, and leaves non-strings untouched. This is the
|
||||
primitive ``execute_step`` uses to render step params at run time."""
|
||||
context = {"inputs": {"name": "World"}, "topic": "weekly"}
|
||||
|
||||
rendered = render_value(
|
||||
{
|
||||
"greeting": "Hello {{ inputs.name }}",
|
||||
"tags": ["{{ topic }}", "static"],
|
||||
"config": {"retries": 3, "label": "{{ topic }}-{{ inputs.name }}"},
|
||||
},
|
||||
context,
|
||||
)
|
||||
|
||||
assert rendered == {
|
||||
"greeting": "Hello World",
|
||||
"tags": ["weekly", "static"],
|
||||
"config": {"retries": 3, "label": "weekly-World"},
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue