Files
devx/tests/unit/test_utils_jinja.py
T
emilandDevin <158243242+devin-ai-integration[bot]@users.noreply.github.com> bbb14c71e9 feat: sync missing features from v0.49.x line to master
The v0.49.x tag line diverged from origin/master, leaving many
features only accessible via tags but not on the master branch.

New modules:
- ci/cancel_superseded_runs.py — cancel superseded CI runs
- ci/check_workflow_artifact_deps.py — validate artifact deps
- ci/check_workflow_tofu_init.py — validate tofu init steps
- tools/check_alert_rules.py — validate Prometheus alert rules
- tools/check_ansible_set_fact_to_json.py — lint set_fact usage
- tools/check_docker_init.py — validate Docker init scripts
- utils/jinja.py — Jinja2 template utilities
- utils/ui.py — UI/console utilities

Modified modules:
- distribute_molecule.py: add --include-roles/--exclude-roles
- utils/api.py: add container.credentials for private registry auth
- install_tools.py: retry ansible-galaxy on transient timeouts
- setup_image.py: skip dep resolution with --no-deps
- cli.py: register new commands
- i18n.py: add new translation keys

Also removes accidentally committed .vale/styles/Google/ files.

Test results: 2195 passed, 100% coverage.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-08-09 03:07:28 +02:00

162 lines
4.8 KiB
Python

"""Unit tests for devx.utils.jinja."""
from __future__ import annotations
import jinja2
from devx.utils.jinja import (
make_env,
make_value_env,
regex_escape,
regex_replace,
regex_search,
render_manifest_values,
render_template,
render_value,
to_bool,
to_json,
)
class TestFilters:
def test_to_json(self):
assert to_json({"a": 1}) == '{"a": 1}'
def test_to_json_list(self):
assert to_json([1, 2]) == "[1, 2]"
def test_to_bool_true(self):
assert to_bool(True) is True
def test_to_bool_false(self):
assert to_bool(False) is False
def test_to_bool_string_true(self):
assert to_bool("yes") is True
def test_to_bool_string_false(self):
assert to_bool("false") is False
def test_to_bool_empty_string(self):
assert to_bool("") is False
def test_to_bool_none(self):
assert to_bool(None) is False
def test_to_bool_int(self):
assert to_bool(1) is True
assert to_bool(0) is False
def test_regex_replace(self):
assert regex_replace("hello world", "world", "there") == "hello there"
def test_regex_replace_with_pattern(self):
assert regex_replace("abc123", r"\d+", "X") == "abcX"
def test_regex_escape(self):
assert regex_escape("a.b*c") == "a\\.b\\*c"
def test_regex_search_found(self):
assert regex_search("hello world", r"world") == "world"
def test_regex_search_not_found(self):
assert regex_search("hello", r"world") is None
def test_regex_search_group(self):
assert regex_search("abc123", r"\d+") == "123"
class TestMakeEnv:
def test_returns_environment(self, tmp_path):
(tmp_path / "test.j2").write_text("hello {{ name }}")
env = make_env(str(tmp_path))
assert isinstance(env, jinja2.Environment)
def test_has_filters(self, tmp_path):
env = make_env(str(tmp_path))
assert "to_json" in env.filters
assert "bool" in env.filters
assert "regex_replace" in env.filters
assert "regex_escape" in env.filters
assert "regex_search" in env.filters
def test_cached(self, tmp_path):
env1 = make_env(str(tmp_path))
env2 = make_env(str(tmp_path))
assert env1 is env2
def test_auto_reload_disabled(self, tmp_path):
env = make_env(str(tmp_path))
assert env.auto_reload is False
def test_strict_undefined(self, tmp_path):
env = make_env(str(tmp_path))
assert env.undefined is jinja2.StrictUndefined
class TestMakeValueEnv:
def test_returns_environment(self):
env = make_value_env()
assert isinstance(env, jinja2.Environment)
def test_chainable_undefined(self):
env = make_value_env()
assert env.undefined is jinja2.ChainableUndefined
def test_cached(self):
assert make_value_env() is make_value_env()
def test_has_filters(self):
env = make_value_env()
assert "to_json" in env.filters
class TestRenderTemplate:
def test_renders_named_template(self, tmp_path):
(tmp_path / "test.j2").write_text("hello {{ name }}")
env = make_env(str(tmp_path))
assert render_template(env, "test.j2", name="world") == "hello world"
def test_renders_with_filters(self, tmp_path):
(tmp_path / "test.j2").write_text("{{ data | to_json }}")
env = make_env(str(tmp_path))
assert render_template(env, "test.j2", data={"a": 1}) == '{"a": 1}'
class TestRenderValue:
def test_renders_string_with_expressions(self):
assert render_value("hello {{ name }}", {"name": "world"}) == "hello world"
def test_passes_through_non_string(self):
assert render_value(42, {}) == 42
def test_passes_through_string_without_expressions(self):
assert render_value("plain text", {}) == "plain text"
def test_passes_through_none(self):
assert render_value(None, {}) is None
class TestRenderManifestValues:
def test_renders_dict_values(self):
result = render_manifest_values({"key": "{{ value }}"}, {"value": "rendered"})
assert result == {"key": "rendered"}
def test_renders_list_values(self):
result = render_manifest_values(["{{ a }}", "{{ b }}"], {"a": "1", "b": "2"})
assert result == ["1", "2"]
def test_renders_nested(self):
result = render_manifest_values({"outer": {"inner": "{{ x }}"}}, {"x": "yes"})
assert result == {"outer": {"inner": "yes"}}
def test_passes_through_non_string(self):
result = render_manifest_values({"n": 42, "b": True, "l": [1, 2]}, {})
assert result == {"n": 42, "b": True, "l": [1, 2]}
def test_empty_dict(self):
assert render_manifest_values({}, {}) == {}
def test_empty_list(self):
assert render_manifest_values([], {}) == []