Skip to content

Commit 6538ad4

Browse files
charliegilletclaude
andcommitted
test(nodes): add unit tests for prompt template engine and polish
Adds 57 unit tests covering the custom template engine (stdlib-only, no Jinja2) in nodes/test/test_prompt_template_engine.py — variable substitution, dotted paths, missing variables, builtins, context-over- builtin overrides, if/elif/else, nested blocks, for loops, escape sequences, malformed/unclosed tags, HTML pass-through, and ReDoS / recursion safety. The test module loads template_engine.py directly via importlib so it does not trigger the depends-based package init. Also addresses outstanding review feedback on PR #573: - Mark the node as experimental in services.json (per asclearuc) - Add an explicit {{question}} substitution test case in services.json (per CodeRabbit) - Add missing return type annotations on _resolve (Any), _at_top_level (bool), _handle_if (int) and _handle_for (int) to satisfy Ruff ANN202 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c5b406c commit 6538ad4

3 files changed

Lines changed: 419 additions & 5 deletions

File tree

nodes/src/nodes/prompt_template/services.json

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
// Capabilities are flags that change the behavior of the underlying
2222
// engine
2323
//
24-
"capabilities": [],
24+
"capabilities": ["experimental"],
2525
//
2626
// Optional:
2727
// Register is either filter, endpoint or ignored if not specified. If the
@@ -142,6 +142,20 @@
142142
"contains": "Summarize the following:"
143143
}
144144
}
145+
},
146+
{
147+
"config": {
148+
"template": "Q={{question}} | I={{input}}",
149+
"variables": {"input": "ctx"}
150+
},
151+
"questions": {
152+
"questions": [{"text": "what is this?"}]
153+
},
154+
"expect": {
155+
"questions": {
156+
"contains": "Q=what is this?"
157+
}
158+
}
145159
}
146160
]
147161
},

nodes/src/nodes/prompt_template/template_engine.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
import uuid as _uuid
3838
from datetime import datetime, timezone
3939
from random import random as _random
40+
from typing import Any
4041

4142

4243
# Sentinel used to distinguish "key missing" from "key is None"
@@ -64,7 +65,7 @@
6465
# ---------------------------------------------------------------------------
6566
# Context value resolution
6667
# ---------------------------------------------------------------------------
67-
def _resolve(name: str, context: dict):
68+
def _resolve(name: str, context: dict) -> Any:
6869
"""Resolve a dotted name against *context*, falling back to built-ins.
6970
7071
Resolution order: context takes priority over built-in helpers.
@@ -169,7 +170,7 @@ def _eval_tokens(tokens: list[tuple[str, str]], context: dict) -> str:
169170
return ''.join(parts)
170171

171172

172-
def _at_top_level(tokens, start, pos):
173+
def _at_top_level(tokens: list[tuple[str, str]], start: int, pos: int) -> bool:
173174
"""Check if *pos* is at the top nesting level between *start* and *pos*."""
174175
depth_if = 0
175176
depth_for = 0
@@ -188,7 +189,12 @@ def _at_top_level(tokens, start, pos):
188189
return depth_if == 0 and depth_for == 0
189190

190191

191-
def _handle_if(tokens, start, context, parts):
192+
def _handle_if(
193+
tokens: list[tuple[str, str]],
194+
start: int,
195+
context: dict,
196+
parts: list[str],
197+
) -> int:
192198
"""Process an {% if %}...{% elif %}...{% else %}...{% endif %} block."""
193199
end = _find_matching_end(tokens, start + 1, 'if', 'endif')
194200
if end == -1:
@@ -225,7 +231,12 @@ def _handle_if(tokens, start, context, parts):
225231
return end + 1
226232

227233

228-
def _handle_for(tokens, start, context, parts):
234+
def _handle_for(
235+
tokens: list[tuple[str, str]],
236+
start: int,
237+
context: dict,
238+
parts: list[str],
239+
) -> int:
229240
"""Process a {% for item in list %}...{% endfor %} block."""
230241
end = _find_matching_end(tokens, start + 1, 'for', 'endfor')
231242
if end == -1:

0 commit comments

Comments
 (0)