119 lines
4.7 KiB
Python
119 lines
4.7 KiB
Python
"""Tests for process_template.py: variables, conditionals, nesting, leftover markers and metadata."""
|
|
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
SCRIPT = Path(__file__).resolve().parents[1] / "process_template.py"
|
|
|
|
TEMPLATE = """---
|
|
name: {skillName}
|
|
---
|
|
|
|
# {displayName}
|
|
|
|
{if-memory}
|
|
Memory lives at {project-root}/_bmad/memory/{skillName}/.
|
|
{if-pulse}
|
|
Pulse is on.
|
|
{/if-pulse}
|
|
{/if-memory}
|
|
|
|
Always: {agent.name}
|
|
"""
|
|
|
|
|
|
def run(*args, template=TEMPLATE):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
path = Path(tmp) / "t.md"
|
|
path.write_text(template, encoding="utf-8")
|
|
res = subprocess.run([sys.executable, str(SCRIPT), str(path), *args], capture_output=True, text=True)
|
|
out_file = Path(tmp) / "out.md"
|
|
output = out_file.read_text(encoding="utf-8") if out_file.exists() else None
|
|
return res, output
|
|
|
|
|
|
class ProcessTemplateTest(unittest.TestCase):
|
|
def test_variables_and_false_block_removed(self):
|
|
res, _ = run("--var", "skillName=bmad-x", "--var", "displayName=X", "--json")
|
|
self.assertEqual(res.returncode, 0, res.stderr)
|
|
self.assertIn("name: bmad-x", res.stdout)
|
|
self.assertIn("# X", res.stdout)
|
|
self.assertNotIn("Memory lives", res.stdout)
|
|
self.assertNotIn("{if-", res.stdout)
|
|
self.assertNotIn("\n\n\n", res.stdout)
|
|
meta = json.loads(res.stderr)
|
|
self.assertEqual(meta["vars_substituted"], ["skillName", "displayName"])
|
|
self.assertEqual(meta["conditions_false"], ["memory"])
|
|
self.assertEqual(meta["conditions_true"], [])
|
|
self.assertEqual(meta["tokens_remaining"], ["{agent.name}"])
|
|
|
|
def test_nested_true_blocks_kept(self):
|
|
res, _ = run("--var", "skillName=bmad-x", "--true", "memory", "--true", "pulse", "--json")
|
|
self.assertEqual(res.returncode, 0, res.stderr)
|
|
self.assertIn("Memory lives at {project-root}/_bmad/memory/bmad-x/.", res.stdout)
|
|
self.assertIn("Pulse is on.", res.stdout)
|
|
meta = json.loads(res.stderr)
|
|
self.assertEqual(sorted(meta["conditions_true"]), ["memory", "pulse"])
|
|
self.assertIn("{project-root}", meta["tokens_remaining"])
|
|
|
|
def test_outer_true_inner_false(self):
|
|
res, _ = run("--true", "memory")
|
|
self.assertEqual(res.returncode, 0, res.stderr)
|
|
self.assertIn("Memory lives", res.stdout)
|
|
self.assertNotIn("Pulse is on.", res.stdout)
|
|
|
|
def test_output_file_prints_metadata_on_stdout(self):
|
|
with tempfile.TemporaryDirectory() as tmp:
|
|
path = Path(tmp) / "t.md"
|
|
out = Path(tmp) / "out.md"
|
|
path.write_text(TEMPLATE, encoding="utf-8")
|
|
res = subprocess.run(
|
|
[sys.executable, str(SCRIPT), str(path), "-o", str(out), "--var", "skillName=s"],
|
|
capture_output=True,
|
|
text=True,
|
|
)
|
|
self.assertEqual(res.returncode, 0, res.stderr)
|
|
meta = json.loads(res.stdout)
|
|
self.assertEqual(meta["output_file"], str(out))
|
|
self.assertIn("name: s", out.read_text(encoding="utf-8"))
|
|
|
|
def test_plain_substitution_leaves_jinja_anchors_and_guidance_alone(self):
|
|
template = (
|
|
'{% raw %}{{epic_number}}{% endraw %} {{ workflow.{selector} }} {% if workflow.route == "x" %}on{% endif %}\n'
|
|
"{project-root}/{skill-root}/{slug}/{output_folder}/{active_initiative} {workflow.lenses}\n"
|
|
"{purpose: one paragraph on what the skill is for}\n"
|
|
"name: {skillName}\n"
|
|
)
|
|
res, _ = run("--var", "selector=route", "--var", "skillName=bmad-x", "--json", template=template)
|
|
self.assertEqual(res.returncode, 0, res.stderr)
|
|
self.assertEqual(
|
|
res.stdout,
|
|
'{% raw %}{{epic_number}}{% endraw %} {{ workflow.route }} {% if workflow.route == "x" %}on{% endif %}\n'
|
|
"{project-root}/{skill-root}/{slug}/{output_folder}/{active_initiative} {workflow.lenses}\n"
|
|
"{purpose: one paragraph on what the skill is for}\n"
|
|
"name: bmad-x\n",
|
|
)
|
|
meta = json.loads(res.stderr)
|
|
self.assertEqual(meta["vars_substituted"], ["selector", "skillName"])
|
|
|
|
def test_leftover_marker_exits_3(self):
|
|
res, _ = run(template="start {if-a} unclosed")
|
|
self.assertEqual(res.returncode, 3)
|
|
self.assertIn("{if-a}", res.stderr)
|
|
|
|
def test_bad_var_format_is_usage_error(self):
|
|
res, _ = run("--var", "novalue")
|
|
self.assertEqual(res.returncode, 2)
|
|
|
|
def test_missing_template(self):
|
|
res = subprocess.run([sys.executable, str(SCRIPT), "/nonexistent/t.md"], capture_output=True, text=True)
|
|
self.assertEqual(res.returncode, 2)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|