fishertools 0.2.1__py3-none-any.whl → 0.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fishertools/__init__.py +16 -5
- fishertools/errors/__init__.py +11 -3
- fishertools/errors/exception_types.py +282 -0
- fishertools/errors/explainer.py +87 -1
- fishertools/errors/models.py +73 -1
- fishertools/errors/patterns.py +40 -0
- fishertools/examples/cli_example.py +156 -0
- fishertools/examples/learn_example.py +65 -0
- fishertools/examples/logger_example.py +176 -0
- fishertools/examples/menu_example.py +101 -0
- fishertools/examples/storage_example.py +175 -0
- fishertools/input_utils.py +185 -0
- fishertools/learn/__init__.py +19 -2
- fishertools/learn/examples.py +88 -1
- fishertools/learn/knowledge_engine.py +321 -0
- fishertools/learn/repl/__init__.py +19 -0
- fishertools/learn/repl/cli.py +31 -0
- fishertools/learn/repl/code_sandbox.py +229 -0
- fishertools/learn/repl/command_handler.py +544 -0
- fishertools/learn/repl/command_parser.py +165 -0
- fishertools/learn/repl/engine.py +479 -0
- fishertools/learn/repl/models.py +121 -0
- fishertools/learn/repl/session_manager.py +284 -0
- fishertools/learn/repl/test_code_sandbox.py +261 -0
- fishertools/learn/repl/test_code_sandbox_pbt.py +148 -0
- fishertools/learn/repl/test_command_handler.py +224 -0
- fishertools/learn/repl/test_command_handler_pbt.py +189 -0
- fishertools/learn/repl/test_command_parser.py +160 -0
- fishertools/learn/repl/test_command_parser_pbt.py +100 -0
- fishertools/learn/repl/test_engine.py +190 -0
- fishertools/learn/repl/test_session_manager.py +310 -0
- fishertools/learn/repl/test_session_manager_pbt.py +182 -0
- fishertools/learn/test_knowledge_engine.py +241 -0
- fishertools/learn/test_knowledge_engine_pbt.py +180 -0
- fishertools/patterns/__init__.py +46 -0
- fishertools/patterns/cli.py +175 -0
- fishertools/patterns/logger.py +140 -0
- fishertools/patterns/menu.py +99 -0
- fishertools/patterns/storage.py +127 -0
- fishertools/readme_transformer.py +631 -0
- fishertools/safe/__init__.py +6 -1
- fishertools/safe/files.py +329 -1
- fishertools/transform_readme.py +105 -0
- fishertools-0.4.0.dist-info/METADATA +104 -0
- fishertools-0.4.0.dist-info/RECORD +131 -0
- {fishertools-0.2.1.dist-info → fishertools-0.4.0.dist-info}/WHEEL +1 -1
- tests/test_documentation_properties.py +329 -0
- tests/test_documentation_structure.py +349 -0
- tests/test_errors/test_exception_types.py +446 -0
- tests/test_errors/test_exception_types_pbt.py +333 -0
- tests/test_errors/test_patterns.py +52 -0
- tests/test_input_utils/__init__.py +1 -0
- tests/test_input_utils/test_input_utils.py +65 -0
- tests/test_learn/test_examples.py +179 -1
- tests/test_learn/test_explain_properties.py +307 -0
- tests/test_patterns_cli.py +611 -0
- tests/test_patterns_docstrings.py +473 -0
- tests/test_patterns_logger.py +465 -0
- tests/test_patterns_menu.py +440 -0
- tests/test_patterns_storage.py +447 -0
- tests/test_readme_enhancements_v0_3_1.py +2036 -0
- tests/test_readme_transformer/__init__.py +1 -0
- tests/test_readme_transformer/test_readme_infrastructure.py +1023 -0
- tests/test_readme_transformer/test_transform_readme_integration.py +431 -0
- tests/test_safe/test_files.py +726 -1
- fishertools-0.2.1.dist-info/METADATA +0 -256
- fishertools-0.2.1.dist-info/RECORD +0 -81
- {fishertools-0.2.1.dist-info → fishertools-0.4.0.dist-info}/licenses/LICENSE +0 -0
- {fishertools-0.2.1.dist-info → fishertools-0.4.0.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,611 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Property-based tests for the SimpleCLI class in fishertools.patterns.
|
|
3
|
+
|
|
4
|
+
Tests the correctness properties of the SimpleCLI class using hypothesis
|
|
5
|
+
for property-based testing.
|
|
6
|
+
|
|
7
|
+
**Validates: Requirements 11.2, 11.3, 11.5**
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
from unittest.mock import patch, MagicMock
|
|
12
|
+
from io import StringIO
|
|
13
|
+
|
|
14
|
+
from fishertools.patterns.cli import SimpleCLI
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TestSimpleCLIExecutesCorrectHandler:
|
|
18
|
+
"""
|
|
19
|
+
Property 8: SimpleCLI Executes Correct Handler
|
|
20
|
+
|
|
21
|
+
For any registered command, calling SimpleCLI with that command should
|
|
22
|
+
execute the corresponding handler function.
|
|
23
|
+
|
|
24
|
+
**Validates: Requirements 11.2, 11.3**
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def test_executes_single_command(self):
|
|
28
|
+
"""Test that SimpleCLI executes a single registered command."""
|
|
29
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
30
|
+
mock_handler = MagicMock()
|
|
31
|
+
|
|
32
|
+
@cli.command("test", "Test command")
|
|
33
|
+
def test_cmd():
|
|
34
|
+
mock_handler()
|
|
35
|
+
|
|
36
|
+
cli.run(["test"])
|
|
37
|
+
|
|
38
|
+
mock_handler.assert_called_once()
|
|
39
|
+
|
|
40
|
+
def test_executes_correct_command_from_multiple(self):
|
|
41
|
+
"""Test that SimpleCLI executes the correct command from multiple options."""
|
|
42
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
43
|
+
mock_handler1 = MagicMock()
|
|
44
|
+
mock_handler2 = MagicMock()
|
|
45
|
+
mock_handler3 = MagicMock()
|
|
46
|
+
|
|
47
|
+
@cli.command("cmd1", "First command")
|
|
48
|
+
def cmd1():
|
|
49
|
+
mock_handler1()
|
|
50
|
+
|
|
51
|
+
@cli.command("cmd2", "Second command")
|
|
52
|
+
def cmd2():
|
|
53
|
+
mock_handler2()
|
|
54
|
+
|
|
55
|
+
@cli.command("cmd3", "Third command")
|
|
56
|
+
def cmd3():
|
|
57
|
+
mock_handler3()
|
|
58
|
+
|
|
59
|
+
# Execute cmd2
|
|
60
|
+
cli.run(["cmd2"])
|
|
61
|
+
|
|
62
|
+
# Only cmd2 should have been called
|
|
63
|
+
mock_handler1.assert_not_called()
|
|
64
|
+
mock_handler2.assert_called_once()
|
|
65
|
+
mock_handler3.assert_not_called()
|
|
66
|
+
|
|
67
|
+
def test_executes_command_with_arguments(self):
|
|
68
|
+
"""Test that SimpleCLI passes arguments to the command handler."""
|
|
69
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
70
|
+
mock_handler = MagicMock()
|
|
71
|
+
|
|
72
|
+
@cli.command("greet", "Greet someone")
|
|
73
|
+
def greet(name):
|
|
74
|
+
mock_handler(name)
|
|
75
|
+
|
|
76
|
+
cli.run(["greet", "Alice"])
|
|
77
|
+
|
|
78
|
+
mock_handler.assert_called_once_with("Alice")
|
|
79
|
+
|
|
80
|
+
def test_executes_command_with_multiple_arguments(self):
|
|
81
|
+
"""Test that SimpleCLI passes multiple arguments to the command handler."""
|
|
82
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
83
|
+
mock_handler = MagicMock()
|
|
84
|
+
|
|
85
|
+
@cli.command("add", "Add two numbers")
|
|
86
|
+
def add(a, b):
|
|
87
|
+
mock_handler(a, b)
|
|
88
|
+
|
|
89
|
+
cli.run(["add", "5", "3"])
|
|
90
|
+
|
|
91
|
+
mock_handler.assert_called_once_with("5", "3")
|
|
92
|
+
|
|
93
|
+
def test_executes_command_with_many_arguments(self):
|
|
94
|
+
"""Test that SimpleCLI passes many arguments to the command handler."""
|
|
95
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
96
|
+
mock_handler = MagicMock()
|
|
97
|
+
|
|
98
|
+
@cli.command("multi", "Multi-argument command")
|
|
99
|
+
def multi(a, b, c, d, e):
|
|
100
|
+
mock_handler(a, b, c, d, e)
|
|
101
|
+
|
|
102
|
+
cli.run(["multi", "1", "2", "3", "4", "5"])
|
|
103
|
+
|
|
104
|
+
mock_handler.assert_called_once_with("1", "2", "3", "4", "5")
|
|
105
|
+
|
|
106
|
+
def test_executes_command_with_no_arguments(self):
|
|
107
|
+
"""Test that SimpleCLI executes command with no arguments."""
|
|
108
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
109
|
+
mock_handler = MagicMock()
|
|
110
|
+
|
|
111
|
+
@cli.command("status", "Show status")
|
|
112
|
+
def status():
|
|
113
|
+
mock_handler()
|
|
114
|
+
|
|
115
|
+
cli.run(["status"])
|
|
116
|
+
|
|
117
|
+
mock_handler.assert_called_once()
|
|
118
|
+
|
|
119
|
+
def test_command_receives_arguments_as_strings(self):
|
|
120
|
+
"""Test that command arguments are passed as strings."""
|
|
121
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
122
|
+
received_args = []
|
|
123
|
+
|
|
124
|
+
@cli.command("echo", "Echo arguments")
|
|
125
|
+
def echo(*args):
|
|
126
|
+
received_args.extend(args)
|
|
127
|
+
|
|
128
|
+
cli.run(["echo", "hello", "123", "true"])
|
|
129
|
+
|
|
130
|
+
# All arguments should be strings
|
|
131
|
+
assert received_args == ["hello", "123", "true"]
|
|
132
|
+
assert all(isinstance(arg, str) for arg in received_args)
|
|
133
|
+
|
|
134
|
+
def test_executes_command_with_special_characters_in_args(self):
|
|
135
|
+
"""Test that SimpleCLI handles special characters in arguments."""
|
|
136
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
137
|
+
mock_handler = MagicMock()
|
|
138
|
+
|
|
139
|
+
@cli.command("special", "Handle special chars")
|
|
140
|
+
def special(arg):
|
|
141
|
+
mock_handler(arg)
|
|
142
|
+
|
|
143
|
+
cli.run(["special", "!@#$%^&*()"])
|
|
144
|
+
|
|
145
|
+
mock_handler.assert_called_once_with("!@#$%^&*()")
|
|
146
|
+
|
|
147
|
+
def test_executes_command_with_empty_string_argument(self):
|
|
148
|
+
"""Test that SimpleCLI handles empty string arguments."""
|
|
149
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
150
|
+
mock_handler = MagicMock()
|
|
151
|
+
|
|
152
|
+
@cli.command("empty", "Handle empty string")
|
|
153
|
+
def empty(arg):
|
|
154
|
+
mock_handler(arg)
|
|
155
|
+
|
|
156
|
+
cli.run(["empty", ""])
|
|
157
|
+
|
|
158
|
+
mock_handler.assert_called_once_with("")
|
|
159
|
+
|
|
160
|
+
def test_executes_command_with_space_in_argument(self):
|
|
161
|
+
"""Test that SimpleCLI handles arguments with spaces."""
|
|
162
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
163
|
+
mock_handler = MagicMock()
|
|
164
|
+
|
|
165
|
+
@cli.command("msg", "Send message")
|
|
166
|
+
def msg(text):
|
|
167
|
+
mock_handler(text)
|
|
168
|
+
|
|
169
|
+
cli.run(["msg", "hello world"])
|
|
170
|
+
|
|
171
|
+
mock_handler.assert_called_once_with("hello world")
|
|
172
|
+
|
|
173
|
+
def test_command_decorator_returns_function(self):
|
|
174
|
+
"""Test that command decorator returns the original function."""
|
|
175
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
176
|
+
|
|
177
|
+
@cli.command("test", "Test command")
|
|
178
|
+
def test_func():
|
|
179
|
+
return "result"
|
|
180
|
+
|
|
181
|
+
# The decorator should return the original function
|
|
182
|
+
assert test_func() == "result"
|
|
183
|
+
|
|
184
|
+
def test_multiple_commands_registered_independently(self):
|
|
185
|
+
"""Test that multiple commands are registered independently."""
|
|
186
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
187
|
+
results = []
|
|
188
|
+
|
|
189
|
+
@cli.command("cmd1", "First")
|
|
190
|
+
def cmd1():
|
|
191
|
+
results.append("cmd1")
|
|
192
|
+
|
|
193
|
+
@cli.command("cmd2", "Second")
|
|
194
|
+
def cmd2():
|
|
195
|
+
results.append("cmd2")
|
|
196
|
+
|
|
197
|
+
cli.run(["cmd1"])
|
|
198
|
+
cli.run(["cmd2"])
|
|
199
|
+
cli.run(["cmd1"])
|
|
200
|
+
|
|
201
|
+
assert results == ["cmd1", "cmd2", "cmd1"]
|
|
202
|
+
|
|
203
|
+
def test_command_with_return_value(self):
|
|
204
|
+
"""Test that command handlers can return values."""
|
|
205
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
206
|
+
|
|
207
|
+
@cli.command("get", "Get value")
|
|
208
|
+
def get_value():
|
|
209
|
+
return 42
|
|
210
|
+
|
|
211
|
+
# The handler should execute without error
|
|
212
|
+
cli.run(["get"])
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class TestSimpleCLIHandlesInvalidCommands:
|
|
216
|
+
"""
|
|
217
|
+
Property 9: SimpleCLI Handles Invalid Commands
|
|
218
|
+
|
|
219
|
+
For any invalid command, SimpleCLI should handle it gracefully without
|
|
220
|
+
crashing.
|
|
221
|
+
|
|
222
|
+
**Validates: Requirements 11.5**
|
|
223
|
+
"""
|
|
224
|
+
|
|
225
|
+
def test_handles_unknown_command(self):
|
|
226
|
+
"""Test that SimpleCLI handles unknown commands gracefully."""
|
|
227
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
228
|
+
|
|
229
|
+
@cli.command("known", "Known command")
|
|
230
|
+
def known():
|
|
231
|
+
pass
|
|
232
|
+
|
|
233
|
+
# Should not raise an exception
|
|
234
|
+
with patch('builtins.print') as mock_print:
|
|
235
|
+
cli.run(["unknown"])
|
|
236
|
+
|
|
237
|
+
# Should print an error message
|
|
238
|
+
printed_text = ' '.join(str(call) for call in mock_print.call_args_list)
|
|
239
|
+
assert "Error" in printed_text or "error" in printed_text.lower()
|
|
240
|
+
|
|
241
|
+
def test_handles_invalid_command_gracefully(self):
|
|
242
|
+
"""Test that SimpleCLI doesn't crash on invalid commands."""
|
|
243
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
244
|
+
|
|
245
|
+
@cli.command("valid", "Valid command")
|
|
246
|
+
def valid():
|
|
247
|
+
pass
|
|
248
|
+
|
|
249
|
+
# Should not raise an exception
|
|
250
|
+
try:
|
|
251
|
+
cli.run(["invalid"])
|
|
252
|
+
except Exception as e:
|
|
253
|
+
pytest.fail(f"SimpleCLI raised an exception: {e}")
|
|
254
|
+
|
|
255
|
+
def test_shows_help_on_invalid_command(self):
|
|
256
|
+
"""Test that SimpleCLI shows help when command is invalid."""
|
|
257
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
258
|
+
|
|
259
|
+
@cli.command("valid", "Valid command")
|
|
260
|
+
def valid():
|
|
261
|
+
pass
|
|
262
|
+
|
|
263
|
+
with patch('builtins.print') as mock_print:
|
|
264
|
+
cli.run(["invalid"])
|
|
265
|
+
|
|
266
|
+
# Should print help information
|
|
267
|
+
printed_text = ' '.join(str(call) for call in mock_print.call_args_list)
|
|
268
|
+
assert "Available commands" in printed_text or "valid" in printed_text.lower()
|
|
269
|
+
|
|
270
|
+
def test_handles_wrong_number_of_arguments(self):
|
|
271
|
+
"""Test that SimpleCLI handles wrong number of arguments gracefully."""
|
|
272
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
273
|
+
|
|
274
|
+
@cli.command("add", "Add two numbers")
|
|
275
|
+
def add(a, b):
|
|
276
|
+
pass
|
|
277
|
+
|
|
278
|
+
# Should not raise an exception
|
|
279
|
+
with patch('builtins.print') as mock_print:
|
|
280
|
+
cli.run(["add", "5"]) # Missing second argument
|
|
281
|
+
|
|
282
|
+
# Should print an error message
|
|
283
|
+
printed_text = ' '.join(str(call) for call in mock_print.call_args_list)
|
|
284
|
+
assert "Error" in printed_text or "error" in printed_text.lower()
|
|
285
|
+
|
|
286
|
+
def test_handles_too_many_arguments(self):
|
|
287
|
+
"""Test that SimpleCLI handles too many arguments gracefully."""
|
|
288
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
289
|
+
|
|
290
|
+
@cli.command("single", "Single argument command")
|
|
291
|
+
def single(arg):
|
|
292
|
+
pass
|
|
293
|
+
|
|
294
|
+
# Should not raise an exception
|
|
295
|
+
with patch('builtins.print') as mock_print:
|
|
296
|
+
cli.run(["single", "arg1", "arg2", "arg3"])
|
|
297
|
+
|
|
298
|
+
# Should print an error message
|
|
299
|
+
printed_text = ' '.join(str(call) for call in mock_print.call_args_list)
|
|
300
|
+
assert "Error" in printed_text or "error" in printed_text.lower()
|
|
301
|
+
|
|
302
|
+
def test_handles_empty_command_list(self):
|
|
303
|
+
"""Test that SimpleCLI handles empty command list gracefully."""
|
|
304
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
305
|
+
|
|
306
|
+
@cli.command("cmd", "Command")
|
|
307
|
+
def cmd():
|
|
308
|
+
pass
|
|
309
|
+
|
|
310
|
+
# Should not raise an exception
|
|
311
|
+
with patch('builtins.print') as mock_print:
|
|
312
|
+
cli.run([])
|
|
313
|
+
|
|
314
|
+
# Should show help
|
|
315
|
+
printed_text = ' '.join(str(call) for call in mock_print.call_args_list)
|
|
316
|
+
assert "Available commands" in printed_text or "test" in printed_text.lower()
|
|
317
|
+
|
|
318
|
+
def test_handles_help_flag(self):
|
|
319
|
+
"""Test that SimpleCLI handles --help flag gracefully."""
|
|
320
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
321
|
+
|
|
322
|
+
@cli.command("cmd", "Command")
|
|
323
|
+
def cmd():
|
|
324
|
+
pass
|
|
325
|
+
|
|
326
|
+
# Should not raise an exception
|
|
327
|
+
with patch('builtins.print') as mock_print:
|
|
328
|
+
cli.run(["--help"])
|
|
329
|
+
|
|
330
|
+
# Should show help
|
|
331
|
+
printed_text = ' '.join(str(call) for call in mock_print.call_args_list)
|
|
332
|
+
assert "Available commands" in printed_text
|
|
333
|
+
|
|
334
|
+
def test_handles_short_help_flag(self):
|
|
335
|
+
"""Test that SimpleCLI handles -h flag gracefully."""
|
|
336
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
337
|
+
|
|
338
|
+
@cli.command("cmd", "Command")
|
|
339
|
+
def cmd():
|
|
340
|
+
pass
|
|
341
|
+
|
|
342
|
+
# Should not raise an exception
|
|
343
|
+
with patch('builtins.print') as mock_print:
|
|
344
|
+
cli.run(["-h"])
|
|
345
|
+
|
|
346
|
+
# Should show help
|
|
347
|
+
printed_text = ' '.join(str(call) for call in mock_print.call_args_list)
|
|
348
|
+
assert "Available commands" in printed_text
|
|
349
|
+
|
|
350
|
+
def test_handles_command_that_raises_exception(self):
|
|
351
|
+
"""Test that SimpleCLI handles exceptions in command handlers."""
|
|
352
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
353
|
+
|
|
354
|
+
@cli.command("fail", "Failing command")
|
|
355
|
+
def fail():
|
|
356
|
+
raise ValueError("Command failed")
|
|
357
|
+
|
|
358
|
+
# Should not raise an exception to the caller
|
|
359
|
+
try:
|
|
360
|
+
cli.run(["fail"])
|
|
361
|
+
except ValueError:
|
|
362
|
+
pytest.fail("SimpleCLI should handle exceptions in handlers")
|
|
363
|
+
|
|
364
|
+
def test_handles_no_commands_registered(self):
|
|
365
|
+
"""Test that SimpleCLI handles case with no commands registered."""
|
|
366
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
367
|
+
|
|
368
|
+
# Should not raise an exception
|
|
369
|
+
with patch('builtins.print') as mock_print:
|
|
370
|
+
cli.run(["anything"])
|
|
371
|
+
|
|
372
|
+
# Should show error and help
|
|
373
|
+
printed_text = ' '.join(str(call) for call in mock_print.call_args_list)
|
|
374
|
+
assert "Error" in printed_text or "error" in printed_text.lower()
|
|
375
|
+
|
|
376
|
+
def test_handles_case_sensitive_commands(self):
|
|
377
|
+
"""Test that SimpleCLI treats commands as case-sensitive."""
|
|
378
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
379
|
+
mock_handler = MagicMock()
|
|
380
|
+
|
|
381
|
+
@cli.command("Test", "Test command")
|
|
382
|
+
def test_cmd():
|
|
383
|
+
mock_handler()
|
|
384
|
+
|
|
385
|
+
# Try with different case
|
|
386
|
+
with patch('builtins.print') as mock_print:
|
|
387
|
+
cli.run(["test"])
|
|
388
|
+
|
|
389
|
+
# Should not find the command (case-sensitive)
|
|
390
|
+
mock_handler.assert_not_called()
|
|
391
|
+
printed_text = ' '.join(str(call) for call in mock_print.call_args_list)
|
|
392
|
+
assert "Error" in printed_text or "error" in printed_text.lower()
|
|
393
|
+
|
|
394
|
+
def test_handles_command_with_special_characters(self):
|
|
395
|
+
"""Test that SimpleCLI handles commands with special characters."""
|
|
396
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
397
|
+
mock_handler = MagicMock()
|
|
398
|
+
|
|
399
|
+
@cli.command("test-cmd", "Test command")
|
|
400
|
+
def test_cmd():
|
|
401
|
+
mock_handler()
|
|
402
|
+
|
|
403
|
+
cli.run(["test-cmd"])
|
|
404
|
+
|
|
405
|
+
mock_handler.assert_called_once()
|
|
406
|
+
|
|
407
|
+
def test_handles_command_with_underscores(self):
|
|
408
|
+
"""Test that SimpleCLI handles commands with underscores."""
|
|
409
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
410
|
+
mock_handler = MagicMock()
|
|
411
|
+
|
|
412
|
+
@cli.command("test_cmd", "Test command")
|
|
413
|
+
def test_cmd():
|
|
414
|
+
mock_handler()
|
|
415
|
+
|
|
416
|
+
cli.run(["test_cmd"])
|
|
417
|
+
|
|
418
|
+
mock_handler.assert_called_once()
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
class TestSimpleCLIHelpDisplay:
|
|
422
|
+
"""Test help display functionality."""
|
|
423
|
+
|
|
424
|
+
def test_shows_application_name(self):
|
|
425
|
+
"""Test that help displays the application name."""
|
|
426
|
+
cli = SimpleCLI("MyApp", "My application")
|
|
427
|
+
|
|
428
|
+
with patch('builtins.print') as mock_print:
|
|
429
|
+
cli.run(["--help"])
|
|
430
|
+
|
|
431
|
+
printed_text = ' '.join(str(call) for call in mock_print.call_args_list)
|
|
432
|
+
assert "MyApp" in printed_text
|
|
433
|
+
|
|
434
|
+
def test_shows_application_description(self):
|
|
435
|
+
"""Test that help displays the application description."""
|
|
436
|
+
cli = SimpleCLI("MyApp", "This is my application")
|
|
437
|
+
|
|
438
|
+
with patch('builtins.print') as mock_print:
|
|
439
|
+
cli.run(["--help"])
|
|
440
|
+
|
|
441
|
+
printed_text = ' '.join(str(call) for call in mock_print.call_args_list)
|
|
442
|
+
assert "This is my application" in printed_text
|
|
443
|
+
|
|
444
|
+
def test_shows_all_commands(self):
|
|
445
|
+
"""Test that help displays all registered commands."""
|
|
446
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
447
|
+
|
|
448
|
+
@cli.command("cmd1", "First command")
|
|
449
|
+
def cmd1():
|
|
450
|
+
pass
|
|
451
|
+
|
|
452
|
+
@cli.command("cmd2", "Second command")
|
|
453
|
+
def cmd2():
|
|
454
|
+
pass
|
|
455
|
+
|
|
456
|
+
@cli.command("cmd3", "Third command")
|
|
457
|
+
def cmd3():
|
|
458
|
+
pass
|
|
459
|
+
|
|
460
|
+
with patch('builtins.print') as mock_print:
|
|
461
|
+
cli.run(["--help"])
|
|
462
|
+
|
|
463
|
+
printed_text = ' '.join(str(call) for call in mock_print.call_args_list)
|
|
464
|
+
assert "cmd1" in printed_text
|
|
465
|
+
assert "cmd2" in printed_text
|
|
466
|
+
assert "cmd3" in printed_text
|
|
467
|
+
|
|
468
|
+
def test_shows_command_descriptions(self):
|
|
469
|
+
"""Test that help displays command descriptions."""
|
|
470
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
471
|
+
|
|
472
|
+
@cli.command("greet", "Greet someone")
|
|
473
|
+
def greet():
|
|
474
|
+
pass
|
|
475
|
+
|
|
476
|
+
@cli.command("goodbye", "Say goodbye")
|
|
477
|
+
def goodbye():
|
|
478
|
+
pass
|
|
479
|
+
|
|
480
|
+
with patch('builtins.print') as mock_print:
|
|
481
|
+
cli.run(["--help"])
|
|
482
|
+
|
|
483
|
+
printed_text = ' '.join(str(call) for call in mock_print.call_args_list)
|
|
484
|
+
assert "Greet someone" in printed_text
|
|
485
|
+
assert "Say goodbye" in printed_text
|
|
486
|
+
|
|
487
|
+
def test_shows_available_commands_header(self):
|
|
488
|
+
"""Test that help shows 'Available commands' header."""
|
|
489
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
490
|
+
|
|
491
|
+
@cli.command("cmd", "Command")
|
|
492
|
+
def cmd():
|
|
493
|
+
pass
|
|
494
|
+
|
|
495
|
+
with patch('builtins.print') as mock_print:
|
|
496
|
+
cli.run(["--help"])
|
|
497
|
+
|
|
498
|
+
printed_text = ' '.join(str(call) for call in mock_print.call_args_list)
|
|
499
|
+
assert "Available commands" in printed_text
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
class TestSimpleCLIInitialization:
|
|
503
|
+
"""Test SimpleCLI initialization."""
|
|
504
|
+
|
|
505
|
+
def test_stores_name(self):
|
|
506
|
+
"""Test that SimpleCLI stores the application name."""
|
|
507
|
+
cli = SimpleCLI("MyApp", "Description")
|
|
508
|
+
assert cli.name == "MyApp"
|
|
509
|
+
|
|
510
|
+
def test_stores_description(self):
|
|
511
|
+
"""Test that SimpleCLI stores the application description."""
|
|
512
|
+
cli = SimpleCLI("MyApp", "My description")
|
|
513
|
+
assert cli.description == "My description"
|
|
514
|
+
|
|
515
|
+
def test_initializes_empty_commands(self):
|
|
516
|
+
"""Test that SimpleCLI initializes with empty commands."""
|
|
517
|
+
cli = SimpleCLI("MyApp", "Description")
|
|
518
|
+
assert cli.commands == {}
|
|
519
|
+
|
|
520
|
+
def test_has_docstring(self):
|
|
521
|
+
"""Test that SimpleCLI has a docstring."""
|
|
522
|
+
assert SimpleCLI.__doc__ is not None
|
|
523
|
+
assert len(SimpleCLI.__doc__) > 0
|
|
524
|
+
|
|
525
|
+
def test_command_method_has_docstring(self):
|
|
526
|
+
"""Test that command method has a docstring."""
|
|
527
|
+
cli = SimpleCLI("test", "test")
|
|
528
|
+
assert cli.command.__doc__ is not None
|
|
529
|
+
assert len(cli.command.__doc__) > 0
|
|
530
|
+
|
|
531
|
+
def test_run_method_has_docstring(self):
|
|
532
|
+
"""Test that run method has a docstring."""
|
|
533
|
+
cli = SimpleCLI("test", "test")
|
|
534
|
+
assert cli.run.__doc__ is not None
|
|
535
|
+
assert len(cli.run.__doc__) > 0
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
class TestSimpleCLIIntegration:
|
|
539
|
+
"""Integration tests for SimpleCLI."""
|
|
540
|
+
|
|
541
|
+
def test_full_workflow(self):
|
|
542
|
+
"""Test a complete workflow with SimpleCLI."""
|
|
543
|
+
cli = SimpleCLI("calculator", "Simple calculator")
|
|
544
|
+
results = []
|
|
545
|
+
|
|
546
|
+
@cli.command("add", "Add two numbers")
|
|
547
|
+
def add(a, b):
|
|
548
|
+
results.append(int(a) + int(b))
|
|
549
|
+
|
|
550
|
+
@cli.command("multiply", "Multiply two numbers")
|
|
551
|
+
def multiply(a, b):
|
|
552
|
+
results.append(int(a) * int(b))
|
|
553
|
+
|
|
554
|
+
cli.run(["add", "5", "3"])
|
|
555
|
+
cli.run(["multiply", "4", "2"])
|
|
556
|
+
|
|
557
|
+
assert results == [8, 8]
|
|
558
|
+
|
|
559
|
+
def test_multiple_cli_instances(self):
|
|
560
|
+
"""Test that multiple SimpleCLI instances work independently."""
|
|
561
|
+
cli1 = SimpleCLI("app1", "App 1")
|
|
562
|
+
cli2 = SimpleCLI("app2", "App 2")
|
|
563
|
+
|
|
564
|
+
results = []
|
|
565
|
+
|
|
566
|
+
@cli1.command("cmd", "Command")
|
|
567
|
+
def cmd1():
|
|
568
|
+
results.append("cli1")
|
|
569
|
+
|
|
570
|
+
@cli2.command("cmd", "Command")
|
|
571
|
+
def cmd2():
|
|
572
|
+
results.append("cli2")
|
|
573
|
+
|
|
574
|
+
cli1.run(["cmd"])
|
|
575
|
+
cli2.run(["cmd"])
|
|
576
|
+
|
|
577
|
+
assert results == ["cli1", "cli2"]
|
|
578
|
+
|
|
579
|
+
def test_command_with_side_effects(self):
|
|
580
|
+
"""Test that commands with side effects work correctly."""
|
|
581
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
582
|
+
state = {"value": 0}
|
|
583
|
+
|
|
584
|
+
@cli.command("increment", "Increment value")
|
|
585
|
+
def increment():
|
|
586
|
+
state["value"] += 1
|
|
587
|
+
|
|
588
|
+
@cli.command("double", "Double value")
|
|
589
|
+
def double():
|
|
590
|
+
state["value"] *= 2
|
|
591
|
+
|
|
592
|
+
cli.run(["increment"])
|
|
593
|
+
cli.run(["double"])
|
|
594
|
+
cli.run(["increment"])
|
|
595
|
+
|
|
596
|
+
assert state["value"] == 3 # (0 + 1) * 2 + 1
|
|
597
|
+
|
|
598
|
+
def test_cli_with_no_arguments_shows_help(self):
|
|
599
|
+
"""Test that running CLI with no arguments shows help."""
|
|
600
|
+
cli = SimpleCLI("test", "Test CLI")
|
|
601
|
+
|
|
602
|
+
@cli.command("cmd", "Command")
|
|
603
|
+
def cmd():
|
|
604
|
+
pass
|
|
605
|
+
|
|
606
|
+
with patch('builtins.print') as mock_print:
|
|
607
|
+
cli.run([])
|
|
608
|
+
|
|
609
|
+
printed_text = ' '.join(str(call) for call in mock_print.call_args_list)
|
|
610
|
+
assert "Available commands" in printed_text
|
|
611
|
+
|