main
py 164 lines 5.12 KB
Raw
1 """Tests for scripts/render_topic_prompt.py."""
2
3 from __future__ import annotations
4
5 import sys
6 from pathlib import Path
7 from unittest.mock import patch
8
9 sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
10
11 from scripts.render_topic_prompt import (
12 _keep_blocks,
13 _remove_blocks,
14 load_wisdom,
15 render_template,
16 )
17
18 SAMPLE_TEMPLATE = """\
19 # Analysis
20
21 {{#IF_TOPIC}}
22 Topic: {{TOPIC_NAME}}
23 Description: {{TOPIC_DESCRIPTION}}
24 ID: {{TOPIC_ID}}
25 Wisdom: {{WISDOM_CONTENT}}
26 {{/IF_TOPIC}}
27 {{#IF_NO_TOPIC}}
28 General analysis mode.
29 {{/IF_NO_TOPIC}}
30
31 ## Common section
32 This appears always.
33 """
34
35
36 class TestRenderTemplateWithTopic:
37 """Template renders correctly when topic config is provided."""
38
39 def test_topic_placeholders_replaced(self):
40 config = {
41 "id": "ai-ml",
42 "name": "AI/ML",
43 "description": "Artificial intelligence and machine learning",
44 }
45 rendered = render_template(SAMPLE_TEMPLATE, config)
46
47 assert "AI/ML" in rendered
48 assert "Artificial intelligence and machine learning" in rendered
49 assert "ai-ml" in rendered
50
51 def test_if_topic_block_included(self):
52 config = {"id": "devops", "name": "DevOps", "description": "CI/CD and infrastructure"}
53 rendered = render_template(SAMPLE_TEMPLATE, config)
54
55 assert "Topic: DevOps" in rendered
56 assert "Description: CI/CD and infrastructure" in rendered
57
58 def test_if_no_topic_block_removed(self):
59 config = {"id": "devops", "name": "DevOps", "description": "CI/CD and infrastructure"}
60 rendered = render_template(SAMPLE_TEMPLATE, config)
61
62 assert "General analysis mode." not in rendered
63
64 def test_common_section_preserved(self):
65 config = {"id": "security", "name": "Security", "description": "AppSec and infra security"}
66 rendered = render_template(SAMPLE_TEMPLATE, config)
67
68 assert "## Common section" in rendered
69 assert "This appears always." in rendered
70
71 def test_wisdom_placeholder_filled_when_no_wisdom_file(self):
72 config = {"id": "nonexistent-topic", "name": "Test", "description": "Test topic"}
73 rendered = render_template(SAMPLE_TEMPLATE, config)
74
75 assert "(No per-topic wisdom accumulated yet.)" in rendered
76
77 def test_wisdom_content_injected(self):
78 config = {"id": "ai-ml", "name": "AI/ML", "description": "AI stuff"}
79 wisdom_text = "Look for transformer architectures and real benchmarks."
80
81 with patch("scripts.render_topic_prompt.load_wisdom", return_value=wisdom_text):
82 rendered = render_template(SAMPLE_TEMPLATE, config)
83
84 assert "Look for transformer architectures" in rendered
85
86 def test_frontmatter_topic_field(self):
87 template_with_frontmatter = """\
88 {{#IF_TOPIC}}
89 - `topic` (value: `{{TOPIC_ID}}`)
90 {{/IF_TOPIC}}
91 """
92 config = {"id": "rust", "name": "Rust", "description": "Rust ecosystem"}
93 rendered = render_template(template_with_frontmatter, config)
94
95 assert "`rust`" in rendered
96
97
98 class TestRenderTemplateWithoutTopic:
99 """Template works without topic config (backward compatibility)."""
100
101 def test_none_config_uses_general_mode(self):
102 rendered = render_template(SAMPLE_TEMPLATE, None)
103
104 assert "General analysis mode." in rendered
105
106 def test_empty_config_uses_general_mode(self):
107 rendered = render_template(SAMPLE_TEMPLATE, {})
108
109 assert "General analysis mode." in rendered
110
111 def test_topic_blocks_removed(self):
112 rendered = render_template(SAMPLE_TEMPLATE, None)
113
114 assert "{{TOPIC_NAME}}" not in rendered
115 assert "{{TOPIC_DESCRIPTION}}" not in rendered
116 assert "{{TOPIC_ID}}" not in rendered
117
118 def test_common_section_preserved(self):
119 rendered = render_template(SAMPLE_TEMPLATE, None)
120
121 assert "## Common section" in rendered
122 assert "This appears always." in rendered
123
124 def test_no_conditional_markers_remain(self):
125 rendered = render_template(SAMPLE_TEMPLATE, None)
126
127 assert "{{#IF_TOPIC}}" not in rendered
128 assert "{{/IF_TOPIC}}" not in rendered
129 assert "{{#IF_NO_TOPIC}}" not in rendered
130 assert "{{/IF_NO_TOPIC}}" not in rendered
131
132
133 class TestLoadWisdom:
134 """Missing wisdom file handled gracefully."""
135
136 def test_missing_wisdom_returns_empty(self):
137 result = load_wisdom("topic-that-does-not-exist-xyz")
138 assert result == ""
139
140 def test_none_topic_id_returns_empty(self):
141 result = load_wisdom(None)
142 assert result == ""
143
144 def test_empty_topic_id_returns_empty(self):
145 result = load_wisdom("")
146 assert result == ""
147
148
149 class TestBlockHelpers:
150 """Unit tests for block manipulation helpers."""
151
152 def test_remove_blocks(self):
153 text = "before\n{{#FOO}}\ninner\n{{/FOO}}\nafter"
154 result = _remove_blocks(text, "FOO")
155 assert "inner" not in result
156 assert "before" in result
157 assert "after" in result
158
159 def test_keep_blocks(self):
160 text = "before\n{{#FOO}}\ninner\n{{/FOO}}\nafter"
161 result = _keep_blocks(text, "FOO")
162 assert "inner" in result
163 assert "{{#FOO}}" not in result
164 assert "{{/FOO}}" not in result