Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
11 changes: 9 additions & 2 deletions infrahub_sdk/template/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,10 @@ def get_variables(self) -> list[str]:
if self.is_file_based and env.loader:
template_source = env.loader.get_source(env, self._template)[0]

template = env.parse(template_source)
try:
template = env.parse(template_source)
except jinja2.TemplateSyntaxError as exc:
self._raise_template_syntax_error(error=exc)

return sorted(meta.find_undeclared_variables(template))

Expand All @@ -96,7 +99,11 @@ def validate(self, restricted: bool = True) -> None:
if self.is_file_based and env.loader:
template_source = env.loader.get_source(env, self._template)[0]

template = env.parse(template_source)
try:
template = env.parse(template_source)
except jinja2.TemplateSyntaxError as exc:
self._raise_template_syntax_error(error=exc)

for node in template.find_all(nodes.Filter):
if node.name not in allowed_list:
raise JinjaTemplateOperationViolationError(f"The '{node.name}' filter isn't allowed to be used")
Expand Down
58 changes: 58 additions & 0 deletions tests/unit/sdk/test_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,14 @@ class JinjaTestCaseFailing:
error: JinjaTemplateError


@dataclass
class JinjaSyntaxErrorTestCase:
name: str
template: str
expected_message: str
expected_lineno: int


SUCCESSFUL_STRING_TEST_CASES = [
JinjaTestCase(
name="hello-world",
Expand Down Expand Up @@ -253,6 +261,56 @@ async def test_manage_unhandled_error() -> None:
assert exc.value.message == "division by zero"


SYNTAX_ERROR_TEST_CASES = [
JinjaSyntaxErrorTestCase(
name="empty-expression",
template="{{ }} testing",
expected_message="Expected an expression, got 'end of print statement'",
expected_lineno=1,
),
JinjaSyntaxErrorTestCase(
name="missing-closing-end-if",
template="Hello {% if name is undefined %}stranger{% else %}{{name}}{% endif",
expected_message="unexpected end of template, expected 'end of statement block'.",
expected_lineno=1,
),
JinjaSyntaxErrorTestCase(
name="fail-on-line-2",
template="Hello \n{{ name }",
expected_message="unexpected '}'",
expected_lineno=2,
),
]


@pytest.mark.parametrize(
"test_case",
[pytest.param(tc, id=tc.name) for tc in SYNTAX_ERROR_TEST_CASES],
)
def test_get_variables_syntax_error(test_case: JinjaSyntaxErrorTestCase) -> None:
"""Test that get_variables() raises JinjaTemplateSyntaxError for invalid templates."""
jinja = Jinja2Template(template=test_case.template)
with pytest.raises(JinjaTemplateSyntaxError) as exc:
jinja.get_variables()

assert exc.value.message == test_case.expected_message
assert exc.value.lineno == test_case.expected_lineno


@pytest.mark.parametrize(
"test_case",
[pytest.param(tc, id=tc.name) for tc in SYNTAX_ERROR_TEST_CASES],
)
def test_validate_syntax_error(test_case: JinjaSyntaxErrorTestCase) -> None:
"""Test that validate() raises JinjaTemplateSyntaxError for invalid templates."""
jinja = Jinja2Template(template=test_case.template)
with pytest.raises(JinjaTemplateSyntaxError) as exc:
jinja.validate()

assert exc.value.message == test_case.expected_message
assert exc.value.lineno == test_case.expected_lineno


async def test_validate_filter() -> None:
jinja = Jinja2Template(template="{{ network | get_all_host }}")
jinja.validate(restricted=False)
Expand Down