forked from yusufkaraaslan/Skill_Seekers
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest_terminal_detection.py
More file actions
337 lines (245 loc) · 12.4 KB
/
test_terminal_detection.py
File metadata and controls
337 lines (245 loc) · 12.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
"""
Tests for terminal detection functionality in enhance_skill_local.py
This module tests the detect_terminal_app() function and terminal launching logic
to ensure correct terminal selection across different environments.
"""
import os
import sys
import unittest
from pathlib import Path
from unittest.mock import MagicMock, patch
# Add parent directory to path for imports
sys.path.insert(0, str(Path(__file__).parent.parent))
from skill_seekers.cli.enhance_skill_local import LocalSkillEnhancer, detect_terminal_app
class TestDetectTerminalApp(unittest.TestCase):
"""Test the detect_terminal_app() function."""
original_skill_seeker: str | None = None
original_term_program: str | None = None
def setUp(self):
"""Save original environment variables."""
self.original_skill_seeker = os.environ.get("SKILL_SEEKER_TERMINAL")
self.original_term_program = os.environ.get("TERM_PROGRAM")
def tearDown(self):
"""Restore original environment variables."""
# Remove test env vars
if "SKILL_SEEKER_TERMINAL" in os.environ:
del os.environ["SKILL_SEEKER_TERMINAL"]
if "TERM_PROGRAM" in os.environ:
del os.environ["TERM_PROGRAM"]
# Restore originals if they existed
if self.original_skill_seeker is not None:
os.environ["SKILL_SEEKER_TERMINAL"] = self.original_skill_seeker
if self.original_term_program is not None:
os.environ["TERM_PROGRAM"] = self.original_term_program
# HIGH PRIORITY TESTS
def test_detect_terminal_with_skill_seeker_env(self):
"""Test that SKILL_SEEKER_TERMINAL env var takes highest priority."""
os.environ["SKILL_SEEKER_TERMINAL"] = "Ghostty"
terminal_app, detection_method = detect_terminal_app()
self.assertEqual(terminal_app, "Ghostty")
self.assertEqual(detection_method, "SKILL_SEEKER_TERMINAL")
def test_detect_terminal_with_term_program_known(self):
"""Test detection from TERM_PROGRAM with known terminal (iTerm)."""
# Ensure SKILL_SEEKER_TERMINAL is not set
if "SKILL_SEEKER_TERMINAL" in os.environ:
del os.environ["SKILL_SEEKER_TERMINAL"]
os.environ["TERM_PROGRAM"] = "iTerm.app"
terminal_app, detection_method = detect_terminal_app()
self.assertEqual(terminal_app, "iTerm")
self.assertEqual(detection_method, "TERM_PROGRAM")
def test_detect_terminal_with_term_program_ghostty(self):
"""Test detection from TERM_PROGRAM with Ghostty terminal."""
if "SKILL_SEEKER_TERMINAL" in os.environ:
del os.environ["SKILL_SEEKER_TERMINAL"]
os.environ["TERM_PROGRAM"] = "ghostty"
terminal_app, detection_method = detect_terminal_app()
self.assertEqual(terminal_app, "Ghostty")
self.assertEqual(detection_method, "TERM_PROGRAM")
def test_detect_terminal_with_term_program_apple_terminal(self):
"""Test detection from TERM_PROGRAM with Apple Terminal."""
if "SKILL_SEEKER_TERMINAL" in os.environ:
del os.environ["SKILL_SEEKER_TERMINAL"]
os.environ["TERM_PROGRAM"] = "Apple_Terminal"
terminal_app, detection_method = detect_terminal_app()
self.assertEqual(terminal_app, "Terminal")
self.assertEqual(detection_method, "TERM_PROGRAM")
def test_detect_terminal_with_term_program_wezterm(self):
"""Test detection from TERM_PROGRAM with WezTerm."""
if "SKILL_SEEKER_TERMINAL" in os.environ:
del os.environ["SKILL_SEEKER_TERMINAL"]
os.environ["TERM_PROGRAM"] = "WezTerm"
terminal_app, detection_method = detect_terminal_app()
self.assertEqual(terminal_app, "WezTerm")
self.assertEqual(detection_method, "TERM_PROGRAM")
def test_detect_terminal_with_term_program_unknown(self):
"""Test fallback behavior when TERM_PROGRAM is unknown (e.g., IDE terminals)."""
if "SKILL_SEEKER_TERMINAL" in os.environ:
del os.environ["SKILL_SEEKER_TERMINAL"]
os.environ["TERM_PROGRAM"] = "zed"
terminal_app, detection_method = detect_terminal_app()
self.assertEqual(terminal_app, "Terminal")
self.assertEqual(detection_method, "unknown TERM_PROGRAM (zed)")
def test_detect_terminal_default_fallback(self):
"""Test default fallback when no environment variables are set."""
# Remove both env vars
if "SKILL_SEEKER_TERMINAL" in os.environ:
del os.environ["SKILL_SEEKER_TERMINAL"]
if "TERM_PROGRAM" in os.environ:
del os.environ["TERM_PROGRAM"]
terminal_app, detection_method = detect_terminal_app()
self.assertEqual(terminal_app, "Terminal")
self.assertEqual(detection_method, "default")
def test_detect_terminal_priority_order(self):
"""Test that SKILL_SEEKER_TERMINAL takes priority over TERM_PROGRAM."""
os.environ["SKILL_SEEKER_TERMINAL"] = "Ghostty"
os.environ["TERM_PROGRAM"] = "iTerm.app"
terminal_app, detection_method = detect_terminal_app()
# SKILL_SEEKER_TERMINAL should win
self.assertEqual(terminal_app, "Ghostty")
self.assertEqual(detection_method, "SKILL_SEEKER_TERMINAL")
@patch("subprocess.Popen")
def test_subprocess_popen_called_with_correct_args(self, mock_popen):
"""Test that subprocess.Popen is called with correct arguments on macOS."""
# Only test on macOS
if sys.platform != "darwin":
self.skipTest("This test only runs on macOS")
# Setup
os.environ["SKILL_SEEKER_TERMINAL"] = "Ghostty"
# Create a test skill directory with minimal setup
import tempfile
with tempfile.TemporaryDirectory() as tmpdir:
skill_dir = Path(tmpdir) / "test_skill"
skill_dir.mkdir()
# Create references directory (required by LocalSkillEnhancer)
(skill_dir / "references").mkdir()
(skill_dir / "references" / "test.md").write_text("# Test")
# Create SKILL.md (required)
(skill_dir / "SKILL.md").write_text("---\nname: test\n---\n# Test")
# Mock Popen to prevent actual terminal launch
mock_popen.return_value = MagicMock()
# Run enhancer in interactive mode (not headless)
enhancer = LocalSkillEnhancer(skill_dir)
_result = enhancer.run(headless=False)
# Verify Popen was called
self.assertTrue(mock_popen.called)
# Verify call arguments
call_args = mock_popen.call_args[0][0]
self.assertEqual(call_args[0], "open")
self.assertEqual(call_args[1], "-a")
self.assertEqual(call_args[2], "Ghostty")
# call_args[3] should be the script file path
self.assertTrue(call_args[3].endswith(".sh"))
# MEDIUM PRIORITY TESTS
def test_detect_terminal_whitespace_handling(self):
"""Test that whitespace is stripped from environment variables."""
os.environ["SKILL_SEEKER_TERMINAL"] = " Ghostty "
terminal_app, detection_method = detect_terminal_app()
self.assertEqual(terminal_app, "Ghostty")
self.assertEqual(detection_method, "SKILL_SEEKER_TERMINAL")
def test_detect_terminal_empty_string_env_vars(self):
"""Test that empty string env vars fall through to next priority."""
os.environ["SKILL_SEEKER_TERMINAL"] = ""
os.environ["TERM_PROGRAM"] = "iTerm.app"
terminal_app, detection_method = detect_terminal_app()
# Should skip empty SKILL_SEEKER_TERMINAL and use TERM_PROGRAM
self.assertEqual(terminal_app, "iTerm")
self.assertEqual(detection_method, "TERM_PROGRAM")
def test_detect_terminal_empty_string_both_vars(self):
"""Test that empty strings on both vars falls back to default."""
os.environ["SKILL_SEEKER_TERMINAL"] = ""
os.environ["TERM_PROGRAM"] = ""
terminal_app, detection_method = detect_terminal_app()
# Should fall back to default
self.assertEqual(terminal_app, "Terminal")
# Empty TERM_PROGRAM should be treated as not set
self.assertEqual(detection_method, "default")
@patch("subprocess.Popen")
def test_terminal_launch_error_handling(self, mock_popen):
"""Test error handling when terminal launch fails."""
# Only test on macOS
if sys.platform != "darwin":
self.skipTest("This test only runs on macOS")
# Setup Popen to raise exception
mock_popen.side_effect = Exception("Terminal not found")
import tempfile
with tempfile.TemporaryDirectory() as tmpdir:
skill_dir = Path(tmpdir) / "test_skill"
skill_dir.mkdir()
(skill_dir / "references").mkdir()
(skill_dir / "references" / "test.md").write_text("# Test")
(skill_dir / "SKILL.md").write_text("---\nname: test\n---\n# Test")
enhancer = LocalSkillEnhancer(skill_dir)
# Capture stdout to check error message
from io import StringIO
captured_output = StringIO()
old_stdout = sys.stdout
sys.stdout = captured_output
# Run in interactive mode (not headless) to test terminal launch
result = enhancer.run(headless=False)
# Restore stdout
sys.stdout = old_stdout
# Should return False on error
self.assertFalse(result)
# Should print error message
output = captured_output.getvalue()
self.assertIn("Error launching", output)
def test_output_message_unknown_terminal(self):
"""Test that unknown terminal prints warning message."""
if sys.platform != "darwin":
self.skipTest("This test only runs on macOS")
os.environ["TERM_PROGRAM"] = "vscode"
if "SKILL_SEEKER_TERMINAL" in os.environ:
del os.environ["SKILL_SEEKER_TERMINAL"]
import tempfile
with tempfile.TemporaryDirectory() as tmpdir:
skill_dir = Path(tmpdir) / "test_skill"
skill_dir.mkdir()
(skill_dir / "references").mkdir()
(skill_dir / "references" / "test.md").write_text("# Test")
(skill_dir / "SKILL.md").write_text("---\nname: test\n---\n# Test")
enhancer = LocalSkillEnhancer(skill_dir)
# Capture stdout
from io import StringIO
captured_output = StringIO()
old_stdout = sys.stdout
sys.stdout = captured_output
# Mock Popen to prevent actual launch
with patch("subprocess.Popen") as mock_popen:
mock_popen.return_value = MagicMock()
# Run in interactive mode (not headless) to test terminal detection
enhancer.run(headless=False)
# Restore stdout
sys.stdout = old_stdout
output = captured_output.getvalue()
# Should contain warning about unknown terminal
self.assertIn("⚠️", output)
self.assertIn("unknown TERM_PROGRAM", output)
self.assertIn("vscode", output)
self.assertIn("Using Terminal.app as fallback", output)
class TestTerminalMapCompleteness(unittest.TestCase):
"""Test that TERMINAL_MAP covers all documented terminals."""
def test_terminal_map_has_all_documented_terminals(self):
"""Verify TERMINAL_MAP contains all terminals mentioned in documentation."""
from skill_seekers.cli.enhance_skill_local import detect_terminal_app
# Get the TERMINAL_MAP from the function's scope
# We need to test this indirectly by checking each known terminal
known_terminals = [
("Apple_Terminal", "Terminal"),
("iTerm.app", "iTerm"),
("ghostty", "Ghostty"),
("WezTerm", "WezTerm"),
]
for term_program_value, expected_app_name in known_terminals:
# Set TERM_PROGRAM and verify detection
os.environ["TERM_PROGRAM"] = term_program_value
if "SKILL_SEEKER_TERMINAL" in os.environ:
del os.environ["SKILL_SEEKER_TERMINAL"]
terminal_app, detection_method = detect_terminal_app()
self.assertEqual(
terminal_app,
expected_app_name,
f"TERM_PROGRAM='{term_program_value}' should map to '{expected_app_name}'",
)
self.assertEqual(detection_method, "TERM_PROGRAM")
if __name__ == "__main__":
unittest.main()