auto-coder 0.1.400__py3-none-any.whl → 2.0.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.
Potentially problematic release.
This version of auto-coder might be problematic. Click here for more details.
- auto_coder-2.0.0.dist-info/LICENSE +158 -0
- auto_coder-2.0.0.dist-info/METADATA +558 -0
- auto_coder-2.0.0.dist-info/RECORD +795 -0
- {auto_coder-0.1.400.dist-info → auto_coder-2.0.0.dist-info}/WHEEL +1 -1
- {auto_coder-0.1.400.dist-info → auto_coder-2.0.0.dist-info}/entry_points.txt +3 -3
- autocoder/__init__.py +31 -0
- autocoder/agent/auto_filegroup.py +32 -13
- autocoder/agent/auto_learn_from_commit.py +9 -1
- autocoder/agent/base_agentic/__init__.py +3 -0
- autocoder/agent/base_agentic/agent_hub.py +1 -1
- autocoder/agent/base_agentic/base_agent.py +235 -136
- autocoder/agent/base_agentic/default_tools.py +119 -118
- autocoder/agent/base_agentic/test_base_agent.py +1 -1
- autocoder/agent/base_agentic/tool_registry.py +32 -20
- autocoder/agent/base_agentic/tools/read_file_tool_resolver.py +25 -4
- autocoder/agent/base_agentic/tools/write_to_file_tool_resolver.py +24 -11
- autocoder/agent/base_agentic/types.py +42 -0
- autocoder/agent/entry_command_agent/chat.py +73 -59
- autocoder/auto_coder.py +31 -40
- autocoder/auto_coder_rag.py +11 -1084
- autocoder/auto_coder_runner.py +1029 -2310
- autocoder/auto_coder_terminal.py +26 -0
- autocoder/auto_coder_terminal_v3.py +190 -0
- autocoder/chat/conf_command.py +224 -124
- autocoder/chat/models_command.py +361 -299
- autocoder/chat/rules_command.py +79 -31
- autocoder/chat_auto_coder.py +1021 -372
- autocoder/chat_auto_coder_lang.py +23 -732
- autocoder/commands/auto_command.py +26 -9
- autocoder/commands/auto_web.py +1 -1
- autocoder/commands/tools.py +44 -44
- autocoder/common/__init__.py +150 -128
- autocoder/common/ac_style_command_parser/__init__.py +39 -2
- autocoder/common/ac_style_command_parser/config.py +422 -0
- autocoder/common/ac_style_command_parser/parser.py +292 -78
- autocoder/common/ac_style_command_parser/test_parser.py +241 -16
- autocoder/common/ac_style_command_parser/test_typed_parser.py +342 -0
- autocoder/common/ac_style_command_parser/typed_parser.py +653 -0
- autocoder/common/action_yml_file_manager.py +25 -13
- autocoder/common/agent_events/__init__.py +52 -0
- autocoder/common/agent_events/agent_event_emitter.py +193 -0
- autocoder/common/agent_events/event_factory.py +177 -0
- autocoder/common/agent_events/examples.py +307 -0
- autocoder/common/agent_events/types.py +113 -0
- autocoder/common/agent_events/utils.py +68 -0
- autocoder/common/agent_hooks/__init__.py +44 -0
- autocoder/common/agent_hooks/examples.py +582 -0
- autocoder/common/agent_hooks/hook_executor.py +217 -0
- autocoder/common/agent_hooks/hook_manager.py +288 -0
- autocoder/common/agent_hooks/types.py +133 -0
- autocoder/common/agent_hooks/utils.py +99 -0
- autocoder/common/agent_query_queue/queue_executor.py +324 -0
- autocoder/common/agent_query_queue/queue_manager.py +325 -0
- autocoder/common/agents/__init__.py +11 -0
- autocoder/common/agents/agent_manager.py +323 -0
- autocoder/common/agents/agent_parser.py +189 -0
- autocoder/common/agents/example_usage.py +344 -0
- autocoder/common/agents/integration_example.py +330 -0
- autocoder/common/agents/test_agent_parser.py +545 -0
- autocoder/common/async_utils.py +101 -0
- autocoder/common/auto_coder_lang.py +23 -972
- autocoder/common/autocoderargs_parser/__init__.py +14 -0
- autocoder/common/autocoderargs_parser/parser.py +184 -0
- autocoder/common/autocoderargs_parser/tests/__init__.py +1 -0
- autocoder/common/autocoderargs_parser/tests/test_args_parser.py +235 -0
- autocoder/common/autocoderargs_parser/tests/test_token_parser.py +195 -0
- autocoder/common/autocoderargs_parser/token_parser.py +290 -0
- autocoder/common/buildin_tokenizer.py +2 -4
- autocoder/common/code_auto_generate.py +149 -74
- autocoder/common/code_auto_generate_diff.py +163 -70
- autocoder/common/code_auto_generate_editblock.py +179 -89
- autocoder/common/code_auto_generate_strict_diff.py +167 -72
- autocoder/common/code_auto_merge_editblock.py +13 -6
- autocoder/common/code_modification_ranker.py +1 -1
- autocoder/common/command_completer.py +3 -3
- autocoder/common/command_file_manager/manager.py +183 -47
- autocoder/common/command_file_manager/test_command_file_manager.py +507 -0
- autocoder/common/command_templates.py +1 -1
- autocoder/common/conf_utils.py +2 -4
- autocoder/common/conversations/config.py +11 -3
- autocoder/common/conversations/get_conversation_manager.py +100 -2
- autocoder/common/conversations/llm_stats_models.py +264 -0
- autocoder/common/conversations/manager.py +112 -28
- autocoder/common/conversations/models.py +16 -2
- autocoder/common/conversations/storage/index_manager.py +134 -10
- autocoder/common/core_config/__init__.py +63 -0
- autocoder/common/core_config/agentic_mode_manager.py +109 -0
- autocoder/common/core_config/base_manager.py +123 -0
- autocoder/common/core_config/compatibility.py +151 -0
- autocoder/common/core_config/config_manager.py +156 -0
- autocoder/common/core_config/conversation_manager.py +31 -0
- autocoder/common/core_config/exclude_manager.py +72 -0
- autocoder/common/core_config/file_manager.py +177 -0
- autocoder/common/core_config/human_as_model_manager.py +129 -0
- autocoder/common/core_config/lib_manager.py +54 -0
- autocoder/common/core_config/main_manager.py +81 -0
- autocoder/common/core_config/mode_manager.py +126 -0
- autocoder/common/core_config/models.py +70 -0
- autocoder/common/core_config/test_memory_manager.py +1056 -0
- autocoder/common/env_manager.py +282 -0
- autocoder/common/env_manager_usage_example.py +211 -0
- autocoder/common/file_checkpoint/conversation_checkpoint.py +19 -19
- autocoder/common/file_checkpoint/manager.py +264 -48
- autocoder/common/file_checkpoint/test_backup.py +1 -18
- autocoder/common/file_checkpoint/test_manager.py +270 -1
- autocoder/common/file_checkpoint/test_store.py +1 -17
- autocoder/common/file_handler/__init__.py +23 -0
- autocoder/common/file_handler/active_context_handler.py +159 -0
- autocoder/common/file_handler/add_files_handler.py +409 -0
- autocoder/common/file_handler/chat_handler.py +180 -0
- autocoder/common/file_handler/coding_handler.py +401 -0
- autocoder/common/file_handler/commit_handler.py +200 -0
- autocoder/common/file_handler/lib_handler.py +156 -0
- autocoder/common/file_handler/list_files_handler.py +111 -0
- autocoder/common/file_handler/mcp_handler.py +268 -0
- autocoder/common/file_handler/models_handler.py +493 -0
- autocoder/common/file_handler/remove_files_handler.py +172 -0
- autocoder/common/file_monitor/test_file_monitor.py +307 -0
- autocoder/common/git_utils.py +51 -10
- autocoder/common/global_cancel.py +15 -6
- autocoder/common/ignorefiles/test_ignore_file_utils.py +1 -1
- autocoder/common/international/__init__.py +31 -0
- autocoder/common/international/demo_international.py +92 -0
- autocoder/common/international/message_manager.py +157 -0
- autocoder/common/international/messages/__init__.py +56 -0
- autocoder/common/international/messages/async_command_messages.py +507 -0
- autocoder/common/international/messages/auto_coder_messages.py +2208 -0
- autocoder/common/international/messages/chat_auto_coder_messages.py +1547 -0
- autocoder/common/international/messages/command_help_messages.py +986 -0
- autocoder/common/international/messages/conversation_command_messages.py +191 -0
- autocoder/common/international/messages/git_helper_plugin_messages.py +159 -0
- autocoder/common/international/messages/queue_command_messages.py +751 -0
- autocoder/common/international/messages/rules_command_messages.py +77 -0
- autocoder/common/international/messages/sdk_messages.py +1707 -0
- autocoder/common/international/messages/token_helper_plugin_messages.py +361 -0
- autocoder/common/international/messages/tool_display_messages.py +1212 -0
- autocoder/common/international/messages/workflow_exception_messages.py +473 -0
- autocoder/common/international/test_international.py +612 -0
- autocoder/common/linter_core/__init__.py +28 -0
- autocoder/common/linter_core/base_linter.py +61 -0
- autocoder/common/linter_core/config_loader.py +271 -0
- autocoder/common/linter_core/formatters/__init__.py +0 -0
- autocoder/common/linter_core/formatters/base_formatter.py +38 -0
- autocoder/common/linter_core/formatters/raw_formatter.py +17 -0
- autocoder/common/linter_core/linter.py +166 -0
- autocoder/common/linter_core/linter_factory.py +216 -0
- autocoder/common/linter_core/linter_manager.py +333 -0
- autocoder/common/linter_core/linters/__init__.py +9 -0
- autocoder/common/linter_core/linters/java_linter.py +342 -0
- autocoder/common/linter_core/linters/python_linter.py +115 -0
- autocoder/common/linter_core/linters/typescript_linter.py +119 -0
- autocoder/common/linter_core/models/__init__.py +7 -0
- autocoder/common/linter_core/models/lint_result.py +91 -0
- autocoder/common/linter_core/models.py +33 -0
- autocoder/common/linter_core/tests/__init__.py +3 -0
- autocoder/common/linter_core/tests/test_config_loader.py +323 -0
- autocoder/common/linter_core/tests/test_config_loading.py +308 -0
- autocoder/common/linter_core/tests/test_factory_manager.py +234 -0
- autocoder/common/linter_core/tests/test_formatters.py +147 -0
- autocoder/common/linter_core/tests/test_integration.py +317 -0
- autocoder/common/linter_core/tests/test_java_linter.py +496 -0
- autocoder/common/linter_core/tests/test_linters.py +265 -0
- autocoder/common/linter_core/tests/test_models.py +81 -0
- autocoder/common/linter_core/tests/verify_config_loading.py +296 -0
- autocoder/common/linter_core/tests/verify_fixes.py +183 -0
- autocoder/common/llm_friendly_package/__init__.py +31 -0
- autocoder/common/llm_friendly_package/base_manager.py +102 -0
- autocoder/common/llm_friendly_package/docs_manager.py +121 -0
- autocoder/common/llm_friendly_package/library_manager.py +171 -0
- autocoder/common/{llm_friendly_package.py → llm_friendly_package/main_manager.py} +204 -231
- autocoder/common/llm_friendly_package/models.py +40 -0
- autocoder/common/llm_friendly_package/test_llm_friendly_package.py +536 -0
- autocoder/common/llms/__init__.py +15 -0
- autocoder/common/llms/demo_error_handling.py +85 -0
- autocoder/common/llms/factory.py +142 -0
- autocoder/common/llms/manager.py +264 -0
- autocoder/common/llms/pricing.py +121 -0
- autocoder/common/llms/registry.py +288 -0
- autocoder/common/llms/schema.py +77 -0
- autocoder/common/llms/simple_demo.py +45 -0
- autocoder/common/llms/test_quick_model.py +116 -0
- autocoder/common/llms/test_remove_functionality.py +182 -0
- autocoder/common/llms/tests/__init__.py +1 -0
- autocoder/common/llms/tests/test_manager.py +330 -0
- autocoder/common/llms/tests/test_registry.py +364 -0
- autocoder/common/mcp_tools/__init__.py +62 -0
- autocoder/common/{mcp_tools.py → mcp_tools/executor.py} +49 -40
- autocoder/common/{mcp_hub.py → mcp_tools/hub.py} +42 -68
- autocoder/common/{mcp_server_install.py → mcp_tools/installer.py} +16 -28
- autocoder/common/{mcp_server.py → mcp_tools/server.py} +176 -48
- autocoder/common/mcp_tools/test_keyboard_interrupt.py +93 -0
- autocoder/common/mcp_tools/test_mcp_tools.py +391 -0
- autocoder/common/{mcp_server_types.py → mcp_tools/types.py} +121 -48
- autocoder/common/mcp_tools/verify_functionality.py +202 -0
- autocoder/common/model_speed_tester.py +32 -26
- autocoder/common/priority_directory_finder/__init__.py +142 -0
- autocoder/common/priority_directory_finder/examples.py +230 -0
- autocoder/common/priority_directory_finder/finder.py +283 -0
- autocoder/common/priority_directory_finder/models.py +236 -0
- autocoder/common/priority_directory_finder/test_priority_directory_finder.py +431 -0
- autocoder/common/project_scanner/__init__.py +18 -0
- autocoder/common/project_scanner/compat.py +77 -0
- autocoder/common/project_scanner/scanner.py +436 -0
- autocoder/common/project_tracker/__init__.py +27 -0
- autocoder/common/project_tracker/api.py +228 -0
- autocoder/common/project_tracker/demo.py +272 -0
- autocoder/common/project_tracker/tracker.py +487 -0
- autocoder/common/project_tracker/types.py +53 -0
- autocoder/common/pruner/__init__.py +67 -0
- autocoder/common/pruner/agentic_conversation_pruner.py +746 -0
- autocoder/common/{context_pruner.py → pruner/context_pruner.py} +137 -40
- autocoder/common/pruner/conversation_message_ids_api.py +386 -0
- autocoder/common/pruner/conversation_message_ids_manager.py +347 -0
- autocoder/common/pruner/conversation_message_ids_pruner.py +473 -0
- autocoder/common/pruner/conversation_normalizer.py +347 -0
- autocoder/common/{conversation_pruner.py → pruner/conversation_pruner.py} +26 -6
- autocoder/common/pruner/test_agentic_conversation_pruner.py +784 -0
- autocoder/common/pruner/test_context_pruner.py +546 -0
- autocoder/common/pruner/test_conversation_normalizer.py +502 -0
- autocoder/common/pruner/test_tool_content_detector.py +324 -0
- autocoder/common/pruner/tool_content_detector.py +227 -0
- autocoder/common/pruner/tools/__init__.py +18 -0
- autocoder/common/pruner/tools/query_message_ids.py +264 -0
- autocoder/common/pruner/tools/test_agentic_pruning_logic.py +432 -0
- autocoder/common/pruner/tools/test_message_ids_pruning_only.py +192 -0
- autocoder/common/pull_requests/__init__.py +9 -1
- autocoder/common/pull_requests/utils.py +122 -1
- autocoder/common/rag_manager/rag_manager.py +36 -40
- autocoder/common/rulefiles/__init__.py +53 -1
- autocoder/common/rulefiles/api.py +250 -0
- autocoder/common/rulefiles/core/__init__.py +14 -0
- autocoder/common/rulefiles/core/manager.py +241 -0
- autocoder/common/rulefiles/core/selector.py +805 -0
- autocoder/common/rulefiles/models/__init__.py +20 -0
- autocoder/common/rulefiles/models/index.py +16 -0
- autocoder/common/rulefiles/models/init_rule.py +18 -0
- autocoder/common/rulefiles/models/rule_file.py +18 -0
- autocoder/common/rulefiles/models/rule_relevance.py +14 -0
- autocoder/common/rulefiles/models/summary.py +16 -0
- autocoder/common/rulefiles/test_rulefiles.py +776 -0
- autocoder/common/rulefiles/utils/__init__.py +34 -0
- autocoder/common/rulefiles/utils/monitor.py +86 -0
- autocoder/common/rulefiles/utils/parser.py +230 -0
- autocoder/common/save_formatted_log.py +67 -10
- autocoder/common/search_replace.py +8 -1
- autocoder/common/search_replace_patch/__init__.py +24 -0
- autocoder/common/search_replace_patch/base.py +115 -0
- autocoder/common/search_replace_patch/manager.py +248 -0
- autocoder/common/search_replace_patch/patch_replacer.py +304 -0
- autocoder/common/search_replace_patch/similarity_replacer.py +306 -0
- autocoder/common/search_replace_patch/string_replacer.py +181 -0
- autocoder/common/search_replace_patch/tests/__init__.py +3 -0
- autocoder/common/search_replace_patch/tests/run_tests.py +126 -0
- autocoder/common/search_replace_patch/tests/test_base.py +188 -0
- autocoder/common/search_replace_patch/tests/test_empty_line_insert.py +233 -0
- autocoder/common/search_replace_patch/tests/test_integration.py +389 -0
- autocoder/common/search_replace_patch/tests/test_manager.py +351 -0
- autocoder/common/search_replace_patch/tests/test_patch_replacer.py +316 -0
- autocoder/common/search_replace_patch/tests/test_regex_replacer.py +306 -0
- autocoder/common/search_replace_patch/tests/test_similarity_replacer.py +384 -0
- autocoder/common/shell_commands/__init__.py +197 -0
- autocoder/common/shell_commands/background_process_notifier.py +346 -0
- autocoder/common/shell_commands/command_executor.py +1127 -0
- autocoder/common/shell_commands/error_recovery.py +541 -0
- autocoder/common/shell_commands/exceptions.py +120 -0
- autocoder/common/shell_commands/interactive_executor.py +476 -0
- autocoder/common/shell_commands/interactive_pexpect_process.py +623 -0
- autocoder/common/shell_commands/interactive_process.py +744 -0
- autocoder/common/shell_commands/interactive_session_manager.py +1014 -0
- autocoder/common/shell_commands/monitoring.py +529 -0
- autocoder/common/shell_commands/process_cleanup.py +386 -0
- autocoder/common/shell_commands/process_manager.py +606 -0
- autocoder/common/shell_commands/test_interactive_pexpect_process.py +281 -0
- autocoder/common/shell_commands/tests/__init__.py +6 -0
- autocoder/common/shell_commands/tests/conftest.py +118 -0
- autocoder/common/shell_commands/tests/test_background_process_notifier.py +703 -0
- autocoder/common/shell_commands/tests/test_command_executor.py +448 -0
- autocoder/common/shell_commands/tests/test_error_recovery.py +305 -0
- autocoder/common/shell_commands/tests/test_exceptions.py +299 -0
- autocoder/common/shell_commands/tests/test_execute_batch.py +588 -0
- autocoder/common/shell_commands/tests/test_indented_batch_commands.py +244 -0
- autocoder/common/shell_commands/tests/test_integration.py +664 -0
- autocoder/common/shell_commands/tests/test_monitoring.py +546 -0
- autocoder/common/shell_commands/tests/test_performance.py +632 -0
- autocoder/common/shell_commands/tests/test_process_cleanup.py +397 -0
- autocoder/common/shell_commands/tests/test_process_manager.py +606 -0
- autocoder/common/shell_commands/tests/test_timeout_config.py +343 -0
- autocoder/common/shell_commands/tests/test_timeout_manager.py +520 -0
- autocoder/common/shell_commands/timeout_config.py +315 -0
- autocoder/common/shell_commands/timeout_manager.py +352 -0
- autocoder/common/terminal_paste/__init__.py +14 -0
- autocoder/common/terminal_paste/demo.py +145 -0
- autocoder/common/terminal_paste/demo_paste_functionality.py +95 -0
- autocoder/common/terminal_paste/paste_handler.py +200 -0
- autocoder/common/terminal_paste/paste_manager.py +118 -0
- autocoder/common/terminal_paste/tests/__init__.py +1 -0
- autocoder/common/terminal_paste/tests/test_paste_handler.py +182 -0
- autocoder/common/terminal_paste/tests/test_paste_manager.py +126 -0
- autocoder/common/terminal_paste/utils.py +163 -0
- autocoder/common/test_autocoder_args.py +232 -0
- autocoder/common/test_env_manager.py +173 -0
- autocoder/common/test_env_manager_integration.py +159 -0
- autocoder/common/text_similarity/__init__.py +9 -0
- autocoder/common/text_similarity/demo.py +216 -0
- autocoder/common/text_similarity/examples.py +266 -0
- autocoder/common/text_similarity/test_text_similarity.py +306 -0
- autocoder/common/text_similarity/text_similarity.py +194 -0
- autocoder/common/text_similarity/utils.py +125 -0
- autocoder/common/todos/__init__.py +61 -0
- autocoder/common/todos/cache/__init__.py +16 -0
- autocoder/common/todos/cache/base_cache.py +89 -0
- autocoder/common/todos/cache/cache_manager.py +228 -0
- autocoder/common/todos/cache/memory_cache.py +225 -0
- autocoder/common/todos/config.py +155 -0
- autocoder/common/todos/exceptions.py +35 -0
- autocoder/common/todos/get_todo_manager.py +161 -0
- autocoder/common/todos/manager.py +537 -0
- autocoder/common/todos/models.py +239 -0
- autocoder/common/todos/storage/__init__.py +14 -0
- autocoder/common/todos/storage/base_storage.py +76 -0
- autocoder/common/todos/storage/file_storage.py +278 -0
- autocoder/common/tokens/__init__.py +15 -0
- autocoder/common/tokens/counter.py +44 -2
- autocoder/common/tools_manager/__init__.py +17 -0
- autocoder/common/tools_manager/examples.py +162 -0
- autocoder/common/tools_manager/manager.py +385 -0
- autocoder/common/tools_manager/models.py +39 -0
- autocoder/common/tools_manager/test_tools_manager.py +303 -0
- autocoder/common/tools_manager/utils.py +191 -0
- autocoder/common/v2/agent/agentic_callbacks.py +270 -0
- autocoder/common/v2/agent/agentic_edit.py +2729 -2052
- autocoder/common/v2/agent/agentic_edit_change_manager.py +474 -0
- autocoder/common/v2/agent/agentic_edit_tools/__init__.py +43 -2
- autocoder/common/v2/agent/agentic_edit_tools/ac_mod_list_tool_resolver.py +279 -0
- autocoder/common/v2/agent/agentic_edit_tools/ac_mod_read_tool_resolver.py +40 -0
- autocoder/common/v2/agent/agentic_edit_tools/ac_mod_write_tool_resolver.py +52 -0
- autocoder/common/v2/agent/agentic_edit_tools/ask_followup_question_tool_resolver.py +8 -0
- autocoder/common/v2/agent/agentic_edit_tools/background_task_tool_resolver.py +1167 -0
- autocoder/common/v2/agent/agentic_edit_tools/base_tool_resolver.py +2 -2
- autocoder/common/v2/agent/agentic_edit_tools/conversation_message_ids_read_tool_resolver.py +214 -0
- autocoder/common/v2/agent/agentic_edit_tools/conversation_message_ids_write_tool_resolver.py +299 -0
- autocoder/common/v2/agent/agentic_edit_tools/count_tokens_tool_resolver.py +290 -0
- autocoder/common/v2/agent/agentic_edit_tools/execute_command_tool_resolver.py +565 -30
- autocoder/common/v2/agent/agentic_edit_tools/execute_workflow_tool_resolver.py +485 -0
- autocoder/common/v2/agent/agentic_edit_tools/extract_to_text_tool_resolver.py +225 -0
- autocoder/common/v2/agent/agentic_edit_tools/lint_report.py +79 -0
- autocoder/common/v2/agent/agentic_edit_tools/linter_config_models.py +343 -0
- autocoder/common/v2/agent/agentic_edit_tools/linter_enabled_tool_resolver.py +189 -0
- autocoder/common/v2/agent/agentic_edit_tools/list_files_tool_resolver.py +169 -101
- autocoder/common/v2/agent/agentic_edit_tools/load_extra_document_tool_resolver.py +349 -0
- autocoder/common/v2/agent/agentic_edit_tools/read_file_tool_resolver.py +244 -51
- autocoder/common/v2/agent/agentic_edit_tools/replace_in_file_tool_resolver.py +667 -147
- autocoder/common/v2/agent/agentic_edit_tools/run_named_subagents_tool_resolver.py +691 -0
- autocoder/common/v2/agent/agentic_edit_tools/search_files_tool_resolver.py +409 -140
- autocoder/common/v2/agent/agentic_edit_tools/session_interactive_tool_resolver.py +115 -0
- autocoder/common/v2/agent/agentic_edit_tools/session_start_tool_resolver.py +190 -0
- autocoder/common/v2/agent/agentic_edit_tools/session_stop_tool_resolver.py +76 -0
- autocoder/common/v2/agent/agentic_edit_tools/test_write_to_file_tool_resolver.py +209 -194
- autocoder/common/v2/agent/agentic_edit_tools/todo_read_tool_resolver.py +135 -0
- autocoder/common/v2/agent/agentic_edit_tools/todo_write_tool_resolver.py +328 -0
- autocoder/common/v2/agent/agentic_edit_tools/use_mcp_tool_resolver.py +2 -2
- autocoder/common/v2/agent/agentic_edit_tools/web_crawl_tool_resolver.py +557 -0
- autocoder/common/v2/agent/agentic_edit_tools/web_search_tool_resolver.py +600 -0
- autocoder/common/v2/agent/agentic_edit_tools/write_to_file_tool_resolver.py +56 -121
- autocoder/common/v2/agent/agentic_edit_types.py +386 -10
- autocoder/common/v2/agent/runner/__init__.py +31 -0
- autocoder/common/v2/agent/runner/base_runner.py +92 -0
- autocoder/common/v2/agent/runner/file_based_event_runner.py +217 -0
- autocoder/common/v2/agent/runner/sdk_runner.py +182 -0
- autocoder/common/v2/agent/runner/terminal_runner.py +396 -0
- autocoder/common/v2/agent/runner/tool_display.py +589 -0
- autocoder/common/v2/agent/test_agentic_callbacks.py +265 -0
- autocoder/common/v2/agent/test_agentic_edit.py +194 -0
- autocoder/common/v2/agent/tool_caller/__init__.py +24 -0
- autocoder/common/v2/agent/tool_caller/default_tool_resolver_map.py +135 -0
- autocoder/common/v2/agent/tool_caller/integration_test.py +172 -0
- autocoder/common/v2/agent/tool_caller/plugins/__init__.py +14 -0
- autocoder/common/v2/agent/tool_caller/plugins/base_plugin.py +126 -0
- autocoder/common/v2/agent/tool_caller/plugins/examples/__init__.py +13 -0
- autocoder/common/v2/agent/tool_caller/plugins/examples/logging_plugin.py +164 -0
- autocoder/common/v2/agent/tool_caller/plugins/examples/security_filter_plugin.py +198 -0
- autocoder/common/v2/agent/tool_caller/plugins/plugin_interface.py +141 -0
- autocoder/common/v2/agent/tool_caller/test_tool_caller.py +278 -0
- autocoder/common/v2/agent/tool_caller/tool_call_plugin_manager.py +331 -0
- autocoder/common/v2/agent/tool_caller/tool_caller.py +337 -0
- autocoder/common/v2/agent/tool_caller/usage_example.py +193 -0
- autocoder/common/v2/code_agentic_editblock_manager.py +4 -4
- autocoder/common/v2/code_auto_generate.py +136 -78
- autocoder/common/v2/code_auto_generate_diff.py +135 -79
- autocoder/common/v2/code_auto_generate_editblock.py +174 -99
- autocoder/common/v2/code_auto_generate_strict_diff.py +151 -71
- autocoder/common/v2/code_auto_merge.py +1 -1
- autocoder/common/v2/code_auto_merge_editblock.py +13 -1
- autocoder/common/v2/code_diff_manager.py +3 -3
- autocoder/common/v2/code_editblock_manager.py +4 -14
- autocoder/common/v2/code_manager.py +1 -1
- autocoder/common/v2/code_strict_diff_manager.py +2 -2
- autocoder/common/wrap_llm_hint/__init__.py +10 -0
- autocoder/common/wrap_llm_hint/test_wrap_llm_hint.py +1067 -0
- autocoder/common/wrap_llm_hint/utils.py +432 -0
- autocoder/common/wrap_llm_hint/wrap_llm_hint.py +323 -0
- autocoder/completer/__init__.py +8 -0
- autocoder/completer/command_completer_v2.py +1051 -0
- autocoder/default_project/__init__.py +501 -0
- autocoder/dispacher/__init__.py +4 -12
- autocoder/dispacher/actions/action.py +165 -7
- autocoder/dispacher/actions/plugins/action_regex_project.py +2 -2
- autocoder/index/entry.py +117 -125
- autocoder/{agent → index/filter}/agentic_filter.py +323 -334
- autocoder/index/filter/normal_filter.py +5 -11
- autocoder/index/filter/quick_filter.py +1 -1
- autocoder/index/index.py +36 -9
- autocoder/index/tests/__init__.py +1 -0
- autocoder/index/tests/run_tests.py +195 -0
- autocoder/index/tests/test_entry.py +303 -0
- autocoder/index/tests/test_index_manager.py +314 -0
- autocoder/index/tests/test_module_integration.py +300 -0
- autocoder/index/tests/test_symbols_utils.py +183 -0
- autocoder/inner/__init__.py +4 -0
- autocoder/inner/agentic.py +932 -0
- autocoder/inner/async_command_handler.py +992 -0
- autocoder/inner/conversation_command_handlers.py +623 -0
- autocoder/inner/merge_command_handler.py +213 -0
- autocoder/inner/queue_command_handler.py +684 -0
- autocoder/models.py +95 -266
- autocoder/plugins/git_helper_plugin.py +31 -29
- autocoder/plugins/token_helper_plugin.py +156 -37
- autocoder/pyproject/__init__.py +32 -29
- autocoder/rag/agentic_rag.py +215 -75
- autocoder/rag/cache/simple_cache.py +1 -2
- autocoder/rag/loaders/image_loader.py +1 -1
- autocoder/rag/long_context_rag.py +42 -26
- autocoder/rag/qa_conversation_strategy.py +1 -1
- autocoder/rag/terminal/__init__.py +17 -0
- autocoder/rag/terminal/args.py +581 -0
- autocoder/rag/terminal/bootstrap.py +61 -0
- autocoder/rag/terminal/command_handlers.py +653 -0
- autocoder/rag/terminal/formatters/__init__.py +20 -0
- autocoder/rag/terminal/formatters/base.py +70 -0
- autocoder/rag/terminal/formatters/json_format.py +66 -0
- autocoder/rag/terminal/formatters/stream_json.py +95 -0
- autocoder/rag/terminal/formatters/text.py +28 -0
- autocoder/rag/terminal/init.py +120 -0
- autocoder/rag/terminal/utils.py +106 -0
- autocoder/rag/test_agentic_rag.py +389 -0
- autocoder/rag/test_doc_filter.py +3 -3
- autocoder/rag/test_long_context_rag.py +1 -1
- autocoder/rag/test_token_limiter.py +517 -10
- autocoder/rag/token_counter.py +3 -0
- autocoder/rag/token_limiter.py +19 -15
- autocoder/rag/tools/__init__.py +26 -2
- autocoder/rag/tools/bochaai_example.py +343 -0
- autocoder/rag/tools/bochaai_sdk.py +541 -0
- autocoder/rag/tools/metaso_example.py +268 -0
- autocoder/rag/tools/metaso_sdk.py +417 -0
- autocoder/rag/tools/recall_tool.py +28 -7
- autocoder/rag/tools/run_integration_tests.py +204 -0
- autocoder/rag/tools/test_all_providers.py +318 -0
- autocoder/rag/tools/test_bochaai_integration.py +482 -0
- autocoder/rag/tools/test_final_integration.py +215 -0
- autocoder/rag/tools/test_metaso_integration.py +424 -0
- autocoder/rag/tools/test_metaso_real.py +171 -0
- autocoder/rag/tools/test_web_crawl_tool.py +639 -0
- autocoder/rag/tools/test_web_search_tool.py +509 -0
- autocoder/rag/tools/todo_read_tool.py +202 -0
- autocoder/rag/tools/todo_write_tool.py +412 -0
- autocoder/rag/tools/web_crawl_tool.py +634 -0
- autocoder/rag/tools/web_search_tool.py +558 -0
- autocoder/rag/tools/web_tools_example.py +119 -0
- autocoder/rag/types.py +16 -0
- autocoder/rag/variable_holder.py +4 -2
- autocoder/rags.py +86 -79
- autocoder/regexproject/__init__.py +23 -21
- autocoder/run_context.py +9 -0
- autocoder/sdk/__init__.py +50 -161
- autocoder/sdk/api.py +370 -0
- autocoder/sdk/async_runner/__init__.py +26 -0
- autocoder/sdk/async_runner/async_executor.py +650 -0
- autocoder/sdk/async_runner/async_handler.py +356 -0
- autocoder/sdk/async_runner/markdown_processor.py +595 -0
- autocoder/sdk/async_runner/task_metadata.py +284 -0
- autocoder/sdk/async_runner/worktree_manager.py +438 -0
- autocoder/sdk/cli/__init__.py +2 -5
- autocoder/sdk/cli/formatters.py +28 -204
- autocoder/sdk/cli/handlers.py +77 -44
- autocoder/sdk/cli/main.py +158 -170
- autocoder/sdk/cli/options.py +95 -22
- autocoder/sdk/constants.py +139 -51
- autocoder/sdk/core/auto_coder_core.py +484 -267
- autocoder/sdk/core/bridge.py +298 -118
- autocoder/sdk/exceptions.py +18 -12
- autocoder/sdk/formatters/__init__.py +19 -0
- autocoder/sdk/formatters/input.py +64 -0
- autocoder/sdk/formatters/output.py +247 -0
- autocoder/sdk/formatters/stream.py +54 -0
- autocoder/sdk/models/__init__.py +6 -5
- autocoder/sdk/models/options.py +55 -18
- autocoder/sdk/utils/formatters.py +27 -195
- autocoder/suffixproject/__init__.py +28 -25
- autocoder/terminal/__init__.py +14 -0
- autocoder/terminal/app.py +454 -0
- autocoder/terminal/args.py +32 -0
- autocoder/terminal/bootstrap.py +178 -0
- autocoder/terminal/command_processor.py +521 -0
- autocoder/terminal/command_registry.py +57 -0
- autocoder/terminal/help.py +97 -0
- autocoder/terminal/tasks/__init__.py +5 -0
- autocoder/terminal/tasks/background.py +77 -0
- autocoder/terminal/tasks/task_event.py +70 -0
- autocoder/terminal/ui/__init__.py +13 -0
- autocoder/terminal/ui/completer.py +268 -0
- autocoder/terminal/ui/keybindings.py +75 -0
- autocoder/terminal/ui/session.py +41 -0
- autocoder/terminal/ui/toolbar.py +64 -0
- autocoder/terminal/utils/__init__.py +13 -0
- autocoder/terminal/utils/errors.py +18 -0
- autocoder/terminal/utils/paths.py +19 -0
- autocoder/terminal/utils/shell.py +43 -0
- autocoder/terminal_v3/__init__.py +10 -0
- autocoder/terminal_v3/app.py +201 -0
- autocoder/terminal_v3/handlers/__init__.py +5 -0
- autocoder/terminal_v3/handlers/command_handler.py +131 -0
- autocoder/terminal_v3/models/__init__.py +6 -0
- autocoder/terminal_v3/models/conversation_buffer.py +214 -0
- autocoder/terminal_v3/models/message.py +50 -0
- autocoder/terminal_v3/models/tool_display.py +247 -0
- autocoder/terminal_v3/ui/__init__.py +7 -0
- autocoder/terminal_v3/ui/keybindings.py +56 -0
- autocoder/terminal_v3/ui/layout.py +141 -0
- autocoder/terminal_v3/ui/styles.py +43 -0
- autocoder/tsproject/__init__.py +23 -23
- autocoder/utils/auto_coder_utils/chat_stream_out.py +1 -1
- autocoder/utils/llms.py +88 -80
- autocoder/utils/math_utils.py +101 -0
- autocoder/utils/model_provider_selector.py +16 -4
- autocoder/utils/operate_config_api.py +33 -5
- autocoder/utils/thread_utils.py +2 -2
- autocoder/version.py +4 -2
- autocoder/workflow_agents/__init__.py +84 -0
- autocoder/workflow_agents/agent.py +143 -0
- autocoder/workflow_agents/exceptions.py +573 -0
- autocoder/workflow_agents/executor.py +489 -0
- autocoder/workflow_agents/loader.py +737 -0
- autocoder/workflow_agents/runner.py +267 -0
- autocoder/workflow_agents/types.py +172 -0
- autocoder/workflow_agents/utils.py +434 -0
- autocoder/workflow_agents/workflow_manager.py +211 -0
- auto_coder-0.1.400.dist-info/METADATA +0 -396
- auto_coder-0.1.400.dist-info/RECORD +0 -425
- auto_coder-0.1.400.dist-info/licenses/LICENSE +0 -201
- autocoder/auto_coder_server.py +0 -672
- autocoder/benchmark.py +0 -138
- autocoder/common/ac_style_command_parser/example.py +0 -7
- autocoder/common/cleaner.py +0 -31
- autocoder/common/command_completer_v2.py +0 -615
- autocoder/common/directory_cache/__init__.py +0 -1
- autocoder/common/directory_cache/cache.py +0 -192
- autocoder/common/directory_cache/test_cache.py +0 -190
- autocoder/common/file_checkpoint/examples.py +0 -217
- autocoder/common/llm_friendly_package_example.py +0 -138
- autocoder/common/llm_friendly_package_test.py +0 -63
- autocoder/common/pull_requests/test_module.py +0 -1
- autocoder/common/rulefiles/autocoderrules_utils.py +0 -484
- autocoder/common/text.py +0 -30
- autocoder/common/v2/agent/agentic_edit_tools/list_package_info_tool_resolver.py +0 -42
- autocoder/common/v2/agent/agentic_edit_tools/test_execute_command_tool_resolver.py +0 -70
- autocoder/common/v2/agent/agentic_edit_tools/test_search_files_tool_resolver.py +0 -163
- autocoder/common/v2/agent/agentic_tool_display.py +0 -183
- autocoder/plugins/dynamic_completion_example.py +0 -148
- autocoder/plugins/sample_plugin.py +0 -160
- autocoder/sdk/cli/__main__.py +0 -26
- autocoder/sdk/cli/completion_wrapper.py +0 -38
- autocoder/sdk/cli/install_completion.py +0 -301
- autocoder/sdk/models/messages.py +0 -209
- autocoder/sdk/session/__init__.py +0 -32
- autocoder/sdk/session/session.py +0 -106
- autocoder/sdk/session/session_manager.py +0 -56
- {auto_coder-0.1.400.dist-info → auto_coder-2.0.0.dist-info}/top_level.txt +0 -0
- /autocoder/{sdk/example.py → common/agent_query_queue/__init__.py} +0 -0
|
@@ -0,0 +1,776 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
AutoCoder 规则文件管理模块测试
|
|
4
|
+
|
|
5
|
+
测试重点:
|
|
6
|
+
1. 基本的规则文件加载和解析功能
|
|
7
|
+
2. 缓存机制,特别是基于文件 MD5 的缓存失效检测
|
|
8
|
+
3. generate_always_apply_summary 和 generate_conditional_rules_index 的缓存失效
|
|
9
|
+
4. 使用临时目录进行隔离测试
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import pytest
|
|
13
|
+
import tempfile
|
|
14
|
+
import os
|
|
15
|
+
import json
|
|
16
|
+
import time
|
|
17
|
+
import hashlib
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
from unittest.mock import Mock, patch
|
|
20
|
+
from typing import Dict, Any
|
|
21
|
+
|
|
22
|
+
# 导入要测试的模块
|
|
23
|
+
from autocoder.common.rulefiles import (
|
|
24
|
+
get_rules, get_parsed_rules, parse_rule_file, reset_rules_manager,
|
|
25
|
+
auto_select_rules, get_required_and_index_rules,
|
|
26
|
+
generate_always_apply_summary, generate_conditional_rules_index,
|
|
27
|
+
invalidate_rules_cache, get_rules_cache_stats, check_rules_cache_validity,
|
|
28
|
+
AutocoderRulesManager, RuleSelector
|
|
29
|
+
)
|
|
30
|
+
from autocoder.common.rulefiles.models import (
|
|
31
|
+
RuleFile, AlwaysApplyRuleSummary, ConditionalRulesIndex, RuleRelevance
|
|
32
|
+
)
|
|
33
|
+
from autocoder.common.rulefiles.utils.cache import RuleCacheManager
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class MockLLM:
|
|
37
|
+
"""模拟 LLM,用于测试"""
|
|
38
|
+
|
|
39
|
+
def __init__(self, responses: Dict[str, Any] = None):
|
|
40
|
+
self.responses = responses or {}
|
|
41
|
+
self.call_count = 0
|
|
42
|
+
|
|
43
|
+
def chat_oai(self, messages, **kwargs):
|
|
44
|
+
"""模拟 chat_oai 方法"""
|
|
45
|
+
self.call_count += 1
|
|
46
|
+
# 根据消息内容返回不同的响应
|
|
47
|
+
message_content = str(messages)
|
|
48
|
+
|
|
49
|
+
if "判断规则是否适用" in message_content:
|
|
50
|
+
return [{
|
|
51
|
+
"content": json.dumps({
|
|
52
|
+
"is_relevant": True,
|
|
53
|
+
"reason": "测试相关规则"
|
|
54
|
+
})
|
|
55
|
+
}]
|
|
56
|
+
elif "Merge all always-apply rules" in message_content:
|
|
57
|
+
return [{
|
|
58
|
+
"content": f"合并的必须应用规则摘要 (调用次数: {self.call_count})"
|
|
59
|
+
}]
|
|
60
|
+
elif "Generate an index directory" in message_content:
|
|
61
|
+
return [{
|
|
62
|
+
"content": f"条件规则索引目录 (调用次数: {self.call_count})"
|
|
63
|
+
}]
|
|
64
|
+
|
|
65
|
+
return [{"content": "模拟响应"}]
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class MockPromptFunction:
|
|
69
|
+
"""模拟 byzerllm.prompt 装饰器生成的函数"""
|
|
70
|
+
|
|
71
|
+
def __init__(self, return_value: str = "默认返回值"):
|
|
72
|
+
self.return_value = return_value
|
|
73
|
+
|
|
74
|
+
def with_llm(self, llm):
|
|
75
|
+
return self
|
|
76
|
+
|
|
77
|
+
def run(self, **kwargs):
|
|
78
|
+
return self.return_value
|
|
79
|
+
|
|
80
|
+
def with_return_type(self, return_type):
|
|
81
|
+
if return_type == RuleRelevance:
|
|
82
|
+
# 为 RuleRelevance 返回特殊的模拟对象
|
|
83
|
+
class MockRuleRelevance:
|
|
84
|
+
def __init__(self):
|
|
85
|
+
self.is_relevant = True
|
|
86
|
+
self.reason = "测试理由"
|
|
87
|
+
return MockWithReturnType(MockRuleRelevance())
|
|
88
|
+
return self
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
class MockWithReturnType:
|
|
92
|
+
"""模拟 with_return_type 后的对象"""
|
|
93
|
+
|
|
94
|
+
def __init__(self, return_obj):
|
|
95
|
+
self.return_obj = return_obj
|
|
96
|
+
|
|
97
|
+
def run(self, **kwargs):
|
|
98
|
+
return self.return_obj
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@pytest.fixture
|
|
102
|
+
def temp_project_dir():
|
|
103
|
+
"""创建临时项目目录"""
|
|
104
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
105
|
+
yield temp_dir
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
@pytest.fixture
|
|
109
|
+
def rules_dir(temp_project_dir):
|
|
110
|
+
"""创建临时规则目录"""
|
|
111
|
+
rules_path = os.path.join(temp_project_dir, ".autocoderrules")
|
|
112
|
+
os.makedirs(rules_path, exist_ok=True)
|
|
113
|
+
return rules_path
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@pytest.fixture
|
|
117
|
+
def mock_llm():
|
|
118
|
+
"""创建模拟 LLM"""
|
|
119
|
+
return MockLLM()
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@pytest.fixture(autouse=True)
|
|
123
|
+
def cleanup_rules_manager():
|
|
124
|
+
"""每个测试后清理单例"""
|
|
125
|
+
yield
|
|
126
|
+
reset_rules_manager()
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def create_rule_file(rules_dir: str, filename: str, content: str,
|
|
130
|
+
description: str = "", globs: list = None, always_apply: bool = False):
|
|
131
|
+
"""帮助函数:创建规则文件"""
|
|
132
|
+
globs = globs or []
|
|
133
|
+
|
|
134
|
+
# 构建 YAML 前置元数据
|
|
135
|
+
yaml_content = f"""---
|
|
136
|
+
description: "{description}"
|
|
137
|
+
globs: {json.dumps(globs)}
|
|
138
|
+
alwaysApply: {str(always_apply).lower()}
|
|
139
|
+
---
|
|
140
|
+
{content}"""
|
|
141
|
+
|
|
142
|
+
file_path = os.path.join(rules_dir, filename)
|
|
143
|
+
with open(file_path, 'w', encoding='utf-8') as f:
|
|
144
|
+
f.write(yaml_content)
|
|
145
|
+
|
|
146
|
+
return file_path
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def modify_rule_file(file_path: str, new_content: str):
|
|
150
|
+
"""帮助函数:修改规则文件内容"""
|
|
151
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
152
|
+
content = f.read()
|
|
153
|
+
|
|
154
|
+
# 保持 YAML 头部,只修改内容部分
|
|
155
|
+
if '---' in content:
|
|
156
|
+
parts = content.split('---', 2)
|
|
157
|
+
if len(parts) >= 3:
|
|
158
|
+
yaml_part = f"---{parts[1]}---"
|
|
159
|
+
modified_content = f"{yaml_part}\n{new_content}"
|
|
160
|
+
else:
|
|
161
|
+
modified_content = new_content
|
|
162
|
+
else:
|
|
163
|
+
modified_content = new_content
|
|
164
|
+
|
|
165
|
+
with open(file_path, 'w', encoding='utf-8') as f:
|
|
166
|
+
f.write(modified_content)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
class TestBasicFunctionality:
|
|
170
|
+
"""测试基本功能"""
|
|
171
|
+
|
|
172
|
+
def test_get_rules_empty_directory(self, temp_project_dir, rules_dir):
|
|
173
|
+
"""测试空目录的规则获取"""
|
|
174
|
+
with patch('os.getcwd', return_value=temp_project_dir):
|
|
175
|
+
rules = get_rules()
|
|
176
|
+
assert rules == {}
|
|
177
|
+
|
|
178
|
+
def test_get_rules_with_files(self, temp_project_dir, rules_dir):
|
|
179
|
+
"""测试有规则文件的目录"""
|
|
180
|
+
# 创建测试规则文件
|
|
181
|
+
create_rule_file(rules_dir, "rule1.md", "规则1内容", "测试规则1")
|
|
182
|
+
create_rule_file(rules_dir, "rule2.md", "规则2内容", "测试规则2", always_apply=True)
|
|
183
|
+
|
|
184
|
+
with patch('os.getcwd', return_value=temp_project_dir):
|
|
185
|
+
rules = get_rules()
|
|
186
|
+
assert len(rules) == 2
|
|
187
|
+
assert any("规则1内容" in content for content in rules.values())
|
|
188
|
+
assert any("规则2内容" in content for content in rules.values())
|
|
189
|
+
|
|
190
|
+
def test_get_parsed_rules(self, temp_project_dir, rules_dir):
|
|
191
|
+
"""测试解析后的规则获取"""
|
|
192
|
+
create_rule_file(rules_dir, "rule1.md", "规则1内容", "测试规则1", ["*.py"], True)
|
|
193
|
+
create_rule_file(rules_dir, "rule2.md", "规则2内容", "测试规则2", ["*.js"], False)
|
|
194
|
+
|
|
195
|
+
with patch('os.getcwd', return_value=temp_project_dir):
|
|
196
|
+
parsed_rules = get_parsed_rules()
|
|
197
|
+
assert len(parsed_rules) == 2
|
|
198
|
+
|
|
199
|
+
# 检查解析的内容
|
|
200
|
+
always_apply_rules = [r for r in parsed_rules if r.always_apply]
|
|
201
|
+
conditional_rules = [r for r in parsed_rules if not r.always_apply]
|
|
202
|
+
|
|
203
|
+
assert len(always_apply_rules) == 1
|
|
204
|
+
assert len(conditional_rules) == 1
|
|
205
|
+
assert always_apply_rules[0].description == "测试规则1"
|
|
206
|
+
assert always_apply_rules[0].globs == ["*.py"]
|
|
207
|
+
|
|
208
|
+
def test_parse_single_rule_file(self, rules_dir):
|
|
209
|
+
"""测试单个规则文件解析"""
|
|
210
|
+
file_path = create_rule_file(
|
|
211
|
+
rules_dir, "test.md", "测试内容",
|
|
212
|
+
"测试描述", ["*.py", "*.js"], True
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
rule = parse_rule_file(file_path)
|
|
216
|
+
assert rule.description == "测试描述"
|
|
217
|
+
assert rule.globs == ["*.py", "*.js"]
|
|
218
|
+
assert rule.always_apply is True
|
|
219
|
+
assert rule.content == "测试内容"
|
|
220
|
+
assert rule.file_path == file_path
|
|
221
|
+
|
|
222
|
+
def test_get_required_and_index_rules(self, temp_project_dir, rules_dir):
|
|
223
|
+
"""测试获取必须应用的规则和 Index.md 文件"""
|
|
224
|
+
create_rule_file(rules_dir, "rule1.md", "普通规则", "普通规则", always_apply=False)
|
|
225
|
+
create_rule_file(rules_dir, "rule2.md", "必须应用规则", "必须应用", always_apply=True)
|
|
226
|
+
create_rule_file(rules_dir, "Index.md", "索引文件", "索引", always_apply=False)
|
|
227
|
+
|
|
228
|
+
with patch('os.getcwd', return_value=temp_project_dir):
|
|
229
|
+
required_rules = get_required_and_index_rules()
|
|
230
|
+
assert len(required_rules) == 2 # 一个 always_apply=True,一个 Index.md
|
|
231
|
+
|
|
232
|
+
contents = list(required_rules.values())
|
|
233
|
+
assert "必须应用规则" in contents
|
|
234
|
+
assert "索引文件" in contents
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
class TestRuleSelection:
|
|
238
|
+
"""测试规则选择功能"""
|
|
239
|
+
|
|
240
|
+
def test_auto_select_rules_without_llm(self, temp_project_dir, rules_dir):
|
|
241
|
+
"""测试没有 LLM 时的规则选择(只选择 always_apply=True 的规则)"""
|
|
242
|
+
create_rule_file(rules_dir, "rule1.md", "普通规则", always_apply=False)
|
|
243
|
+
create_rule_file(rules_dir, "rule2.md", "必须应用规则", always_apply=True)
|
|
244
|
+
|
|
245
|
+
with patch('os.getcwd', return_value=temp_project_dir):
|
|
246
|
+
selected_rules = auto_select_rules("测试上下文")
|
|
247
|
+
assert len(selected_rules) == 1
|
|
248
|
+
assert "必须应用规则" in list(selected_rules.values())[0]
|
|
249
|
+
|
|
250
|
+
def test_auto_select_rules_with_llm(self, temp_project_dir, rules_dir, mock_llm):
|
|
251
|
+
"""测试有 LLM 时的规则选择"""
|
|
252
|
+
create_rule_file(rules_dir, "rule1.md", "条件规则", always_apply=False)
|
|
253
|
+
create_rule_file(rules_dir, "rule2.md", "必须应用规则", always_apply=True)
|
|
254
|
+
|
|
255
|
+
with patch('os.getcwd', return_value=temp_project_dir):
|
|
256
|
+
with patch('autocoder.common.rulefiles.core.selector.byzerllm.prompt') as mock_prompt:
|
|
257
|
+
# 模拟 prompt 函数
|
|
258
|
+
mock_prompt_func = MockPromptFunction()
|
|
259
|
+
mock_prompt.return_value = mock_prompt_func
|
|
260
|
+
|
|
261
|
+
# 模拟 RuleSelector
|
|
262
|
+
selector = RuleSelector(llm=mock_llm)
|
|
263
|
+
|
|
264
|
+
# 模拟 _evaluate_rule 方法返回相关规则
|
|
265
|
+
with patch.object(selector, '_evaluate_rule') as mock_eval:
|
|
266
|
+
mock_eval.return_value = (Mock(file_path="rule1.md", content="条件规则"), True, "相关")
|
|
267
|
+
|
|
268
|
+
selected_rules = selector.get_selected_rules_content("测试上下文")
|
|
269
|
+
# 应该包含 always_apply 规则和被 LLM 选择的条件规则
|
|
270
|
+
assert len(selected_rules) >= 1
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
class TestCacheFunctionality:
|
|
274
|
+
"""测试缓存功能"""
|
|
275
|
+
|
|
276
|
+
def test_cache_manager_basic_operations(self, temp_project_dir, rules_dir):
|
|
277
|
+
"""测试缓存管理器基本操作"""
|
|
278
|
+
cache_dir = os.path.join(rules_dir, ".cache")
|
|
279
|
+
cache_manager = RuleCacheManager(cache_dir=cache_dir, project_root=temp_project_dir)
|
|
280
|
+
|
|
281
|
+
# 创建测试规则
|
|
282
|
+
create_rule_file(rules_dir, "rule1.md", "测试规则", always_apply=True)
|
|
283
|
+
|
|
284
|
+
with patch('os.getcwd', return_value=temp_project_dir):
|
|
285
|
+
rules = get_parsed_rules()
|
|
286
|
+
|
|
287
|
+
# 测试摘要缓存
|
|
288
|
+
summary = AlwaysApplyRuleSummary(
|
|
289
|
+
summary="测试摘要",
|
|
290
|
+
rule_count=1,
|
|
291
|
+
covered_areas=["测试"]
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
# 保存缓存
|
|
295
|
+
cache_manager.save_summary_cache(summary, rules)
|
|
296
|
+
|
|
297
|
+
# 获取缓存
|
|
298
|
+
cached_summary = cache_manager.get_summary_cache(rules)
|
|
299
|
+
assert cached_summary is not None
|
|
300
|
+
assert cached_summary.summary == "测试摘要"
|
|
301
|
+
assert cached_summary.rule_count == 1
|
|
302
|
+
|
|
303
|
+
# 清理
|
|
304
|
+
cache_manager.cleanup()
|
|
305
|
+
|
|
306
|
+
def test_cache_invalidation_on_content_change(self, temp_project_dir, rules_dir, mock_llm):
|
|
307
|
+
"""测试内容变更时缓存失效"""
|
|
308
|
+
# 创建初始规则文件
|
|
309
|
+
rule_file = create_rule_file(rules_dir, "rule1.md", "初始内容", always_apply=True)
|
|
310
|
+
|
|
311
|
+
with patch('os.getcwd', return_value=temp_project_dir):
|
|
312
|
+
# 直接测试缓存管理器的功能
|
|
313
|
+
cache_dir = os.path.join(rules_dir, ".cache")
|
|
314
|
+
cache_manager = RuleCacheManager(cache_dir=cache_dir, project_root=temp_project_dir)
|
|
315
|
+
|
|
316
|
+
# 获取初始规则
|
|
317
|
+
rules = get_parsed_rules()
|
|
318
|
+
assert len(rules) == 1
|
|
319
|
+
initial_rule = rules[0]
|
|
320
|
+
assert initial_rule.content == "初始内容"
|
|
321
|
+
|
|
322
|
+
# 创建模拟摘要
|
|
323
|
+
summary1 = AlwaysApplyRuleSummary(
|
|
324
|
+
summary="初始摘要内容",
|
|
325
|
+
rule_count=1,
|
|
326
|
+
covered_areas=[]
|
|
327
|
+
)
|
|
328
|
+
|
|
329
|
+
# 保存缓存
|
|
330
|
+
cache_manager.save_summary_cache(summary1, rules)
|
|
331
|
+
|
|
332
|
+
# 验证缓存可以获取
|
|
333
|
+
cached_summary = cache_manager.get_summary_cache(rules)
|
|
334
|
+
assert cached_summary is not None
|
|
335
|
+
assert cached_summary.summary == "初始摘要内容"
|
|
336
|
+
|
|
337
|
+
# 修改规则文件内容
|
|
338
|
+
modify_rule_file(rule_file, "修改后的内容")
|
|
339
|
+
|
|
340
|
+
# 重新获取规则,内容应该已经变化
|
|
341
|
+
reset_rules_manager() # 重置管理器以重新加载文件
|
|
342
|
+
updated_rules = get_parsed_rules()
|
|
343
|
+
assert len(updated_rules) == 1
|
|
344
|
+
updated_rule = updated_rules[0]
|
|
345
|
+
assert updated_rule.content == "修改后的内容"
|
|
346
|
+
|
|
347
|
+
# 验证缓存应该失效(因为文件内容变了)
|
|
348
|
+
cached_summary_after = cache_manager.get_summary_cache(updated_rules)
|
|
349
|
+
assert cached_summary_after is None # 缓存应该失效
|
|
350
|
+
|
|
351
|
+
cache_manager.cleanup()
|
|
352
|
+
|
|
353
|
+
def test_cache_invalidation_on_file_addition(self, temp_project_dir, rules_dir, mock_llm):
|
|
354
|
+
"""测试添加文件时缓存失效"""
|
|
355
|
+
# 创建初始规则文件
|
|
356
|
+
create_rule_file(rules_dir, "rule1.md", "规则1", always_apply=True)
|
|
357
|
+
|
|
358
|
+
with patch('os.getcwd', return_value=temp_project_dir):
|
|
359
|
+
# 直接测试缓存管理器的功能
|
|
360
|
+
cache_dir = os.path.join(rules_dir, ".cache")
|
|
361
|
+
cache_manager = RuleCacheManager(cache_dir=cache_dir, project_root=temp_project_dir)
|
|
362
|
+
|
|
363
|
+
# 获取初始规则
|
|
364
|
+
rules = get_parsed_rules()
|
|
365
|
+
assert len(rules) == 1
|
|
366
|
+
|
|
367
|
+
# 创建模拟摘要
|
|
368
|
+
summary1 = AlwaysApplyRuleSummary(
|
|
369
|
+
summary="单个规则摘要",
|
|
370
|
+
rule_count=1,
|
|
371
|
+
covered_areas=[]
|
|
372
|
+
)
|
|
373
|
+
|
|
374
|
+
# 保存缓存
|
|
375
|
+
cache_manager.save_summary_cache(summary1, rules)
|
|
376
|
+
|
|
377
|
+
# 验证缓存可以获取
|
|
378
|
+
cached_summary = cache_manager.get_summary_cache(rules)
|
|
379
|
+
assert cached_summary is not None
|
|
380
|
+
assert cached_summary.rule_count == 1
|
|
381
|
+
|
|
382
|
+
# 添加新的规则文件
|
|
383
|
+
create_rule_file(rules_dir, "rule2.md", "规则2", always_apply=True)
|
|
384
|
+
|
|
385
|
+
# 重新获取规则,应该有两个规则了
|
|
386
|
+
reset_rules_manager() # 重置管理器以重新加载文件
|
|
387
|
+
updated_rules = get_parsed_rules()
|
|
388
|
+
assert len(updated_rules) == 2
|
|
389
|
+
|
|
390
|
+
# 验证缓存应该失效(因为规则数量变化了)
|
|
391
|
+
cached_summary_after = cache_manager.get_summary_cache(updated_rules)
|
|
392
|
+
assert cached_summary_after is None # 缓存应该失效
|
|
393
|
+
|
|
394
|
+
cache_manager.cleanup()
|
|
395
|
+
|
|
396
|
+
def test_cache_invalidation_on_file_deletion(self, temp_project_dir, rules_dir, mock_llm):
|
|
397
|
+
"""测试删除文件时缓存失效"""
|
|
398
|
+
# 创建两个规则文件
|
|
399
|
+
rule_file1 = create_rule_file(rules_dir, "rule1.md", "规则1", always_apply=True)
|
|
400
|
+
rule_file2 = create_rule_file(rules_dir, "rule2.md", "规则2", always_apply=True)
|
|
401
|
+
|
|
402
|
+
with patch('os.getcwd', return_value=temp_project_dir):
|
|
403
|
+
# 直接测试缓存管理器的功能
|
|
404
|
+
cache_dir = os.path.join(rules_dir, ".cache")
|
|
405
|
+
cache_manager = RuleCacheManager(cache_dir=cache_dir, project_root=temp_project_dir)
|
|
406
|
+
|
|
407
|
+
# 获取初始规则
|
|
408
|
+
rules = get_parsed_rules()
|
|
409
|
+
assert len(rules) == 2
|
|
410
|
+
|
|
411
|
+
# 创建模拟摘要
|
|
412
|
+
summary1 = AlwaysApplyRuleSummary(
|
|
413
|
+
summary="两个规则摘要",
|
|
414
|
+
rule_count=2,
|
|
415
|
+
covered_areas=[]
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
# 保存缓存
|
|
419
|
+
cache_manager.save_summary_cache(summary1, rules)
|
|
420
|
+
|
|
421
|
+
# 验证缓存可以获取
|
|
422
|
+
cached_summary = cache_manager.get_summary_cache(rules)
|
|
423
|
+
assert cached_summary is not None
|
|
424
|
+
assert cached_summary.rule_count == 2
|
|
425
|
+
|
|
426
|
+
# 删除一个规则文件
|
|
427
|
+
os.remove(rule_file2)
|
|
428
|
+
|
|
429
|
+
# 重新获取规则,应该只有一个规则了
|
|
430
|
+
reset_rules_manager() # 重置管理器以重新加载文件
|
|
431
|
+
updated_rules = get_parsed_rules()
|
|
432
|
+
assert len(updated_rules) == 1
|
|
433
|
+
|
|
434
|
+
# 验证缓存应该失效(因为规则数量减少了)
|
|
435
|
+
cached_summary_after = cache_manager.get_summary_cache(updated_rules)
|
|
436
|
+
assert cached_summary_after is None # 缓存应该失效
|
|
437
|
+
|
|
438
|
+
cache_manager.cleanup()
|
|
439
|
+
|
|
440
|
+
def test_conditional_rules_index_cache_invalidation(self, temp_project_dir, rules_dir, mock_llm):
|
|
441
|
+
"""测试条件规则索引的缓存失效"""
|
|
442
|
+
# 创建条件规则文件
|
|
443
|
+
rule_file = create_rule_file(rules_dir, "conditional.md", "条件规则内容", always_apply=False)
|
|
444
|
+
|
|
445
|
+
with patch('os.getcwd', return_value=temp_project_dir):
|
|
446
|
+
# 直接测试缓存管理器的功能
|
|
447
|
+
cache_dir = os.path.join(rules_dir, ".cache")
|
|
448
|
+
cache_manager = RuleCacheManager(cache_dir=cache_dir, project_root=temp_project_dir)
|
|
449
|
+
|
|
450
|
+
# 获取初始规则
|
|
451
|
+
rules = get_parsed_rules()
|
|
452
|
+
conditional_rules = [rule for rule in rules if not rule.always_apply]
|
|
453
|
+
assert len(conditional_rules) == 1
|
|
454
|
+
assert conditional_rules[0].content == "条件规则内容"
|
|
455
|
+
|
|
456
|
+
# 创建模拟索引
|
|
457
|
+
index1 = ConditionalRulesIndex(
|
|
458
|
+
index_content="初始索引内容",
|
|
459
|
+
rule_count=1,
|
|
460
|
+
categories=[]
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
# 保存缓存
|
|
464
|
+
cache_manager.save_index_cache(index1, conditional_rules)
|
|
465
|
+
|
|
466
|
+
# 验证缓存可以获取
|
|
467
|
+
cached_index = cache_manager.get_index_cache(conditional_rules)
|
|
468
|
+
assert cached_index is not None
|
|
469
|
+
assert cached_index.index_content == "初始索引内容"
|
|
470
|
+
|
|
471
|
+
# 修改条件规则文件
|
|
472
|
+
modify_rule_file(rule_file, "修改后的条件规则内容")
|
|
473
|
+
|
|
474
|
+
# 重新获取规则,内容应该已经变化
|
|
475
|
+
reset_rules_manager() # 重置管理器以重新加载文件
|
|
476
|
+
updated_rules = get_parsed_rules()
|
|
477
|
+
updated_conditional_rules = [rule for rule in updated_rules if not rule.always_apply]
|
|
478
|
+
assert len(updated_conditional_rules) == 1
|
|
479
|
+
assert updated_conditional_rules[0].content == "修改后的条件规则内容"
|
|
480
|
+
|
|
481
|
+
# 验证缓存应该失效(因为文件内容变了)
|
|
482
|
+
cached_index_after = cache_manager.get_index_cache(updated_conditional_rules)
|
|
483
|
+
assert cached_index_after is None # 缓存应该失效
|
|
484
|
+
|
|
485
|
+
cache_manager.cleanup()
|
|
486
|
+
|
|
487
|
+
def test_cache_validity_check(self, temp_project_dir, rules_dir):
|
|
488
|
+
"""测试缓存有效性检查"""
|
|
489
|
+
create_rule_file(rules_dir, "rule1.md", "测试规则", always_apply=True)
|
|
490
|
+
|
|
491
|
+
with patch('os.getcwd', return_value=temp_project_dir):
|
|
492
|
+
# 检查初始缓存状态
|
|
493
|
+
validity = check_rules_cache_validity()
|
|
494
|
+
assert validity['status'] == 'success'
|
|
495
|
+
|
|
496
|
+
# 获取缓存统计信息
|
|
497
|
+
stats = get_rules_cache_stats()
|
|
498
|
+
assert stats['status'] in ['success', 'no_rules_directories']
|
|
499
|
+
|
|
500
|
+
def test_manual_cache_invalidation(self, temp_project_dir, rules_dir, mock_llm):
|
|
501
|
+
"""测试手动缓存失效"""
|
|
502
|
+
create_rule_file(rules_dir, "rule1.md", "测试规则", always_apply=True)
|
|
503
|
+
|
|
504
|
+
with patch('os.getcwd', return_value=temp_project_dir):
|
|
505
|
+
# 直接测试缓存管理器的功能
|
|
506
|
+
cache_dir = os.path.join(rules_dir, ".cache")
|
|
507
|
+
cache_manager = RuleCacheManager(cache_dir=cache_dir, project_root=temp_project_dir)
|
|
508
|
+
|
|
509
|
+
# 获取规则
|
|
510
|
+
rules = get_parsed_rules()
|
|
511
|
+
assert len(rules) == 1
|
|
512
|
+
|
|
513
|
+
# 创建模拟摘要
|
|
514
|
+
summary1 = AlwaysApplyRuleSummary(
|
|
515
|
+
summary="测试摘要",
|
|
516
|
+
rule_count=1,
|
|
517
|
+
covered_areas=[]
|
|
518
|
+
)
|
|
519
|
+
|
|
520
|
+
# 保存缓存
|
|
521
|
+
cache_manager.save_summary_cache(summary1, rules)
|
|
522
|
+
|
|
523
|
+
# 验证缓存可以获取
|
|
524
|
+
cached_summary = cache_manager.get_summary_cache(rules)
|
|
525
|
+
assert cached_summary is not None
|
|
526
|
+
assert cached_summary.summary == "测试摘要"
|
|
527
|
+
|
|
528
|
+
# 手动失效缓存
|
|
529
|
+
invalidate_rules_cache()
|
|
530
|
+
|
|
531
|
+
# 验证缓存已被清理
|
|
532
|
+
cached_summary_after = cache_manager.get_summary_cache(rules)
|
|
533
|
+
assert cached_summary_after is None # 缓存应该已被清理
|
|
534
|
+
|
|
535
|
+
cache_manager.cleanup()
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
class TestMD5BasedCacheInvalidation:
|
|
539
|
+
"""测试基于 MD5 的缓存失效机制"""
|
|
540
|
+
|
|
541
|
+
def test_md5_calculation_accuracy(self, temp_project_dir, rules_dir):
|
|
542
|
+
"""测试 MD5 计算的准确性"""
|
|
543
|
+
cache_dir = os.path.join(rules_dir, ".cache")
|
|
544
|
+
cache_manager = RuleCacheManager(cache_dir=cache_dir, project_root=temp_project_dir)
|
|
545
|
+
|
|
546
|
+
# 创建测试文件
|
|
547
|
+
test_file = os.path.join(rules_dir, "test.md")
|
|
548
|
+
content = "测试内容"
|
|
549
|
+
with open(test_file, 'w', encoding='utf-8') as f:
|
|
550
|
+
f.write(content)
|
|
551
|
+
|
|
552
|
+
# 计算 MD5
|
|
553
|
+
md5_hash = cache_manager._calculate_file_md5(test_file)
|
|
554
|
+
|
|
555
|
+
# 验证 MD5 计算正确性
|
|
556
|
+
expected_md5 = hashlib.md5(content.encode('utf-8')).hexdigest()
|
|
557
|
+
assert md5_hash == expected_md5
|
|
558
|
+
|
|
559
|
+
cache_manager.cleanup()
|
|
560
|
+
|
|
561
|
+
def test_directory_md5_tracking(self, temp_project_dir, rules_dir):
|
|
562
|
+
"""测试目录级别的 MD5 跟踪"""
|
|
563
|
+
cache_dir = os.path.join(rules_dir, ".cache")
|
|
564
|
+
cache_manager = RuleCacheManager(cache_dir=cache_dir, project_root=temp_project_dir)
|
|
565
|
+
|
|
566
|
+
# 创建多个规则文件
|
|
567
|
+
create_rule_file(rules_dir, "rule1.md", "规则1内容")
|
|
568
|
+
create_rule_file(rules_dir, "rule2.md", "规则2内容")
|
|
569
|
+
|
|
570
|
+
# 计算目录 MD5 映射
|
|
571
|
+
rules_directories = cache_manager._get_rules_directories()
|
|
572
|
+
md5_map = cache_manager._calculate_directory_md5(rules_directories)
|
|
573
|
+
|
|
574
|
+
# 验证所有 .md 文件都被跟踪
|
|
575
|
+
md5_files = [path for path in md5_map.keys() if path.endswith('.md')]
|
|
576
|
+
assert len(md5_files) == 2
|
|
577
|
+
|
|
578
|
+
cache_manager.cleanup()
|
|
579
|
+
|
|
580
|
+
def test_subtle_content_changes_detected(self, temp_project_dir, rules_dir, mock_llm):
|
|
581
|
+
"""测试微小内容变更能被检测到"""
|
|
582
|
+
rule_file = create_rule_file(rules_dir, "rule1.md", "原始内容", always_apply=True)
|
|
583
|
+
|
|
584
|
+
with patch('os.getcwd', return_value=temp_project_dir):
|
|
585
|
+
# 直接测试缓存管理器的功能
|
|
586
|
+
cache_dir = os.path.join(rules_dir, ".cache")
|
|
587
|
+
cache_manager = RuleCacheManager(cache_dir=cache_dir, project_root=temp_project_dir)
|
|
588
|
+
|
|
589
|
+
# 获取初始规则
|
|
590
|
+
rules = get_parsed_rules()
|
|
591
|
+
assert len(rules) == 1
|
|
592
|
+
assert rules[0].content == "原始内容"
|
|
593
|
+
|
|
594
|
+
# 创建模拟摘要
|
|
595
|
+
summary1 = AlwaysApplyRuleSummary(
|
|
596
|
+
summary="原始摘要",
|
|
597
|
+
rule_count=1,
|
|
598
|
+
covered_areas=[]
|
|
599
|
+
)
|
|
600
|
+
|
|
601
|
+
# 保存缓存
|
|
602
|
+
cache_manager.save_summary_cache(summary1, rules)
|
|
603
|
+
|
|
604
|
+
# 验证缓存可以获取
|
|
605
|
+
cached_summary = cache_manager.get_summary_cache(rules)
|
|
606
|
+
assert cached_summary is not None
|
|
607
|
+
assert cached_summary.summary == "原始摘要"
|
|
608
|
+
|
|
609
|
+
# 进行微小的内容修改(仅添加一个字符)
|
|
610
|
+
modify_rule_file(rule_file, "原始内容a")
|
|
611
|
+
|
|
612
|
+
# 重新获取规则,内容应该已经变化
|
|
613
|
+
reset_rules_manager() # 重置管理器以重新加载文件
|
|
614
|
+
updated_rules = get_parsed_rules()
|
|
615
|
+
assert len(updated_rules) == 1
|
|
616
|
+
assert updated_rules[0].content == "原始内容a"
|
|
617
|
+
|
|
618
|
+
# 验证缓存应该失效(即使只是微小变化)
|
|
619
|
+
cached_summary_after = cache_manager.get_summary_cache(updated_rules)
|
|
620
|
+
assert cached_summary_after is None # 缓存应该失效
|
|
621
|
+
|
|
622
|
+
cache_manager.cleanup()
|
|
623
|
+
|
|
624
|
+
def test_cache_metadata_structure(self, temp_project_dir, rules_dir, mock_llm):
|
|
625
|
+
"""测试缓存元数据结构"""
|
|
626
|
+
create_rule_file(rules_dir, "rule1.md", "测试规则", always_apply=True)
|
|
627
|
+
|
|
628
|
+
with patch('os.getcwd', return_value=temp_project_dir):
|
|
629
|
+
# 直接测试缓存管理器的功能
|
|
630
|
+
cache_dir = os.path.join(rules_dir, ".cache")
|
|
631
|
+
cache_manager = RuleCacheManager(cache_dir=cache_dir, project_root=temp_project_dir)
|
|
632
|
+
|
|
633
|
+
# 获取规则
|
|
634
|
+
rules = get_parsed_rules()
|
|
635
|
+
assert len(rules) == 1
|
|
636
|
+
|
|
637
|
+
# 创建模拟摘要
|
|
638
|
+
summary = AlwaysApplyRuleSummary(
|
|
639
|
+
summary="测试摘要",
|
|
640
|
+
rule_count=1,
|
|
641
|
+
covered_areas=[]
|
|
642
|
+
)
|
|
643
|
+
|
|
644
|
+
# 保存缓存以创建元数据
|
|
645
|
+
cache_manager.save_summary_cache(summary, rules)
|
|
646
|
+
|
|
647
|
+
# 检查缓存元数据结构
|
|
648
|
+
metadata_file = os.path.join(cache_dir, "cache_metadata.json")
|
|
649
|
+
|
|
650
|
+
assert os.path.exists(metadata_file)
|
|
651
|
+
with open(metadata_file, 'r', encoding='utf-8') as f:
|
|
652
|
+
metadata = json.load(f)
|
|
653
|
+
|
|
654
|
+
# 验证元数据结构
|
|
655
|
+
assert 'summary' in metadata
|
|
656
|
+
summary_info = metadata['summary']
|
|
657
|
+
assert 'rules_hash' in summary_info
|
|
658
|
+
assert 'timestamp' in summary_info
|
|
659
|
+
assert 'rule_count' in summary_info
|
|
660
|
+
assert 'rule_files' in summary_info
|
|
661
|
+
assert 'file_md5_map' in summary_info
|
|
662
|
+
assert 'cache_version' in summary_info
|
|
663
|
+
|
|
664
|
+
# 验证数据的正确性
|
|
665
|
+
assert summary_info['rule_count'] == 1
|
|
666
|
+
assert len(summary_info['rule_files']) == 1
|
|
667
|
+
assert len(summary_info['file_md5_map']) == 1
|
|
668
|
+
assert summary_info['cache_version'] == '1.1'
|
|
669
|
+
|
|
670
|
+
cache_manager.cleanup()
|
|
671
|
+
|
|
672
|
+
|
|
673
|
+
class TestIntegrationScenarios:
|
|
674
|
+
"""集成测试场景"""
|
|
675
|
+
|
|
676
|
+
def test_full_workflow_with_cache_invalidation(self, temp_project_dir, rules_dir, mock_llm):
|
|
677
|
+
"""测试完整的工作流程和缓存失效"""
|
|
678
|
+
# 第一阶段:创建初始规则
|
|
679
|
+
create_rule_file(rules_dir, "always.md", "必须应用规则", always_apply=True)
|
|
680
|
+
create_rule_file(rules_dir, "conditional.md", "条件规则", always_apply=False)
|
|
681
|
+
|
|
682
|
+
with patch('os.getcwd', return_value=temp_project_dir):
|
|
683
|
+
# 直接测试缓存管理器的功能
|
|
684
|
+
cache_dir = os.path.join(rules_dir, ".cache")
|
|
685
|
+
cache_manager = RuleCacheManager(cache_dir=cache_dir, project_root=temp_project_dir)
|
|
686
|
+
|
|
687
|
+
# 第一阶段:获取初始规则
|
|
688
|
+
rules = get_parsed_rules()
|
|
689
|
+
always_apply_rules = [rule for rule in rules if rule.always_apply]
|
|
690
|
+
conditional_rules = [rule for rule in rules if not rule.always_apply]
|
|
691
|
+
|
|
692
|
+
assert len(always_apply_rules) == 1
|
|
693
|
+
assert len(conditional_rules) == 1
|
|
694
|
+
|
|
695
|
+
# 创建初始缓存
|
|
696
|
+
summary1 = AlwaysApplyRuleSummary(
|
|
697
|
+
summary="初始摘要",
|
|
698
|
+
rule_count=1,
|
|
699
|
+
covered_areas=[]
|
|
700
|
+
)
|
|
701
|
+
cache_manager.save_summary_cache(summary1, always_apply_rules)
|
|
702
|
+
|
|
703
|
+
index1 = ConditionalRulesIndex(
|
|
704
|
+
index_content="初始索引",
|
|
705
|
+
rule_count=1,
|
|
706
|
+
categories=[]
|
|
707
|
+
)
|
|
708
|
+
cache_manager.save_index_cache(index1, conditional_rules)
|
|
709
|
+
|
|
710
|
+
# 验证缓存
|
|
711
|
+
assert cache_manager.get_summary_cache(always_apply_rules) is not None
|
|
712
|
+
assert cache_manager.get_index_cache(conditional_rules) is not None
|
|
713
|
+
|
|
714
|
+
# 第二阶段:添加新规则
|
|
715
|
+
create_rule_file(rules_dir, "always2.md", "第二个必须应用规则", always_apply=True)
|
|
716
|
+
create_rule_file(rules_dir, "conditional2.md", "第二个条件规则", always_apply=False)
|
|
717
|
+
|
|
718
|
+
# 重新获取规则
|
|
719
|
+
reset_rules_manager()
|
|
720
|
+
updated_rules = get_parsed_rules()
|
|
721
|
+
updated_always_apply_rules = [rule for rule in updated_rules if rule.always_apply]
|
|
722
|
+
updated_conditional_rules = [rule for rule in updated_rules if not rule.always_apply]
|
|
723
|
+
|
|
724
|
+
assert len(updated_always_apply_rules) == 2
|
|
725
|
+
assert len(updated_conditional_rules) == 2
|
|
726
|
+
|
|
727
|
+
# 验证缓存失效
|
|
728
|
+
assert cache_manager.get_summary_cache(updated_always_apply_rules) is None
|
|
729
|
+
assert cache_manager.get_index_cache(updated_conditional_rules) is None
|
|
730
|
+
|
|
731
|
+
cache_manager.cleanup()
|
|
732
|
+
|
|
733
|
+
def test_concurrent_cache_access_safety(self, temp_project_dir, rules_dir, mock_llm):
|
|
734
|
+
"""测试并发缓存访问的安全性"""
|
|
735
|
+
create_rule_file(rules_dir, "rule1.md", "测试规则", always_apply=True)
|
|
736
|
+
|
|
737
|
+
with patch('os.getcwd', return_value=temp_project_dir):
|
|
738
|
+
# 直接测试缓存管理器的功能
|
|
739
|
+
cache_dir = os.path.join(rules_dir, ".cache")
|
|
740
|
+
|
|
741
|
+
# 获取规则
|
|
742
|
+
rules = get_parsed_rules()
|
|
743
|
+
assert len(rules) == 1
|
|
744
|
+
|
|
745
|
+
# 创建模拟摘要
|
|
746
|
+
summary = AlwaysApplyRuleSummary(
|
|
747
|
+
summary="并发测试摘要",
|
|
748
|
+
rule_count=1,
|
|
749
|
+
covered_areas=[]
|
|
750
|
+
)
|
|
751
|
+
|
|
752
|
+
# 模拟并发访问缓存管理器
|
|
753
|
+
cache_managers = []
|
|
754
|
+
for i in range(3):
|
|
755
|
+
cache_manager = RuleCacheManager(cache_dir=cache_dir, project_root=temp_project_dir)
|
|
756
|
+
cache_managers.append(cache_manager)
|
|
757
|
+
|
|
758
|
+
# 所有缓存管理器都保存相同的摘要
|
|
759
|
+
for cache_manager in cache_managers:
|
|
760
|
+
cache_manager.save_summary_cache(summary, rules)
|
|
761
|
+
|
|
762
|
+
# 验证所有缓存管理器都能正确获取缓存
|
|
763
|
+
cached_summaries = []
|
|
764
|
+
for cache_manager in cache_managers:
|
|
765
|
+
cached_summary = cache_manager.get_summary_cache(rules)
|
|
766
|
+
cached_summaries.append(cached_summary)
|
|
767
|
+
|
|
768
|
+
# 验证结果一致性
|
|
769
|
+
for cached_summary in cached_summaries:
|
|
770
|
+
assert cached_summary is not None
|
|
771
|
+
assert cached_summary.summary == "并发测试摘要"
|
|
772
|
+
assert cached_summary.rule_count == 1
|
|
773
|
+
|
|
774
|
+
# 清理所有缓存管理器
|
|
775
|
+
for cache_manager in cache_managers:
|
|
776
|
+
cache_manager.cleanup()
|