| """ |
| 测试增强CLI界面的命令补全功能 |
| """ |
| |
| import pytest |
| import asyncio |
| from unittest.mock import Mock, patch, AsyncMock |
| from prompt_toolkit.completion import WordCompleter |
| from prompt_toolkit.shortcuts import CompleteStyle |
| |
| import sys |
| import os |
| sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../../src')) |
| |
| from claude_agent.cli.enhanced_interface import EnhancedCLIInterface |
| |
| |
| class TestEnhancedCLICommandCompletion: |
| """测试增强CLI界面的命令补全功能""" |
| |
| def setup_method(self): |
| """设置测试""" |
| self.cli = EnhancedCLIInterface() |
| |
| def test_command_completer_initialization(self): |
| """测试命令补全器初始化""" |
| # 验证补全器存在 |
| assert self.cli.command_completer is not None |
| assert isinstance(self.cli.command_completer, WordCompleter) |
| |
| # 验证sentence模式已启用 |
| assert hasattr(self.cli.command_completer, 'sentence') |
| assert self.cli.command_completer.sentence is True |
| |
| def test_command_completer_words(self): |
| """测试命令补全器包含正确的命令""" |
| expected_commands = [ |
| '/help', '/mode', '/history', '/clear', '/tools', '/status', '/quit', '/exit', |
| '/sshout', '/sshout connect', '/sshout disconnect', '/sshout status', '/sshout send' |
| ] |
| |
| # 获取补全器的词列表 |
| completer_words = self.cli.command_completer.words |
| |
| # 验证所有期望的命令都存在 |
| for command in expected_commands: |
| assert command in completer_words |
| |
| def test_session_has_completer(self): |
| """测试prompt session包含补全器""" |
| # 验证session存在且配置了补全器 |
| assert self.cli.session is not None |
| assert self.cli.session.completer is self.cli.command_completer |
| |
| def test_session_complete_style(self): |
| """测试session的补全样式配置""" |
| assert self.cli.session.complete_style == CompleteStyle.MULTI_COLUMN |
| |
| def test_session_history_configured(self): |
| """测试session的历史功能配置""" |
| assert self.cli.session.history is self.cli.history |
| assert self.cli.history is not None |
| |
| def test_command_completion_integration(self): |
| """测试命令补全的集成""" |
| # 测试补全器是否可以正确处理部分命令 |
| from prompt_toolkit.document import Document |
| |
| # 测试基本命令补全 |
| document = Document('/he') |
| completions = list(self.cli.command_completer.get_completions(document, None)) |
| |
| # 应该包含 /help |
| completion_texts = [comp.text for comp in completions] |
| assert '/help' in completion_texts |
| |
| def test_sshout_command_completion(self): |
| """测试SSHOUT命令补全""" |
| from prompt_toolkit.document import Document |
| |
| # 测试SSHOUT子命令补全 |
| document = Document('/sshout con') |
| completions = list(self.cli.command_completer.get_completions(document, None)) |
| |
| # 应该包含 /sshout connect |
| completion_texts = [comp.text for comp in completions] |
| assert '/sshout connect' in completion_texts |
| |
| def test_empty_input_completion(self): |
| """测试空输入的补全""" |
| from prompt_toolkit.document import Document |
| |
| # 测试空文档 |
| document = Document('') |
| completions = list(self.cli.command_completer.get_completions(document, None)) |
| |
| # 空输入应该显示所有可能的命令 |
| assert len(completions) > 0 |
| |
| def test_slash_prefix_completion(self): |
| """测试斜杠前缀补全""" |
| from prompt_toolkit.document import Document |
| |
| # 测试只输入斜杠 |
| document = Document('/') |
| completions = list(self.cli.command_completer.get_completions(document, None)) |
| |
| # 应该显示所有以/开头的命令 |
| completion_texts = [comp.text for comp in completions] |
| assert '/help' in completion_texts |
| assert '/mode' in completion_texts |
| assert '/quit' in completion_texts |
| |
| def test_non_command_input_completion(self): |
| """测试非命令输入的补全""" |
| from prompt_toolkit.document import Document |
| |
| # 测试非命令输入(不以/开头) |
| document = Document('hello') |
| completions = list(self.cli.command_completer.get_completions(document, None)) |
| |
| # 非命令输入不应该有补全 |
| assert len(completions) == 0 |
| |
| def test_partial_command_completion(self): |
| """测试部分命令补全""" |
| from prompt_toolkit.document import Document |
| |
| test_cases = [ |
| ('/he', '/help'), # /help |
| ('/mo', '/mode'), # /mode |
| ('/st', '/status'), # /status |
| ('/hi', '/history'), # /history |
| ('/cl', '/clear'), # /clear |
| ('/to', '/tools'), # /tools |
| ('/qu', '/quit'), # /quit |
| ] |
| |
| for partial_input, expected_completion in test_cases: |
| document = Document(partial_input) |
| completions = list(self.cli.command_completer.get_completions(document, None)) |
| completion_texts = [comp.text for comp in completions] |
| assert expected_completion in completion_texts, f"Expected '{expected_completion}' for input '{partial_input}'" |
| |
| @pytest.mark.asyncio |
| async def test_get_user_input_with_completion(self): |
| """测试get_user_input方法包含补全功能""" |
| # Mock session的prompt_async方法 |
| with patch.object(self.cli.session, 'prompt_async', new_callable=AsyncMock) as mock_prompt: |
| mock_prompt.return_value = '/help' |
| |
| result = await self.cli.get_user_input("测试") |
| |
| # 验证prompt_async被调用且没有额外的completer参数 |
| mock_prompt.assert_called_once() |
| call_args = mock_prompt.call_args |
| |
| # 确保没有传递额外的completer参数(因为已在session初始化时设置) |
| assert 'completer' not in call_args.kwargs |
| assert result == '/help' |
| |
| def test_command_completion_case_sensitivity(self): |
| """测试命令补全的大小写敏感性""" |
| from prompt_toolkit.document import Document |
| |
| # 测试大写输入 |
| document = Document('/HE') |
| completions = list(self.cli.command_completer.get_completions(document, None)) |
| |
| # 应该没有补全,因为命令是小写的 |
| assert len(completions) == 0 |
| |
| def test_command_completion_with_spaces(self): |
| """测试带空格的命令补全""" |
| from prompt_toolkit.document import Document |
| |
| # 测试多词命令补全 |
| document = Document('/sshout dis') |
| completions = list(self.cli.command_completer.get_completions(document, None)) |
| completion_texts = [comp.text for comp in completions] |
| |
| # 应该包含 /sshout disconnect |
| assert '/sshout disconnect' in completion_texts |
| |
| def test_command_completion_exact_match(self): |
| """测试完全匹配的命令补全""" |
| from prompt_toolkit.document import Document |
| |
| # 测试完全输入的命令 |
| document = Document('/help') |
| completions = list(self.cli.command_completer.get_completions(document, None)) |
| |
| # 完全匹配的命令应该还是会返回该命令(这是WordCompleter的行为) |
| completion_texts = [comp.text for comp in completions] |
| assert '/help' in completion_texts |
| |
| def test_command_list_completeness(self): |
| """测试命令列表的完整性""" |
| # 验证所有必需的基本命令都存在 |
| basic_commands = ['/help', '/mode', '/history', '/clear', '/tools', '/status'] |
| exit_commands = ['/quit', '/exit'] |
| sshout_commands = ['/sshout', '/sshout connect', '/sshout disconnect', '/sshout status', '/sshout send'] |
| |
| all_expected = basic_commands + exit_commands + sshout_commands |
| completer_words = self.cli.command_completer.words |
| |
| for command in all_expected: |
| assert command in completer_words, f"Missing command: {command}" |
| |
| def test_completer_ignore_case_setting(self): |
| """测试补全器的忽略大小写设置""" |
| # WordCompleter默认是区分大小写的,这符合我们的设计 |
| assert not hasattr(self.cli.command_completer, 'ignore_case') or \ |
| not getattr(self.cli.command_completer, 'ignore_case', True) |
| |
| |
| class TestEnhancedCLICommandCompletionEdgeCases: |
| """测试命令补全的边界情况""" |
| |
| def setup_method(self): |
| """设置测试""" |
| self.cli = EnhancedCLIInterface() |
| |
| def test_completion_with_cursor_in_middle(self): |
| """测试光标在中间时的补全""" |
| from prompt_toolkit.document import Document |
| |
| # 测试光标在单词中间 |
| document = Document('/help', cursor_position=3) # 光标在'e'后面 |
| completions = list(self.cli.command_completer.get_completions(document, None)) |
| |
| # 应该基于光标前的内容进行补全 |
| completion_texts = [comp.text for comp in completions] |
| assert '/help' in completion_texts |
| |
| def test_completion_with_trailing_space(self): |
| """测试带尾随空格的补全""" |
| from prompt_toolkit.document import Document |
| |
| # 测试命令后有空格 |
| document = Document('/help ') |
| completions = list(self.cli.command_completer.get_completions(document, None)) |
| |
| # 完整命令后的空格不应该有补全 |
| assert len(completions) == 0 |
| |
| def test_completion_performance(self): |
| """测试补全性能""" |
| from prompt_toolkit.document import Document |
| import time |
| |
| # 测试大量补全请求的性能 |
| document = Document('/') |
| |
| start_time = time.time() |
| for _ in range(100): |
| list(self.cli.command_completer.get_completions(document, None)) |
| end_time = time.time() |
| |
| # 100次补全应该在1秒内完成 |
| assert end_time - start_time < 1.0 |
| |
| def test_completion_memory_usage(self): |
| """测试补全的内存使用""" |
| from prompt_toolkit.document import Document |
| import gc |
| |
| # 测试补全不会导致内存泄漏 |
| document = Document('/') |
| |
| # 执行多次补全并强制垃圾回收 |
| for _ in range(50): |
| list(self.cli.command_completer.get_completions(document, None)) |
| gc.collect() |
| |
| # 如果没有内存泄漏,这应该正常完成 |
| assert True |
| |
| |
| if __name__ == '__main__': |
| pytest.main([__file__]) |