crackerjack 0.18.2__py3-none-any.whl → 0.45.2__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.
- crackerjack/README.md +19 -0
- crackerjack/__init__.py +96 -2
- crackerjack/__main__.py +637 -138
- crackerjack/adapters/README.md +18 -0
- crackerjack/adapters/__init__.py +39 -0
- crackerjack/adapters/_output_paths.py +167 -0
- crackerjack/adapters/_qa_adapter_base.py +309 -0
- crackerjack/adapters/_tool_adapter_base.py +706 -0
- crackerjack/adapters/ai/README.md +65 -0
- crackerjack/adapters/ai/__init__.py +5 -0
- crackerjack/adapters/ai/claude.py +853 -0
- crackerjack/adapters/complexity/README.md +53 -0
- crackerjack/adapters/complexity/__init__.py +10 -0
- crackerjack/adapters/complexity/complexipy.py +641 -0
- crackerjack/adapters/dependency/__init__.py +22 -0
- crackerjack/adapters/dependency/pip_audit.py +418 -0
- crackerjack/adapters/format/README.md +72 -0
- crackerjack/adapters/format/__init__.py +11 -0
- crackerjack/adapters/format/mdformat.py +313 -0
- crackerjack/adapters/format/ruff.py +516 -0
- crackerjack/adapters/lint/README.md +47 -0
- crackerjack/adapters/lint/__init__.py +11 -0
- crackerjack/adapters/lint/codespell.py +273 -0
- crackerjack/adapters/lsp/README.md +49 -0
- crackerjack/adapters/lsp/__init__.py +27 -0
- crackerjack/adapters/lsp/_base.py +194 -0
- crackerjack/adapters/lsp/_client.py +358 -0
- crackerjack/adapters/lsp/_manager.py +193 -0
- crackerjack/adapters/lsp/skylos.py +283 -0
- crackerjack/adapters/lsp/zuban.py +557 -0
- crackerjack/adapters/refactor/README.md +59 -0
- crackerjack/adapters/refactor/__init__.py +12 -0
- crackerjack/adapters/refactor/creosote.py +318 -0
- crackerjack/adapters/refactor/refurb.py +406 -0
- crackerjack/adapters/refactor/skylos.py +494 -0
- crackerjack/adapters/sast/README.md +132 -0
- crackerjack/adapters/sast/__init__.py +32 -0
- crackerjack/adapters/sast/_base.py +201 -0
- crackerjack/adapters/sast/bandit.py +423 -0
- crackerjack/adapters/sast/pyscn.py +405 -0
- crackerjack/adapters/sast/semgrep.py +241 -0
- crackerjack/adapters/security/README.md +111 -0
- crackerjack/adapters/security/__init__.py +17 -0
- crackerjack/adapters/security/gitleaks.py +339 -0
- crackerjack/adapters/type/README.md +52 -0
- crackerjack/adapters/type/__init__.py +12 -0
- crackerjack/adapters/type/pyrefly.py +402 -0
- crackerjack/adapters/type/ty.py +402 -0
- crackerjack/adapters/type/zuban.py +522 -0
- crackerjack/adapters/utility/README.md +51 -0
- crackerjack/adapters/utility/__init__.py +10 -0
- crackerjack/adapters/utility/checks.py +884 -0
- crackerjack/agents/README.md +264 -0
- crackerjack/agents/__init__.py +66 -0
- crackerjack/agents/architect_agent.py +238 -0
- crackerjack/agents/base.py +167 -0
- crackerjack/agents/claude_code_bridge.py +641 -0
- crackerjack/agents/coordinator.py +600 -0
- crackerjack/agents/documentation_agent.py +520 -0
- crackerjack/agents/dry_agent.py +585 -0
- crackerjack/agents/enhanced_coordinator.py +279 -0
- crackerjack/agents/enhanced_proactive_agent.py +185 -0
- crackerjack/agents/error_middleware.py +53 -0
- crackerjack/agents/formatting_agent.py +230 -0
- crackerjack/agents/helpers/__init__.py +9 -0
- crackerjack/agents/helpers/performance/__init__.py +22 -0
- crackerjack/agents/helpers/performance/performance_ast_analyzer.py +357 -0
- crackerjack/agents/helpers/performance/performance_pattern_detector.py +909 -0
- crackerjack/agents/helpers/performance/performance_recommender.py +572 -0
- crackerjack/agents/helpers/refactoring/__init__.py +22 -0
- crackerjack/agents/helpers/refactoring/code_transformer.py +536 -0
- crackerjack/agents/helpers/refactoring/complexity_analyzer.py +344 -0
- crackerjack/agents/helpers/refactoring/dead_code_detector.py +437 -0
- crackerjack/agents/helpers/test_creation/__init__.py +19 -0
- crackerjack/agents/helpers/test_creation/test_ast_analyzer.py +216 -0
- crackerjack/agents/helpers/test_creation/test_coverage_analyzer.py +643 -0
- crackerjack/agents/helpers/test_creation/test_template_generator.py +1031 -0
- crackerjack/agents/import_optimization_agent.py +1181 -0
- crackerjack/agents/performance_agent.py +325 -0
- crackerjack/agents/performance_helpers.py +205 -0
- crackerjack/agents/proactive_agent.py +55 -0
- crackerjack/agents/refactoring_agent.py +511 -0
- crackerjack/agents/refactoring_helpers.py +247 -0
- crackerjack/agents/security_agent.py +793 -0
- crackerjack/agents/semantic_agent.py +479 -0
- crackerjack/agents/semantic_helpers.py +356 -0
- crackerjack/agents/test_creation_agent.py +570 -0
- crackerjack/agents/test_specialist_agent.py +526 -0
- crackerjack/agents/tracker.py +110 -0
- crackerjack/api.py +647 -0
- crackerjack/cli/README.md +394 -0
- crackerjack/cli/__init__.py +24 -0
- crackerjack/cli/cache_handlers.py +209 -0
- crackerjack/cli/cache_handlers_enhanced.py +680 -0
- crackerjack/cli/facade.py +162 -0
- crackerjack/cli/formatting.py +13 -0
- crackerjack/cli/handlers/__init__.py +85 -0
- crackerjack/cli/handlers/advanced.py +103 -0
- crackerjack/cli/handlers/ai_features.py +62 -0
- crackerjack/cli/handlers/analytics.py +479 -0
- crackerjack/cli/handlers/changelog.py +271 -0
- crackerjack/cli/handlers/config_handlers.py +16 -0
- crackerjack/cli/handlers/coverage.py +84 -0
- crackerjack/cli/handlers/documentation.py +280 -0
- crackerjack/cli/handlers/main_handlers.py +497 -0
- crackerjack/cli/handlers/monitoring.py +371 -0
- crackerjack/cli/handlers.py +700 -0
- crackerjack/cli/interactive.py +488 -0
- crackerjack/cli/options.py +1216 -0
- crackerjack/cli/semantic_handlers.py +292 -0
- crackerjack/cli/utils.py +19 -0
- crackerjack/cli/version.py +19 -0
- crackerjack/code_cleaner.py +1307 -0
- crackerjack/config/README.md +472 -0
- crackerjack/config/__init__.py +275 -0
- crackerjack/config/global_lock_config.py +207 -0
- crackerjack/config/hooks.py +390 -0
- crackerjack/config/loader.py +239 -0
- crackerjack/config/settings.py +141 -0
- crackerjack/config/tool_commands.py +331 -0
- crackerjack/core/README.md +393 -0
- crackerjack/core/__init__.py +0 -0
- crackerjack/core/async_workflow_orchestrator.py +738 -0
- crackerjack/core/autofix_coordinator.py +282 -0
- crackerjack/core/container.py +105 -0
- crackerjack/core/enhanced_container.py +583 -0
- crackerjack/core/file_lifecycle.py +472 -0
- crackerjack/core/performance.py +244 -0
- crackerjack/core/performance_monitor.py +357 -0
- crackerjack/core/phase_coordinator.py +1227 -0
- crackerjack/core/proactive_workflow.py +267 -0
- crackerjack/core/resource_manager.py +425 -0
- crackerjack/core/retry.py +275 -0
- crackerjack/core/service_watchdog.py +601 -0
- crackerjack/core/session_coordinator.py +239 -0
- crackerjack/core/timeout_manager.py +563 -0
- crackerjack/core/websocket_lifecycle.py +410 -0
- crackerjack/core/workflow/__init__.py +21 -0
- crackerjack/core/workflow/workflow_ai_coordinator.py +863 -0
- crackerjack/core/workflow/workflow_event_orchestrator.py +1107 -0
- crackerjack/core/workflow/workflow_issue_parser.py +714 -0
- crackerjack/core/workflow/workflow_phase_executor.py +1158 -0
- crackerjack/core/workflow/workflow_security_gates.py +400 -0
- crackerjack/core/workflow_orchestrator.py +2243 -0
- crackerjack/data/README.md +11 -0
- crackerjack/data/__init__.py +8 -0
- crackerjack/data/models.py +79 -0
- crackerjack/data/repository.py +210 -0
- crackerjack/decorators/README.md +180 -0
- crackerjack/decorators/__init__.py +35 -0
- crackerjack/decorators/error_handling.py +649 -0
- crackerjack/decorators/error_handling_decorators.py +334 -0
- crackerjack/decorators/helpers.py +58 -0
- crackerjack/decorators/patterns.py +281 -0
- crackerjack/decorators/utils.py +58 -0
- crackerjack/docs/INDEX.md +11 -0
- crackerjack/docs/README.md +11 -0
- crackerjack/docs/generated/api/API_REFERENCE.md +10895 -0
- crackerjack/docs/generated/api/CLI_REFERENCE.md +109 -0
- crackerjack/docs/generated/api/CROSS_REFERENCES.md +1755 -0
- crackerjack/docs/generated/api/PROTOCOLS.md +3 -0
- crackerjack/docs/generated/api/SERVICES.md +1252 -0
- crackerjack/documentation/README.md +11 -0
- crackerjack/documentation/__init__.py +31 -0
- crackerjack/documentation/ai_templates.py +756 -0
- crackerjack/documentation/dual_output_generator.py +767 -0
- crackerjack/documentation/mkdocs_integration.py +518 -0
- crackerjack/documentation/reference_generator.py +1065 -0
- crackerjack/dynamic_config.py +678 -0
- crackerjack/errors.py +378 -0
- crackerjack/events/README.md +11 -0
- crackerjack/events/__init__.py +16 -0
- crackerjack/events/telemetry.py +175 -0
- crackerjack/events/workflow_bus.py +346 -0
- crackerjack/exceptions/README.md +301 -0
- crackerjack/exceptions/__init__.py +5 -0
- crackerjack/exceptions/config.py +4 -0
- crackerjack/exceptions/tool_execution_error.py +245 -0
- crackerjack/executors/README.md +591 -0
- crackerjack/executors/__init__.py +13 -0
- crackerjack/executors/async_hook_executor.py +938 -0
- crackerjack/executors/cached_hook_executor.py +316 -0
- crackerjack/executors/hook_executor.py +1295 -0
- crackerjack/executors/hook_lock_manager.py +708 -0
- crackerjack/executors/individual_hook_executor.py +739 -0
- crackerjack/executors/lsp_aware_hook_executor.py +349 -0
- crackerjack/executors/progress_hook_executor.py +282 -0
- crackerjack/executors/tool_proxy.py +433 -0
- crackerjack/hooks/README.md +485 -0
- crackerjack/hooks/lsp_hook.py +93 -0
- crackerjack/intelligence/README.md +557 -0
- crackerjack/intelligence/__init__.py +37 -0
- crackerjack/intelligence/adaptive_learning.py +693 -0
- crackerjack/intelligence/agent_orchestrator.py +485 -0
- crackerjack/intelligence/agent_registry.py +377 -0
- crackerjack/intelligence/agent_selector.py +439 -0
- crackerjack/intelligence/integration.py +250 -0
- crackerjack/interactive.py +719 -0
- crackerjack/managers/README.md +369 -0
- crackerjack/managers/__init__.py +11 -0
- crackerjack/managers/async_hook_manager.py +135 -0
- crackerjack/managers/hook_manager.py +585 -0
- crackerjack/managers/publish_manager.py +631 -0
- crackerjack/managers/test_command_builder.py +391 -0
- crackerjack/managers/test_executor.py +474 -0
- crackerjack/managers/test_manager.py +1357 -0
- crackerjack/managers/test_progress.py +187 -0
- crackerjack/mcp/README.md +374 -0
- crackerjack/mcp/__init__.py +0 -0
- crackerjack/mcp/cache.py +352 -0
- crackerjack/mcp/client_runner.py +121 -0
- crackerjack/mcp/context.py +802 -0
- crackerjack/mcp/dashboard.py +657 -0
- crackerjack/mcp/enhanced_progress_monitor.py +493 -0
- crackerjack/mcp/file_monitor.py +394 -0
- crackerjack/mcp/progress_components.py +607 -0
- crackerjack/mcp/progress_monitor.py +1016 -0
- crackerjack/mcp/rate_limiter.py +336 -0
- crackerjack/mcp/server.py +24 -0
- crackerjack/mcp/server_core.py +526 -0
- crackerjack/mcp/service_watchdog.py +505 -0
- crackerjack/mcp/state.py +407 -0
- crackerjack/mcp/task_manager.py +259 -0
- crackerjack/mcp/tools/README.md +27 -0
- crackerjack/mcp/tools/__init__.py +19 -0
- crackerjack/mcp/tools/core_tools.py +469 -0
- crackerjack/mcp/tools/error_analyzer.py +283 -0
- crackerjack/mcp/tools/execution_tools.py +384 -0
- crackerjack/mcp/tools/intelligence_tool_registry.py +46 -0
- crackerjack/mcp/tools/intelligence_tools.py +264 -0
- crackerjack/mcp/tools/monitoring_tools.py +628 -0
- crackerjack/mcp/tools/proactive_tools.py +367 -0
- crackerjack/mcp/tools/progress_tools.py +222 -0
- crackerjack/mcp/tools/semantic_tools.py +584 -0
- crackerjack/mcp/tools/utility_tools.py +358 -0
- crackerjack/mcp/tools/workflow_executor.py +699 -0
- crackerjack/mcp/websocket/README.md +31 -0
- crackerjack/mcp/websocket/__init__.py +14 -0
- crackerjack/mcp/websocket/app.py +54 -0
- crackerjack/mcp/websocket/endpoints.py +492 -0
- crackerjack/mcp/websocket/event_bridge.py +188 -0
- crackerjack/mcp/websocket/jobs.py +406 -0
- crackerjack/mcp/websocket/monitoring/__init__.py +25 -0
- crackerjack/mcp/websocket/monitoring/api/__init__.py +19 -0
- crackerjack/mcp/websocket/monitoring/api/dependencies.py +141 -0
- crackerjack/mcp/websocket/monitoring/api/heatmap.py +154 -0
- crackerjack/mcp/websocket/monitoring/api/intelligence.py +199 -0
- crackerjack/mcp/websocket/monitoring/api/metrics.py +203 -0
- crackerjack/mcp/websocket/monitoring/api/telemetry.py +101 -0
- crackerjack/mcp/websocket/monitoring/dashboard.py +18 -0
- crackerjack/mcp/websocket/monitoring/factory.py +109 -0
- crackerjack/mcp/websocket/monitoring/filters.py +10 -0
- crackerjack/mcp/websocket/monitoring/metrics.py +64 -0
- crackerjack/mcp/websocket/monitoring/models.py +90 -0
- crackerjack/mcp/websocket/monitoring/utils.py +171 -0
- crackerjack/mcp/websocket/monitoring/websocket_manager.py +78 -0
- crackerjack/mcp/websocket/monitoring/websockets/__init__.py +17 -0
- crackerjack/mcp/websocket/monitoring/websockets/dependencies.py +126 -0
- crackerjack/mcp/websocket/monitoring/websockets/heatmap.py +176 -0
- crackerjack/mcp/websocket/monitoring/websockets/intelligence.py +291 -0
- crackerjack/mcp/websocket/monitoring/websockets/metrics.py +291 -0
- crackerjack/mcp/websocket/monitoring_endpoints.py +21 -0
- crackerjack/mcp/websocket/server.py +174 -0
- crackerjack/mcp/websocket/websocket_handler.py +276 -0
- crackerjack/mcp/websocket_server.py +10 -0
- crackerjack/models/README.md +308 -0
- crackerjack/models/__init__.py +40 -0
- crackerjack/models/config.py +730 -0
- crackerjack/models/config_adapter.py +265 -0
- crackerjack/models/protocols.py +1535 -0
- crackerjack/models/pydantic_models.py +320 -0
- crackerjack/models/qa_config.py +145 -0
- crackerjack/models/qa_results.py +134 -0
- crackerjack/models/resource_protocols.py +299 -0
- crackerjack/models/results.py +35 -0
- crackerjack/models/semantic_models.py +258 -0
- crackerjack/models/task.py +173 -0
- crackerjack/models/test_models.py +60 -0
- crackerjack/monitoring/README.md +11 -0
- crackerjack/monitoring/__init__.py +0 -0
- crackerjack/monitoring/ai_agent_watchdog.py +405 -0
- crackerjack/monitoring/metrics_collector.py +427 -0
- crackerjack/monitoring/regression_prevention.py +580 -0
- crackerjack/monitoring/websocket_server.py +406 -0
- crackerjack/orchestration/README.md +340 -0
- crackerjack/orchestration/__init__.py +43 -0
- crackerjack/orchestration/advanced_orchestrator.py +894 -0
- crackerjack/orchestration/cache/README.md +312 -0
- crackerjack/orchestration/cache/__init__.py +37 -0
- crackerjack/orchestration/cache/memory_cache.py +338 -0
- crackerjack/orchestration/cache/tool_proxy_cache.py +340 -0
- crackerjack/orchestration/config.py +297 -0
- crackerjack/orchestration/coverage_improvement.py +180 -0
- crackerjack/orchestration/execution_strategies.py +361 -0
- crackerjack/orchestration/hook_orchestrator.py +1398 -0
- crackerjack/orchestration/strategies/README.md +401 -0
- crackerjack/orchestration/strategies/__init__.py +39 -0
- crackerjack/orchestration/strategies/adaptive_strategy.py +630 -0
- crackerjack/orchestration/strategies/parallel_strategy.py +237 -0
- crackerjack/orchestration/strategies/sequential_strategy.py +299 -0
- crackerjack/orchestration/test_progress_streamer.py +647 -0
- crackerjack/plugins/README.md +11 -0
- crackerjack/plugins/__init__.py +15 -0
- crackerjack/plugins/base.py +200 -0
- crackerjack/plugins/hooks.py +254 -0
- crackerjack/plugins/loader.py +335 -0
- crackerjack/plugins/managers.py +264 -0
- crackerjack/py313.py +191 -0
- crackerjack/security/README.md +11 -0
- crackerjack/security/__init__.py +0 -0
- crackerjack/security/audit.py +197 -0
- crackerjack/services/README.md +374 -0
- crackerjack/services/__init__.py +9 -0
- crackerjack/services/ai/README.md +295 -0
- crackerjack/services/ai/__init__.py +7 -0
- crackerjack/services/ai/advanced_optimizer.py +878 -0
- crackerjack/services/ai/contextual_ai_assistant.py +542 -0
- crackerjack/services/ai/embeddings.py +444 -0
- crackerjack/services/ai/intelligent_commit.py +328 -0
- crackerjack/services/ai/predictive_analytics.py +510 -0
- crackerjack/services/anomaly_detector.py +392 -0
- crackerjack/services/api_extractor.py +617 -0
- crackerjack/services/backup_service.py +467 -0
- crackerjack/services/bounded_status_operations.py +530 -0
- crackerjack/services/cache.py +369 -0
- crackerjack/services/changelog_automation.py +399 -0
- crackerjack/services/command_execution_service.py +305 -0
- crackerjack/services/config_integrity.py +132 -0
- crackerjack/services/config_merge.py +546 -0
- crackerjack/services/config_service.py +198 -0
- crackerjack/services/config_template.py +493 -0
- crackerjack/services/coverage_badge_service.py +173 -0
- crackerjack/services/coverage_ratchet.py +381 -0
- crackerjack/services/debug.py +733 -0
- crackerjack/services/dependency_analyzer.py +460 -0
- crackerjack/services/dependency_monitor.py +622 -0
- crackerjack/services/documentation_generator.py +493 -0
- crackerjack/services/documentation_service.py +704 -0
- crackerjack/services/enhanced_filesystem.py +497 -0
- crackerjack/services/enterprise_optimizer.py +865 -0
- crackerjack/services/error_pattern_analyzer.py +676 -0
- crackerjack/services/file_filter.py +221 -0
- crackerjack/services/file_hasher.py +149 -0
- crackerjack/services/file_io_service.py +361 -0
- crackerjack/services/file_modifier.py +615 -0
- crackerjack/services/filesystem.py +381 -0
- crackerjack/services/git.py +422 -0
- crackerjack/services/health_metrics.py +615 -0
- crackerjack/services/heatmap_generator.py +744 -0
- crackerjack/services/incremental_executor.py +380 -0
- crackerjack/services/initialization.py +823 -0
- crackerjack/services/input_validator.py +668 -0
- crackerjack/services/intelligent_commit.py +327 -0
- crackerjack/services/log_manager.py +289 -0
- crackerjack/services/logging.py +228 -0
- crackerjack/services/lsp_client.py +628 -0
- crackerjack/services/memory_optimizer.py +414 -0
- crackerjack/services/metrics.py +587 -0
- crackerjack/services/monitoring/README.md +30 -0
- crackerjack/services/monitoring/__init__.py +9 -0
- crackerjack/services/monitoring/dependency_monitor.py +678 -0
- crackerjack/services/monitoring/error_pattern_analyzer.py +676 -0
- crackerjack/services/monitoring/health_metrics.py +716 -0
- crackerjack/services/monitoring/metrics.py +587 -0
- crackerjack/services/monitoring/performance_benchmarks.py +410 -0
- crackerjack/services/monitoring/performance_cache.py +388 -0
- crackerjack/services/monitoring/performance_monitor.py +569 -0
- crackerjack/services/parallel_executor.py +527 -0
- crackerjack/services/pattern_cache.py +333 -0
- crackerjack/services/pattern_detector.py +478 -0
- crackerjack/services/patterns/__init__.py +142 -0
- crackerjack/services/patterns/agents.py +107 -0
- crackerjack/services/patterns/code/__init__.py +15 -0
- crackerjack/services/patterns/code/detection.py +118 -0
- crackerjack/services/patterns/code/imports.py +107 -0
- crackerjack/services/patterns/code/paths.py +159 -0
- crackerjack/services/patterns/code/performance.py +119 -0
- crackerjack/services/patterns/code/replacement.py +36 -0
- crackerjack/services/patterns/core.py +212 -0
- crackerjack/services/patterns/documentation/__init__.py +14 -0
- crackerjack/services/patterns/documentation/badges_markdown.py +96 -0
- crackerjack/services/patterns/documentation/comments_blocks.py +83 -0
- crackerjack/services/patterns/documentation/docstrings.py +89 -0
- crackerjack/services/patterns/formatting.py +226 -0
- crackerjack/services/patterns/operations.py +339 -0
- crackerjack/services/patterns/security/__init__.py +23 -0
- crackerjack/services/patterns/security/code_injection.py +122 -0
- crackerjack/services/patterns/security/credentials.py +190 -0
- crackerjack/services/patterns/security/path_traversal.py +221 -0
- crackerjack/services/patterns/security/unsafe_operations.py +216 -0
- crackerjack/services/patterns/templates.py +62 -0
- crackerjack/services/patterns/testing/__init__.py +18 -0
- crackerjack/services/patterns/testing/error_patterns.py +107 -0
- crackerjack/services/patterns/testing/pytest_output.py +126 -0
- crackerjack/services/patterns/tool_output/__init__.py +16 -0
- crackerjack/services/patterns/tool_output/bandit.py +72 -0
- crackerjack/services/patterns/tool_output/other.py +97 -0
- crackerjack/services/patterns/tool_output/pyright.py +67 -0
- crackerjack/services/patterns/tool_output/ruff.py +44 -0
- crackerjack/services/patterns/url_sanitization.py +114 -0
- crackerjack/services/patterns/utilities.py +42 -0
- crackerjack/services/patterns/utils.py +339 -0
- crackerjack/services/patterns/validation.py +46 -0
- crackerjack/services/patterns/versioning.py +62 -0
- crackerjack/services/predictive_analytics.py +523 -0
- crackerjack/services/profiler.py +280 -0
- crackerjack/services/quality/README.md +415 -0
- crackerjack/services/quality/__init__.py +11 -0
- crackerjack/services/quality/anomaly_detector.py +392 -0
- crackerjack/services/quality/pattern_cache.py +333 -0
- crackerjack/services/quality/pattern_detector.py +479 -0
- crackerjack/services/quality/qa_orchestrator.py +491 -0
- crackerjack/services/quality/quality_baseline.py +395 -0
- crackerjack/services/quality/quality_baseline_enhanced.py +649 -0
- crackerjack/services/quality/quality_intelligence.py +949 -0
- crackerjack/services/regex_patterns.py +58 -0
- crackerjack/services/regex_utils.py +483 -0
- crackerjack/services/secure_path_utils.py +524 -0
- crackerjack/services/secure_status_formatter.py +450 -0
- crackerjack/services/secure_subprocess.py +635 -0
- crackerjack/services/security.py +239 -0
- crackerjack/services/security_logger.py +495 -0
- crackerjack/services/server_manager.py +411 -0
- crackerjack/services/smart_scheduling.py +167 -0
- crackerjack/services/status_authentication.py +460 -0
- crackerjack/services/status_security_manager.py +315 -0
- crackerjack/services/terminal_utils.py +0 -0
- crackerjack/services/thread_safe_status_collector.py +441 -0
- crackerjack/services/tool_filter.py +368 -0
- crackerjack/services/tool_version_service.py +43 -0
- crackerjack/services/unified_config.py +115 -0
- crackerjack/services/validation_rate_limiter.py +220 -0
- crackerjack/services/vector_store.py +689 -0
- crackerjack/services/version_analyzer.py +461 -0
- crackerjack/services/version_checker.py +223 -0
- crackerjack/services/websocket_resource_limiter.py +438 -0
- crackerjack/services/zuban_lsp_service.py +391 -0
- crackerjack/slash_commands/README.md +11 -0
- crackerjack/slash_commands/__init__.py +59 -0
- crackerjack/slash_commands/init.md +112 -0
- crackerjack/slash_commands/run.md +197 -0
- crackerjack/slash_commands/status.md +127 -0
- crackerjack/tools/README.md +11 -0
- crackerjack/tools/__init__.py +30 -0
- crackerjack/tools/_git_utils.py +105 -0
- crackerjack/tools/check_added_large_files.py +139 -0
- crackerjack/tools/check_ast.py +105 -0
- crackerjack/tools/check_json.py +103 -0
- crackerjack/tools/check_jsonschema.py +297 -0
- crackerjack/tools/check_toml.py +103 -0
- crackerjack/tools/check_yaml.py +110 -0
- crackerjack/tools/codespell_wrapper.py +72 -0
- crackerjack/tools/end_of_file_fixer.py +202 -0
- crackerjack/tools/format_json.py +128 -0
- crackerjack/tools/mdformat_wrapper.py +114 -0
- crackerjack/tools/trailing_whitespace.py +198 -0
- crackerjack/tools/validate_input_validator_patterns.py +236 -0
- crackerjack/tools/validate_regex_patterns.py +188 -0
- crackerjack/ui/README.md +11 -0
- crackerjack/ui/__init__.py +1 -0
- crackerjack/ui/dashboard_renderer.py +28 -0
- crackerjack/ui/templates/README.md +11 -0
- crackerjack/utils/console_utils.py +13 -0
- crackerjack/utils/dependency_guard.py +230 -0
- crackerjack/utils/retry_utils.py +275 -0
- crackerjack/workflows/README.md +590 -0
- crackerjack/workflows/__init__.py +46 -0
- crackerjack/workflows/actions.py +811 -0
- crackerjack/workflows/auto_fix.py +444 -0
- crackerjack/workflows/container_builder.py +499 -0
- crackerjack/workflows/definitions.py +443 -0
- crackerjack/workflows/engine.py +177 -0
- crackerjack/workflows/event_bridge.py +242 -0
- crackerjack-0.45.2.dist-info/METADATA +1678 -0
- crackerjack-0.45.2.dist-info/RECORD +478 -0
- {crackerjack-0.18.2.dist-info → crackerjack-0.45.2.dist-info}/WHEEL +1 -1
- crackerjack-0.45.2.dist-info/entry_points.txt +2 -0
- crackerjack/.gitignore +0 -14
- crackerjack/.libcst.codemod.yaml +0 -18
- crackerjack/.pdm.toml +0 -1
- crackerjack/.pre-commit-config.yaml +0 -91
- crackerjack/.pytest_cache/.gitignore +0 -2
- crackerjack/.pytest_cache/CACHEDIR.TAG +0 -4
- crackerjack/.pytest_cache/README.md +0 -8
- crackerjack/.pytest_cache/v/cache/nodeids +0 -1
- crackerjack/.pytest_cache/v/cache/stepwise +0 -1
- crackerjack/.ruff_cache/.gitignore +0 -1
- crackerjack/.ruff_cache/0.1.11/3256171999636029978 +0 -0
- crackerjack/.ruff_cache/0.1.14/602324811142551221 +0 -0
- crackerjack/.ruff_cache/0.1.4/10355199064880463147 +0 -0
- crackerjack/.ruff_cache/0.1.6/15140459877605758699 +0 -0
- crackerjack/.ruff_cache/0.1.7/1790508110482614856 +0 -0
- crackerjack/.ruff_cache/0.1.9/17041001205004563469 +0 -0
- crackerjack/.ruff_cache/0.11.2/4070660268492669020 +0 -0
- crackerjack/.ruff_cache/0.11.3/9818742842212983150 +0 -0
- crackerjack/.ruff_cache/0.11.4/9818742842212983150 +0 -0
- crackerjack/.ruff_cache/0.11.6/3557596832929915217 +0 -0
- crackerjack/.ruff_cache/0.11.7/10386934055395314831 +0 -0
- crackerjack/.ruff_cache/0.11.7/3557596832929915217 +0 -0
- crackerjack/.ruff_cache/0.11.8/530407680854991027 +0 -0
- crackerjack/.ruff_cache/0.2.0/10047773857155985907 +0 -0
- crackerjack/.ruff_cache/0.2.1/8522267973936635051 +0 -0
- crackerjack/.ruff_cache/0.2.2/18053836298936336950 +0 -0
- crackerjack/.ruff_cache/0.3.0/12548816621480535786 +0 -0
- crackerjack/.ruff_cache/0.3.3/11081883392474770722 +0 -0
- crackerjack/.ruff_cache/0.3.4/676973378459347183 +0 -0
- crackerjack/.ruff_cache/0.3.5/16311176246009842383 +0 -0
- crackerjack/.ruff_cache/0.5.7/1493622539551733492 +0 -0
- crackerjack/.ruff_cache/0.5.7/6231957614044513175 +0 -0
- crackerjack/.ruff_cache/0.5.7/9932762556785938009 +0 -0
- crackerjack/.ruff_cache/0.6.0/11982804814124138945 +0 -0
- crackerjack/.ruff_cache/0.6.0/12055761203849489982 +0 -0
- crackerjack/.ruff_cache/0.6.2/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.4/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.5/1206147804896221174 +0 -0
- crackerjack/.ruff_cache/0.6.7/3657366982708166874 +0 -0
- crackerjack/.ruff_cache/0.6.9/285614542852677309 +0 -0
- crackerjack/.ruff_cache/0.7.1/1024065805990144819 +0 -0
- crackerjack/.ruff_cache/0.7.1/285614542852677309 +0 -0
- crackerjack/.ruff_cache/0.7.3/16061516852537040135 +0 -0
- crackerjack/.ruff_cache/0.8.4/16354268377385700367 +0 -0
- crackerjack/.ruff_cache/0.9.10/12813592349865671909 +0 -0
- crackerjack/.ruff_cache/0.9.10/923908772239632759 +0 -0
- crackerjack/.ruff_cache/0.9.3/13948373885254993391 +0 -0
- crackerjack/.ruff_cache/0.9.9/12813592349865671909 +0 -0
- crackerjack/.ruff_cache/0.9.9/8843823720003377982 +0 -0
- crackerjack/.ruff_cache/CACHEDIR.TAG +0 -1
- crackerjack/crackerjack.py +0 -855
- crackerjack/pyproject.toml +0 -214
- crackerjack-0.18.2.dist-info/METADATA +0 -420
- crackerjack-0.18.2.dist-info/RECORD +0 -59
- crackerjack-0.18.2.dist-info/entry_points.txt +0 -4
- {crackerjack-0.18.2.dist-info → crackerjack-0.45.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,1031 @@
|
|
|
1
|
+
"""Test template generator for test creation.
|
|
2
|
+
|
|
3
|
+
This module provides comprehensive test template generation for various
|
|
4
|
+
Python code patterns. Uses AgentContext pattern (legacy, intentional).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import ast
|
|
8
|
+
from collections.abc import Callable
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from crackerjack.agents.base import AgentContext
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TestTemplateGenerator:
|
|
16
|
+
"""Test template generator for test creation.
|
|
17
|
+
|
|
18
|
+
Uses AgentContext pattern (legacy, intentional).
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, context: AgentContext) -> None:
|
|
22
|
+
self.context = context
|
|
23
|
+
|
|
24
|
+
async def generate_test_content(
|
|
25
|
+
self,
|
|
26
|
+
module_file: Path,
|
|
27
|
+
functions: list[dict[str, Any]],
|
|
28
|
+
classes: list[dict[str, Any]],
|
|
29
|
+
) -> str:
|
|
30
|
+
"""Generate complete test file content."""
|
|
31
|
+
test_params = self._prepare_test_generation_params(module_file)
|
|
32
|
+
return await self._generate_all_test_types(test_params, functions, classes)
|
|
33
|
+
|
|
34
|
+
async def generate_comprehensive_test_content(
|
|
35
|
+
self,
|
|
36
|
+
test_params: dict[str, Any],
|
|
37
|
+
functions: list[dict[str, Any]],
|
|
38
|
+
classes: list[dict[str, Any]],
|
|
39
|
+
) -> str:
|
|
40
|
+
"""Generate comprehensive test content with all test types."""
|
|
41
|
+
return await self._generate_all_test_types(test_params, functions, classes)
|
|
42
|
+
|
|
43
|
+
def _prepare_test_generation_params(self, module_file: Path) -> dict[str, Any]:
|
|
44
|
+
"""Prepare parameters for test generation."""
|
|
45
|
+
module_name = self._get_module_import_path(module_file)
|
|
46
|
+
module_category = self._categorize_module(
|
|
47
|
+
str(module_file.relative_to(self.context.project_path))
|
|
48
|
+
)
|
|
49
|
+
return {
|
|
50
|
+
"module_name": module_name,
|
|
51
|
+
"module_file": module_file,
|
|
52
|
+
"module_category": module_category,
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async def _generate_all_test_types(
|
|
56
|
+
self,
|
|
57
|
+
test_params: dict[str, Any],
|
|
58
|
+
functions: list[dict[str, Any]],
|
|
59
|
+
classes: list[dict[str, Any]],
|
|
60
|
+
) -> str:
|
|
61
|
+
"""Generate all test types (functions, classes, integration)."""
|
|
62
|
+
base_content = self._generate_enhanced_test_file_header(
|
|
63
|
+
test_params["module_name"],
|
|
64
|
+
test_params["module_file"],
|
|
65
|
+
test_params["module_category"],
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
function_tests = await self._generate_function_tests_content(
|
|
69
|
+
functions, test_params["module_category"]
|
|
70
|
+
)
|
|
71
|
+
class_tests = await self._generate_class_tests_content(
|
|
72
|
+
classes, test_params["module_category"]
|
|
73
|
+
)
|
|
74
|
+
integration_tests = await self._generate_integration_tests_content(
|
|
75
|
+
test_params["module_file"],
|
|
76
|
+
functions,
|
|
77
|
+
classes,
|
|
78
|
+
test_params["module_category"],
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
return base_content + function_tests + class_tests + integration_tests
|
|
82
|
+
|
|
83
|
+
async def _generate_function_tests_content(
|
|
84
|
+
self, functions: list[dict[str, Any]], module_category: str
|
|
85
|
+
) -> str:
|
|
86
|
+
"""Generate test content for all functions."""
|
|
87
|
+
return await self._generate_enhanced_function_tests(functions, module_category)
|
|
88
|
+
|
|
89
|
+
async def _generate_class_tests_content(
|
|
90
|
+
self, classes: list[dict[str, Any]], module_category: str
|
|
91
|
+
) -> str:
|
|
92
|
+
"""Generate test content for all classes."""
|
|
93
|
+
return await self._generate_enhanced_class_tests(classes, module_category)
|
|
94
|
+
|
|
95
|
+
async def _generate_integration_tests_content(
|
|
96
|
+
self,
|
|
97
|
+
module_file: Path,
|
|
98
|
+
functions: list[dict[str, Any]],
|
|
99
|
+
classes: list[dict[str, Any]],
|
|
100
|
+
module_category: str,
|
|
101
|
+
) -> str:
|
|
102
|
+
"""Generate integration test content."""
|
|
103
|
+
return await self._generate_integration_tests(
|
|
104
|
+
module_file, functions, classes, module_category
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
def _generate_enhanced_test_file_header(
|
|
108
|
+
self, module_name: str, module_file: Path, module_category: str
|
|
109
|
+
) -> str:
|
|
110
|
+
"""Generate test file header with imports and class definition."""
|
|
111
|
+
imports = [
|
|
112
|
+
"import pytest",
|
|
113
|
+
"from pathlib import Path",
|
|
114
|
+
"from unittest.mock import Mock, patch, AsyncMock",
|
|
115
|
+
]
|
|
116
|
+
|
|
117
|
+
if module_category in ("service", "manager", "core"):
|
|
118
|
+
imports.append("import asyncio")
|
|
119
|
+
|
|
120
|
+
if module_category == "agent":
|
|
121
|
+
imports.extend(
|
|
122
|
+
[
|
|
123
|
+
"from crackerjack.agents.base import AgentContext, FixResult, "
|
|
124
|
+
"Issue, IssueType",
|
|
125
|
+
]
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
imports_str = "\n".join(imports)
|
|
129
|
+
|
|
130
|
+
try:
|
|
131
|
+
content = self.context.get_file_content(module_file) or ""
|
|
132
|
+
tree = ast.parse(content)
|
|
133
|
+
|
|
134
|
+
importable_items = []
|
|
135
|
+
for node in ast.walk(tree):
|
|
136
|
+
if isinstance(node, ast.ClassDef) and not node.name.startswith("_"):
|
|
137
|
+
importable_items.append(node.name)
|
|
138
|
+
elif isinstance(
|
|
139
|
+
node, ast.FunctionDef | ast.AsyncFunctionDef
|
|
140
|
+
) and not node.name.startswith("_"):
|
|
141
|
+
importable_items.append(node.name)
|
|
142
|
+
|
|
143
|
+
if importable_items:
|
|
144
|
+
specific_imports = (
|
|
145
|
+
f"from {module_name} import {', '.join(importable_items[:10])}"
|
|
146
|
+
)
|
|
147
|
+
else:
|
|
148
|
+
specific_imports = f"import {module_name}"
|
|
149
|
+
|
|
150
|
+
except Exception:
|
|
151
|
+
specific_imports = f"import {module_name}"
|
|
152
|
+
|
|
153
|
+
class_name = f"Test{module_file.stem.replace('_', '').title()}"
|
|
154
|
+
|
|
155
|
+
return (
|
|
156
|
+
f'"""{imports_str}\n'
|
|
157
|
+
f"{specific_imports}\n"
|
|
158
|
+
"\n"
|
|
159
|
+
"\n"
|
|
160
|
+
f"class {class_name}:\n"
|
|
161
|
+
f' """Tests for {module_name}.\n'
|
|
162
|
+
"\n"
|
|
163
|
+
f" This module contains comprehensive tests for {module_name}\n"
|
|
164
|
+
" including:\n"
|
|
165
|
+
" - Basic functionality tests\n"
|
|
166
|
+
" - Edge case validation\n"
|
|
167
|
+
" - Error handling verification\n"
|
|
168
|
+
" - Integration testing\n"
|
|
169
|
+
" - Performance validation (where applicable)\n"
|
|
170
|
+
' """\n'
|
|
171
|
+
"\n"
|
|
172
|
+
" def test_module_imports_successfully(self):\n"
|
|
173
|
+
' """Test that the module can be imported without errors."""\n'
|
|
174
|
+
f" import {module_name}\n"
|
|
175
|
+
f" assert {module_name} is not None\n"
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
def _get_module_import_path(self, file_path: Path) -> str:
|
|
179
|
+
"""Get module import path from file path."""
|
|
180
|
+
try:
|
|
181
|
+
relative_path = file_path.relative_to(self.context.project_path)
|
|
182
|
+
parts = (*relative_path.parts[:-1], relative_path.stem)
|
|
183
|
+
return ".".join(parts)
|
|
184
|
+
except ValueError:
|
|
185
|
+
return file_path.stem
|
|
186
|
+
|
|
187
|
+
async def generate_function_test(self, func_info: dict[str, Any]) -> str:
|
|
188
|
+
"""Generate single function test."""
|
|
189
|
+
func_name = func_info["name"]
|
|
190
|
+
args = func_info.get("args", [])
|
|
191
|
+
|
|
192
|
+
test_template = f"""def test_{func_name}_basic(self):
|
|
193
|
+
\"\"\"Test basic functionality of {func_name}.\"\"\"
|
|
194
|
+
try:
|
|
195
|
+
result = {func_name}({self._generate_default_args(args)})
|
|
196
|
+
assert result is not None or result is None
|
|
197
|
+
except TypeError:
|
|
198
|
+
pytest.skip(
|
|
199
|
+
"Function requires specific arguments - manual implementation needed"
|
|
200
|
+
)
|
|
201
|
+
except Exception as e:
|
|
202
|
+
pytest.fail(f"Unexpected error in {func_name}: {{e}}")"""
|
|
203
|
+
|
|
204
|
+
return test_template
|
|
205
|
+
|
|
206
|
+
async def _generate_enhanced_function_tests(
|
|
207
|
+
self, functions: list[dict[str, Any]], module_category: str
|
|
208
|
+
) -> str:
|
|
209
|
+
"""Generate enhanced function tests with multiple test types."""
|
|
210
|
+
if not functions:
|
|
211
|
+
return ""
|
|
212
|
+
|
|
213
|
+
test_methods = []
|
|
214
|
+
for func in functions:
|
|
215
|
+
func_tests = await self._generate_all_tests_for_function(
|
|
216
|
+
func, module_category
|
|
217
|
+
)
|
|
218
|
+
test_methods.extend(func_tests)
|
|
219
|
+
|
|
220
|
+
return "\n".join(test_methods)
|
|
221
|
+
|
|
222
|
+
async def _generate_all_tests_for_function(
|
|
223
|
+
self, func: dict[str, Any], module_category: str
|
|
224
|
+
) -> list[str]:
|
|
225
|
+
"""Generate all test types for a function."""
|
|
226
|
+
func_tests = []
|
|
227
|
+
|
|
228
|
+
basic_test = await self._generate_basic_function_test(func, module_category)
|
|
229
|
+
func_tests.append(basic_test)
|
|
230
|
+
|
|
231
|
+
additional_tests = await self._generate_conditional_tests_for_function(
|
|
232
|
+
func, module_category
|
|
233
|
+
)
|
|
234
|
+
func_tests.extend(additional_tests)
|
|
235
|
+
|
|
236
|
+
return func_tests
|
|
237
|
+
|
|
238
|
+
async def _generate_conditional_tests_for_function(
|
|
239
|
+
self, func: dict[str, Any], module_category: str
|
|
240
|
+
) -> list[str]:
|
|
241
|
+
"""Generate conditional tests based on function characteristics."""
|
|
242
|
+
tests = []
|
|
243
|
+
args = func.get("args", [])
|
|
244
|
+
func_name = func["name"]
|
|
245
|
+
|
|
246
|
+
if self._should_generate_parametrized_test(args):
|
|
247
|
+
parametrized_test = await self._generate_parametrized_test(
|
|
248
|
+
func, module_category
|
|
249
|
+
)
|
|
250
|
+
tests.append(parametrized_test)
|
|
251
|
+
|
|
252
|
+
error_test = await self._generate_error_handling_test(func, module_category)
|
|
253
|
+
tests.append(error_test)
|
|
254
|
+
|
|
255
|
+
if self._should_generate_edge_case_test(args, func_name):
|
|
256
|
+
edge_test = await self._generate_edge_case_test(func, module_category)
|
|
257
|
+
tests.append(edge_test)
|
|
258
|
+
|
|
259
|
+
return tests
|
|
260
|
+
|
|
261
|
+
def _should_generate_parametrized_test(self, args: list[str]) -> bool:
|
|
262
|
+
"""Check if parametrized test should be generated."""
|
|
263
|
+
return len(args) > 1
|
|
264
|
+
|
|
265
|
+
def _should_generate_edge_case_test(self, args: list[str], func_name: str) -> bool:
|
|
266
|
+
"""Check if edge case test should be generated."""
|
|
267
|
+
has_multiple_args = len(args) > 2
|
|
268
|
+
is_complex_function = any(
|
|
269
|
+
hint in func_name.lower()
|
|
270
|
+
for hint in ("process", "validate", "parse", "convert")
|
|
271
|
+
)
|
|
272
|
+
return has_multiple_args or is_complex_function
|
|
273
|
+
|
|
274
|
+
async def _generate_basic_function_test(
|
|
275
|
+
self, func: dict[str, Any], module_category: str
|
|
276
|
+
) -> str:
|
|
277
|
+
"""Generate basic function test."""
|
|
278
|
+
func_name = func["name"]
|
|
279
|
+
args = func.get("args", [])
|
|
280
|
+
|
|
281
|
+
template_generator = self._get_test_template_generator(module_category)
|
|
282
|
+
return template_generator(func_name, args)
|
|
283
|
+
|
|
284
|
+
def _get_test_template_generator(
|
|
285
|
+
self, module_category: str
|
|
286
|
+
) -> Callable[[str, list[str]], str]:
|
|
287
|
+
"""Get test template generator based on module category."""
|
|
288
|
+
return {
|
|
289
|
+
"agent": self._generate_agent_test_template,
|
|
290
|
+
"service": self._generate_async_test_template,
|
|
291
|
+
"manager": self._generate_async_test_template,
|
|
292
|
+
}.get(module_category, self._generate_default_test_template)
|
|
293
|
+
|
|
294
|
+
def _generate_agent_test_template(self, func_name: str, args: list[str]) -> str:
|
|
295
|
+
"""Generate test template for agent functions."""
|
|
296
|
+
template = (
|
|
297
|
+
" def test_FUNC_NAME_basic_functionality(self):\n"
|
|
298
|
+
' """Test basic functionality of FUNC_NAME."""\n'
|
|
299
|
+
"\n"
|
|
300
|
+
"\n"
|
|
301
|
+
" try:\n"
|
|
302
|
+
" result = FUNC_NAME(ARGS)\n"
|
|
303
|
+
" assert result is not None or result is None\n"
|
|
304
|
+
" except (TypeError, NotImplementedError) as e:\n"
|
|
305
|
+
+ (
|
|
306
|
+
" pytest.skip('Function FUNC_NAME requires manual "
|
|
307
|
+
"implementation: ' + str(e))\n"
|
|
308
|
+
)
|
|
309
|
+
+ " except Exception as e:\n"
|
|
310
|
+
" pytest.fail('Unexpected error in FUNC_NAME: ' + str(e))"
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
return template.replace("FUNC_NAME", func_name).replace(
|
|
314
|
+
"ARGS", self._generate_smart_default_args(args)
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
def _generate_async_test_template(self, func_name: str, args: list[str]) -> str:
|
|
318
|
+
"""Generate test template for async functions."""
|
|
319
|
+
template = (
|
|
320
|
+
" @pytest.mark.asyncio\n"
|
|
321
|
+
" async def test_FUNC_NAME_basic_functionality(self):\n"
|
|
322
|
+
' """Test basic functionality of FUNC_NAME."""\n'
|
|
323
|
+
"\n"
|
|
324
|
+
"\n"
|
|
325
|
+
" try:\n"
|
|
326
|
+
" if asyncio.iscoroutinefunction(FUNC_NAME):\n"
|
|
327
|
+
" result = await FUNC_NAME(ARGS)\n"
|
|
328
|
+
" else:\n"
|
|
329
|
+
" result = FUNC_NAME(ARGS)\n"
|
|
330
|
+
" assert result is not None or result is None\n"
|
|
331
|
+
" except (TypeError, NotImplementedError) as e:\n"
|
|
332
|
+
+ (
|
|
333
|
+
" pytest.skip('Function FUNC_NAME requires manual "
|
|
334
|
+
"implementation: ' + str(e))\n"
|
|
335
|
+
)
|
|
336
|
+
+ " except Exception as e:\n"
|
|
337
|
+
" pytest.fail('Unexpected error in FUNC_NAME: ' + str(e))"
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
return template.replace("FUNC_NAME", func_name).replace(
|
|
341
|
+
"ARGS", self._generate_smart_default_args(args)
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
def _generate_default_test_template(self, func_name: str, args: list[str]) -> str:
|
|
345
|
+
"""Generate default test template."""
|
|
346
|
+
template = (
|
|
347
|
+
" def test_FUNC_NAME_basic_functionality(self):\n"
|
|
348
|
+
' """Test basic functionality of FUNC_NAME."""\n'
|
|
349
|
+
" try:\n"
|
|
350
|
+
" result = FUNC_NAME(ARGS)\n"
|
|
351
|
+
" assert result is not None or result is None\n"
|
|
352
|
+
" except (TypeError, NotImplementedError) as e:\n"
|
|
353
|
+
+ (
|
|
354
|
+
" pytest.skip('Function FUNC_NAME requires manual "
|
|
355
|
+
"implementation: ' + str(e))\n"
|
|
356
|
+
)
|
|
357
|
+
+ " except Exception as e:\n"
|
|
358
|
+
" pytest.fail('Unexpected error in FUNC_NAME: ' + str(e))"
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
return template.replace("FUNC_NAME", func_name).replace(
|
|
362
|
+
"ARGS", self._generate_smart_default_args(args)
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
async def _generate_parametrized_test(
|
|
366
|
+
self, func: dict[str, Any], module_category: str
|
|
367
|
+
) -> str:
|
|
368
|
+
"""Generate parametrized test."""
|
|
369
|
+
func_name = func["name"]
|
|
370
|
+
args = func.get("args", [])
|
|
371
|
+
|
|
372
|
+
test_cases = self._generate_test_parameters(args)
|
|
373
|
+
|
|
374
|
+
if not test_cases:
|
|
375
|
+
return ""
|
|
376
|
+
|
|
377
|
+
parametrize_decorator = f"@pytest.mark.parametrize({test_cases})"
|
|
378
|
+
|
|
379
|
+
test_template = (
|
|
380
|
+
f" {parametrize_decorator}\n"
|
|
381
|
+
f" def test_{func_name}_with_parameters(self, "
|
|
382
|
+
f"{', '.join(args) if len(args) <= 5 else 'test_input'}):\n"
|
|
383
|
+
f' """Test {func_name} with various parameter combinations."""\n'
|
|
384
|
+
" try:\n"
|
|
385
|
+
f" if len({args}) <= 5:\n"
|
|
386
|
+
f" result = {func_name}({', '.join(args)})\n"
|
|
387
|
+
" else:\n"
|
|
388
|
+
f" result = {func_name}(**test_input)\n"
|
|
389
|
+
"\n"
|
|
390
|
+
" assert result is not None or result is None\n"
|
|
391
|
+
" except (TypeError, ValueError) as expected_error:\n"
|
|
392
|
+
"\n"
|
|
393
|
+
" pass\n"
|
|
394
|
+
" except Exception as e:\n"
|
|
395
|
+
' pytest.fail(f"Unexpected error with parameters: {e}")'
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
return test_template
|
|
399
|
+
|
|
400
|
+
async def _generate_error_handling_test(
|
|
401
|
+
self, func: dict[str, Any], module_category: str
|
|
402
|
+
) -> str:
|
|
403
|
+
"""Generate error handling test."""
|
|
404
|
+
func_name = func["name"]
|
|
405
|
+
args = func.get("args", [])
|
|
406
|
+
|
|
407
|
+
test_template = (
|
|
408
|
+
f" def test_{func_name}_error_handling(self):\n"
|
|
409
|
+
f' """Test {func_name} error handling with invalid inputs."""\n'
|
|
410
|
+
"\n"
|
|
411
|
+
" with pytest.raises((TypeError, ValueError, AttributeError)):\n"
|
|
412
|
+
f" {func_name}({self._generate_invalid_args(args)})\n"
|
|
413
|
+
"\n"
|
|
414
|
+
"\n"
|
|
415
|
+
f" if len({args}) > 0:\n"
|
|
416
|
+
" with pytest.raises((TypeError, ValueError)):\n"
|
|
417
|
+
f" {func_name}("
|
|
418
|
+
f"{self._generate_edge_case_args(args, 'empty')})"
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
return test_template
|
|
422
|
+
|
|
423
|
+
async def _generate_edge_case_test(
|
|
424
|
+
self, func: dict[str, Any], module_category: str
|
|
425
|
+
) -> str:
|
|
426
|
+
"""Generate edge case test."""
|
|
427
|
+
func_name = func["name"]
|
|
428
|
+
args = func.get("args", [])
|
|
429
|
+
|
|
430
|
+
test_template = (
|
|
431
|
+
f" def test_{func_name}_edge_cases(self):\n"
|
|
432
|
+
f' """Test {func_name} with edge case scenarios."""\n'
|
|
433
|
+
"\n"
|
|
434
|
+
" edge_cases = [\n"
|
|
435
|
+
f" {self._generate_edge_case_args(args, 'boundary')},\n"
|
|
436
|
+
f" {self._generate_edge_case_args(args, 'extreme')},\n"
|
|
437
|
+
" ]\n"
|
|
438
|
+
"\n"
|
|
439
|
+
" for edge_case in edge_cases:\n"
|
|
440
|
+
" try:\n"
|
|
441
|
+
f" result = {func_name}(*edge_case)\n"
|
|
442
|
+
"\n"
|
|
443
|
+
" assert result is not None or result is None\n"
|
|
444
|
+
" except (ValueError, TypeError):\n"
|
|
445
|
+
"\n"
|
|
446
|
+
" pass\n"
|
|
447
|
+
" except Exception as e:\n"
|
|
448
|
+
' pytest.fail(f"Unexpected error with edge case {edge_case}: '
|
|
449
|
+
'{e}")'
|
|
450
|
+
)
|
|
451
|
+
|
|
452
|
+
return test_template
|
|
453
|
+
|
|
454
|
+
def _generate_test_parameters(self, args: list[str]) -> str:
|
|
455
|
+
"""Generate test parameters for parametrized tests."""
|
|
456
|
+
if not args or len(args) > 5:
|
|
457
|
+
return ""
|
|
458
|
+
|
|
459
|
+
param_names = ", ".join(f'"{arg}"' for arg in args)
|
|
460
|
+
param_values = []
|
|
461
|
+
|
|
462
|
+
for i in range(min(3, len(args))):
|
|
463
|
+
test_case = []
|
|
464
|
+
for arg in args:
|
|
465
|
+
if "path" in arg.lower():
|
|
466
|
+
test_case.append(f'Path("test_{i}")')
|
|
467
|
+
elif "str" in arg.lower() or "name" in arg.lower():
|
|
468
|
+
test_case.append(f'"test_{i}"')
|
|
469
|
+
elif "int" in arg.lower() or "count" in arg.lower():
|
|
470
|
+
test_case.append(str(i))
|
|
471
|
+
elif "bool" in arg.lower():
|
|
472
|
+
test_case.append("True" if i % 2 == 0 else "False")
|
|
473
|
+
else:
|
|
474
|
+
test_case.append("None")
|
|
475
|
+
param_values.append(f"({', '.join(test_case)})")
|
|
476
|
+
|
|
477
|
+
return f"[{param_names}], [{', '.join(param_values)}]"
|
|
478
|
+
|
|
479
|
+
def _generate_smart_default_args(self, args: list[str]) -> str:
|
|
480
|
+
"""Generate smart default arguments based on arg names."""
|
|
481
|
+
if not args or args == ["self"]:
|
|
482
|
+
return ""
|
|
483
|
+
|
|
484
|
+
filtered_args = self._filter_args(args)
|
|
485
|
+
if not filtered_args:
|
|
486
|
+
return ""
|
|
487
|
+
|
|
488
|
+
placeholders = [
|
|
489
|
+
self._generate_placeholder_for_arg(arg) for arg in filtered_args
|
|
490
|
+
]
|
|
491
|
+
return ", ".join(placeholders)
|
|
492
|
+
|
|
493
|
+
def _filter_args(self, args: list[str]) -> list[str]:
|
|
494
|
+
"""Filter out 'self' from arguments."""
|
|
495
|
+
return [arg for arg in args if arg != "self"]
|
|
496
|
+
|
|
497
|
+
def _generate_placeholder_for_arg(self, arg: str) -> str:
|
|
498
|
+
"""Generate placeholder value for argument."""
|
|
499
|
+
arg_lower = arg.lower()
|
|
500
|
+
|
|
501
|
+
if self._is_path_arg(arg_lower):
|
|
502
|
+
return 'Path("test_file.txt")'
|
|
503
|
+
elif self._is_url_arg(arg_lower):
|
|
504
|
+
return '"https: //example.com"'
|
|
505
|
+
elif self._is_email_arg(arg_lower):
|
|
506
|
+
return '"test@example.com"'
|
|
507
|
+
elif self._is_id_arg(arg_lower):
|
|
508
|
+
return '"test-id-123"'
|
|
509
|
+
elif self._is_name_arg(arg_lower):
|
|
510
|
+
return '"test_name"'
|
|
511
|
+
elif self._is_numeric_arg(arg_lower):
|
|
512
|
+
return "10"
|
|
513
|
+
elif self._is_boolean_arg(arg_lower):
|
|
514
|
+
return "True"
|
|
515
|
+
elif self._is_text_arg(arg_lower):
|
|
516
|
+
return '"test data"'
|
|
517
|
+
elif self._is_list_arg(arg_lower):
|
|
518
|
+
return '["test1", "test2"]'
|
|
519
|
+
elif self._is_dict_arg(arg_lower):
|
|
520
|
+
return '{"key": "value"}'
|
|
521
|
+
return '"test"'
|
|
522
|
+
|
|
523
|
+
def _is_path_arg(self, arg_lower: str) -> bool:
|
|
524
|
+
"""Check if arg is path-like."""
|
|
525
|
+
return any(term in arg_lower for term in ("path", "file"))
|
|
526
|
+
|
|
527
|
+
def _is_url_arg(self, arg_lower: str) -> bool:
|
|
528
|
+
"""Check if arg is URL-like."""
|
|
529
|
+
return any(term in arg_lower for term in ("url", "uri"))
|
|
530
|
+
|
|
531
|
+
def _is_email_arg(self, arg_lower: str) -> bool:
|
|
532
|
+
"""Check if arg is email-like."""
|
|
533
|
+
return any(term in arg_lower for term in ("email", "mail"))
|
|
534
|
+
|
|
535
|
+
def _is_id_arg(self, arg_lower: str) -> bool:
|
|
536
|
+
"""Check if arg is ID-like."""
|
|
537
|
+
return any(term in arg_lower for term in ("id", "uuid"))
|
|
538
|
+
|
|
539
|
+
def _is_name_arg(self, arg_lower: str) -> bool:
|
|
540
|
+
"""Check if arg is name-like."""
|
|
541
|
+
return any(term in arg_lower for term in ("name", "title"))
|
|
542
|
+
|
|
543
|
+
def _is_numeric_arg(self, arg_lower: str) -> bool:
|
|
544
|
+
"""Check if arg is numeric."""
|
|
545
|
+
return any(term in arg_lower for term in ("count", "size", "number", "num"))
|
|
546
|
+
|
|
547
|
+
def _is_boolean_arg(self, arg_lower: str) -> bool:
|
|
548
|
+
"""Check if arg is boolean."""
|
|
549
|
+
return any(term in arg_lower for term in ("enable", "flag", "is_", "has_"))
|
|
550
|
+
|
|
551
|
+
def _is_text_arg(self, arg_lower: str) -> bool:
|
|
552
|
+
"""Check if arg is text-like."""
|
|
553
|
+
return any(term in arg_lower for term in ("data", "content", "text"))
|
|
554
|
+
|
|
555
|
+
def _is_list_arg(self, arg_lower: str) -> bool:
|
|
556
|
+
"""Check if arg is list-like."""
|
|
557
|
+
return any(term in arg_lower for term in ("list[t.Any]", "items"))
|
|
558
|
+
|
|
559
|
+
def _is_dict_arg(self, arg_lower: str) -> bool:
|
|
560
|
+
"""Check if arg is dict-like."""
|
|
561
|
+
return any(
|
|
562
|
+
term in arg_lower for term in ("dict[str, t.Any]", "config", "options")
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
def _generate_invalid_args(self, args: list[str]) -> str:
|
|
566
|
+
"""Generate invalid arguments for error testing."""
|
|
567
|
+
filtered_args = [arg for arg in args if arg != "self"]
|
|
568
|
+
if not filtered_args:
|
|
569
|
+
return ""
|
|
570
|
+
return ", ".join(["None"] * len(filtered_args))
|
|
571
|
+
|
|
572
|
+
def _generate_edge_case_args(self, args: list[str], case_type: str) -> str:
|
|
573
|
+
"""Generate edge case arguments."""
|
|
574
|
+
filtered_args = self._filter_args(args)
|
|
575
|
+
if not filtered_args:
|
|
576
|
+
return ""
|
|
577
|
+
|
|
578
|
+
placeholders = self._generate_placeholders_by_case_type(
|
|
579
|
+
filtered_args, case_type
|
|
580
|
+
)
|
|
581
|
+
return ", ".join(placeholders)
|
|
582
|
+
|
|
583
|
+
def _generate_placeholders_by_case_type(
|
|
584
|
+
self, filtered_args: list[str], case_type: str
|
|
585
|
+
) -> list[str]:
|
|
586
|
+
"""Generate placeholders based on case type."""
|
|
587
|
+
if case_type == "empty":
|
|
588
|
+
return self._generate_empty_case_placeholders(filtered_args)
|
|
589
|
+
elif case_type == "boundary":
|
|
590
|
+
return self._generate_boundary_case_placeholders(filtered_args)
|
|
591
|
+
|
|
592
|
+
return self._generate_extreme_case_placeholders(filtered_args)
|
|
593
|
+
|
|
594
|
+
def _generate_empty_case_placeholders(self, filtered_args: list[str]) -> list[str]:
|
|
595
|
+
"""Generate empty case placeholders."""
|
|
596
|
+
placeholders = []
|
|
597
|
+
for arg in filtered_args:
|
|
598
|
+
arg_lower = arg.lower()
|
|
599
|
+
if any(term in arg_lower for term in ("str", "name", "text")):
|
|
600
|
+
placeholders.append('""')
|
|
601
|
+
elif any(term in arg_lower for term in ("list[t.Any]", "items")):
|
|
602
|
+
placeholders.append("[]")
|
|
603
|
+
elif any(term in arg_lower for term in ("dict[str, t.Any]", "config")):
|
|
604
|
+
placeholders.append("{}")
|
|
605
|
+
else:
|
|
606
|
+
placeholders.append("None")
|
|
607
|
+
return placeholders
|
|
608
|
+
|
|
609
|
+
def _generate_boundary_case_placeholders(
|
|
610
|
+
self, filtered_args: list[str]
|
|
611
|
+
) -> list[str]:
|
|
612
|
+
"""Generate boundary case placeholders."""
|
|
613
|
+
placeholders = []
|
|
614
|
+
for arg in filtered_args:
|
|
615
|
+
arg_lower = arg.lower()
|
|
616
|
+
if any(term in arg_lower for term in ("count", "size", "number")):
|
|
617
|
+
placeholders.append("0")
|
|
618
|
+
elif any(term in arg_lower for term in ("str", "name")):
|
|
619
|
+
placeholders.append('"x" * 1000')
|
|
620
|
+
else:
|
|
621
|
+
placeholders.append("None")
|
|
622
|
+
return placeholders
|
|
623
|
+
|
|
624
|
+
def _generate_extreme_case_placeholders(
|
|
625
|
+
self, filtered_args: list[str]
|
|
626
|
+
) -> list[str]:
|
|
627
|
+
"""Generate extreme case placeholders."""
|
|
628
|
+
placeholders = []
|
|
629
|
+
for arg in filtered_args:
|
|
630
|
+
arg_lower = arg.lower()
|
|
631
|
+
if any(term in arg_lower for term in ("count", "size", "number")):
|
|
632
|
+
placeholders.append("-1")
|
|
633
|
+
else:
|
|
634
|
+
placeholders.append("None")
|
|
635
|
+
return placeholders
|
|
636
|
+
|
|
637
|
+
async def _generate_enhanced_class_tests(
|
|
638
|
+
self, classes: list[dict[str, Any]], module_category: str
|
|
639
|
+
) -> str:
|
|
640
|
+
"""Generate enhanced class tests."""
|
|
641
|
+
if not classes:
|
|
642
|
+
return ""
|
|
643
|
+
|
|
644
|
+
test_components = await self._generate_all_class_test_components(
|
|
645
|
+
classes, module_category
|
|
646
|
+
)
|
|
647
|
+
return self._combine_class_test_elements(
|
|
648
|
+
test_components["fixtures"], test_components["test_methods"]
|
|
649
|
+
)
|
|
650
|
+
|
|
651
|
+
async def _generate_all_class_test_components(
|
|
652
|
+
self, classes: list[dict[str, Any]], module_category: str
|
|
653
|
+
) -> dict[str, list[str]]:
|
|
654
|
+
"""Generate all class test components."""
|
|
655
|
+
fixtures = []
|
|
656
|
+
test_methods = []
|
|
657
|
+
|
|
658
|
+
for cls in classes:
|
|
659
|
+
class_components = await self._generate_single_class_test_components(
|
|
660
|
+
cls, module_category
|
|
661
|
+
)
|
|
662
|
+
fixtures.extend(class_components["fixtures"])
|
|
663
|
+
test_methods.extend(class_components["test_methods"])
|
|
664
|
+
|
|
665
|
+
return {"fixtures": fixtures, "test_methods": test_methods}
|
|
666
|
+
|
|
667
|
+
async def _generate_single_class_test_components(
|
|
668
|
+
self, cls: dict[str, Any], module_category: str
|
|
669
|
+
) -> dict[str, list[str]]:
|
|
670
|
+
"""Generate test components for single class."""
|
|
671
|
+
fixtures = []
|
|
672
|
+
test_methods = []
|
|
673
|
+
methods = cls.get("methods", [])
|
|
674
|
+
|
|
675
|
+
fixture = await self._generate_class_fixture(cls, module_category)
|
|
676
|
+
if fixture:
|
|
677
|
+
fixtures.append(fixture)
|
|
678
|
+
|
|
679
|
+
core_tests = await self._generate_core_class_tests(
|
|
680
|
+
cls, methods, module_category
|
|
681
|
+
)
|
|
682
|
+
test_methods.extend(core_tests)
|
|
683
|
+
|
|
684
|
+
return {"fixtures": fixtures, "test_methods": test_methods}
|
|
685
|
+
|
|
686
|
+
async def _generate_core_class_tests(
|
|
687
|
+
self, cls: dict[str, Any], methods: list[str], module_category: str
|
|
688
|
+
) -> list[str]:
|
|
689
|
+
"""Generate core class tests."""
|
|
690
|
+
test_methods = []
|
|
691
|
+
|
|
692
|
+
instantiation_test = await self._generate_class_instantiation_test(
|
|
693
|
+
cls, module_category
|
|
694
|
+
)
|
|
695
|
+
test_methods.append(instantiation_test)
|
|
696
|
+
|
|
697
|
+
method_tests = await self._generate_method_tests(
|
|
698
|
+
cls, methods[:5], module_category
|
|
699
|
+
)
|
|
700
|
+
test_methods.extend(method_tests)
|
|
701
|
+
|
|
702
|
+
property_test = await self._generate_class_property_test(cls, module_category)
|
|
703
|
+
if property_test:
|
|
704
|
+
test_methods.append(property_test)
|
|
705
|
+
|
|
706
|
+
return test_methods
|
|
707
|
+
|
|
708
|
+
async def _generate_method_tests(
|
|
709
|
+
self, cls: dict[str, Any], methods: list[str], module_category: str
|
|
710
|
+
) -> list[str]:
|
|
711
|
+
"""Generate tests for class methods."""
|
|
712
|
+
method_tests = []
|
|
713
|
+
for method in methods:
|
|
714
|
+
method_test = await self._generate_class_method_test(
|
|
715
|
+
cls, method, module_category
|
|
716
|
+
)
|
|
717
|
+
method_tests.append(method_test)
|
|
718
|
+
return method_tests
|
|
719
|
+
|
|
720
|
+
def _combine_class_test_elements(
|
|
721
|
+
self, fixtures: list[str], test_methods: list[str]
|
|
722
|
+
) -> str:
|
|
723
|
+
"""Combine class test fixtures and methods."""
|
|
724
|
+
fixture_section = "\n".join(fixtures) if fixtures else ""
|
|
725
|
+
test_section = "\n".join(test_methods)
|
|
726
|
+
return fixture_section + test_section
|
|
727
|
+
|
|
728
|
+
async def _generate_class_fixture(
|
|
729
|
+
self, cls: dict[str, Any], module_category: str
|
|
730
|
+
) -> str:
|
|
731
|
+
"""Generate class fixture."""
|
|
732
|
+
class_name = cls["name"]
|
|
733
|
+
|
|
734
|
+
if module_category in ("service", "manager", "core"):
|
|
735
|
+
fixture_template = (
|
|
736
|
+
" @pytest.fixture\n"
|
|
737
|
+
f" def {class_name.lower()}_instance(self):\n"
|
|
738
|
+
f' """Fixture to create {class_name} instance for testing."""\n'
|
|
739
|
+
"\n"
|
|
740
|
+
" try:\n"
|
|
741
|
+
f" return {class_name}()\n"
|
|
742
|
+
" except TypeError:\n"
|
|
743
|
+
"\n"
|
|
744
|
+
f" with patch.object({class_name}, '__init__', return_value=None):\n"
|
|
745
|
+
f" instance = {class_name}.__new__({class_name})\n"
|
|
746
|
+
" return instance"
|
|
747
|
+
)
|
|
748
|
+
|
|
749
|
+
elif module_category == "agent":
|
|
750
|
+
fixture_template = (
|
|
751
|
+
" @pytest.fixture\n"
|
|
752
|
+
f" def {class_name.lower()}_instance(self):\n"
|
|
753
|
+
f' """Fixture to create {class_name} instance for testing."""\n'
|
|
754
|
+
"\n"
|
|
755
|
+
" mock_context = Mock(spec=AgentContext)\n"
|
|
756
|
+
' mock_context.project_path = Path("/test/project")\n'
|
|
757
|
+
' mock_context.get_file_content = Mock(return_value="# test content")\n'
|
|
758
|
+
" mock_context.write_file_content = Mock(return_value=True)\n"
|
|
759
|
+
"\n"
|
|
760
|
+
" try:\n"
|
|
761
|
+
f" return {class_name}(mock_context)\n"
|
|
762
|
+
" except Exception:\n"
|
|
763
|
+
' pytest.skip("Agent requires specific context configuration")'
|
|
764
|
+
)
|
|
765
|
+
|
|
766
|
+
else:
|
|
767
|
+
fixture_template = (
|
|
768
|
+
" @pytest.fixture\n"
|
|
769
|
+
f" def {class_name.lower()}_instance(self):\n"
|
|
770
|
+
f' """Fixture to create {class_name} instance for testing."""\n'
|
|
771
|
+
" try:\n"
|
|
772
|
+
f" return {class_name}()\n"
|
|
773
|
+
" except TypeError:\n"
|
|
774
|
+
' pytest.skip("Class requires specific constructor arguments")'
|
|
775
|
+
)
|
|
776
|
+
|
|
777
|
+
return fixture_template
|
|
778
|
+
|
|
779
|
+
@staticmethod
|
|
780
|
+
async def _generate_class_instantiation_test(
|
|
781
|
+
class_info: dict[str, Any], module_category: str
|
|
782
|
+
) -> str:
|
|
783
|
+
"""Generate class instantiation test."""
|
|
784
|
+
class_name = class_info["name"]
|
|
785
|
+
|
|
786
|
+
test_template = (
|
|
787
|
+
f" def test_{class_name.lower()}_instantiation(self, {class_name.lower()}_instance):\n"
|
|
788
|
+
f' """Test successful instantiation of {class_name}."""\n'
|
|
789
|
+
f" assert {class_name.lower()}_instance is not None\n"
|
|
790
|
+
f" assert isinstance({class_name.lower()}_instance, {class_name})\n"
|
|
791
|
+
"\n"
|
|
792
|
+
f" assert hasattr({class_name.lower()}_instance, '__class__')\n"
|
|
793
|
+
f' assert {class_name.lower()}_instance.__class__.__name__ == "{class_name}"'
|
|
794
|
+
)
|
|
795
|
+
|
|
796
|
+
return test_template
|
|
797
|
+
|
|
798
|
+
async def _generate_class_method_test(
|
|
799
|
+
self, cls: dict[str, Any], method_name: str, module_category: str
|
|
800
|
+
) -> str:
|
|
801
|
+
"""Generate test for class method."""
|
|
802
|
+
class_name = cls["name"]
|
|
803
|
+
|
|
804
|
+
if self._is_special_agent_method(module_category, method_name):
|
|
805
|
+
return self._generate_agent_method_test(class_name, method_name)
|
|
806
|
+
if module_category in ("service", "manager"):
|
|
807
|
+
return self._generate_async_method_test(class_name, method_name)
|
|
808
|
+
return self._generate_default_method_test(class_name, method_name)
|
|
809
|
+
|
|
810
|
+
def _is_special_agent_method(self, module_category: str, method_name: str) -> bool:
|
|
811
|
+
"""Check if method is special agent method."""
|
|
812
|
+
return module_category == "agent" and method_name in (
|
|
813
|
+
"can_handle",
|
|
814
|
+
"analyze_and_fix",
|
|
815
|
+
)
|
|
816
|
+
|
|
817
|
+
def _generate_agent_method_test(self, class_name: str, method_name: str) -> str:
|
|
818
|
+
"""Generate test for agent method."""
|
|
819
|
+
if method_name == "can_handle":
|
|
820
|
+
return self._generate_can_handle_test(class_name)
|
|
821
|
+
elif method_name == "analyze_and_fix":
|
|
822
|
+
return self._generate_analyze_and_fix_test(class_name)
|
|
823
|
+
return self._generate_generic_agent_method_test(class_name, method_name)
|
|
824
|
+
|
|
825
|
+
def _generate_can_handle_test(self, class_name: str) -> str:
|
|
826
|
+
"""Generate can_handle test for agent."""
|
|
827
|
+
return (
|
|
828
|
+
" @pytest.mark.asyncio\n"
|
|
829
|
+
f" async def test_{class_name.lower()}_can_handle(self, {class_name.lower()}_instance):\n"
|
|
830
|
+
f' """Test {class_name}.can_handle method."""\n'
|
|
831
|
+
"\n"
|
|
832
|
+
" mock_issue = Mock(spec=Issue)\n"
|
|
833
|
+
" mock_issue.type = IssueType.COVERAGE_IMPROVEMENT\n"
|
|
834
|
+
' mock_issue.message = "test coverage issue"\n'
|
|
835
|
+
' mock_issue.file_path = "/test/path.py"\n'
|
|
836
|
+
"\n"
|
|
837
|
+
f" result = await {class_name.lower()}_instance.can_handle(mock_issue)\n"
|
|
838
|
+
" assert isinstance(result, (int, float))\n"
|
|
839
|
+
" assert 0.0 <= result <= 1.0"
|
|
840
|
+
)
|
|
841
|
+
|
|
842
|
+
def _generate_analyze_and_fix_test(self, class_name: str) -> str:
|
|
843
|
+
"""Generate analyze_and_fix test for agent."""
|
|
844
|
+
return (
|
|
845
|
+
" @pytest.mark.asyncio\n"
|
|
846
|
+
f" async def test_{class_name.lower()}_analyze_and_fix(self, {class_name.lower()}_instance):\n"
|
|
847
|
+
f' """Test {class_name}.analyze_and_fix method."""\n'
|
|
848
|
+
"\n"
|
|
849
|
+
" mock_issue = Mock(spec=Issue)\n"
|
|
850
|
+
" mock_issue.type = IssueType.COVERAGE_IMPROVEMENT\n"
|
|
851
|
+
' mock_issue.message = "test coverage issue"\n'
|
|
852
|
+
' mock_issue.file_path = "/test/path.py"\n'
|
|
853
|
+
"\n"
|
|
854
|
+
f" result = await {class_name.lower()}_instance.analyze_and_fix(mock_issue)\n"
|
|
855
|
+
" assert isinstance(result, FixResult)\n"
|
|
856
|
+
" assert hasattr(result, 'success')\n"
|
|
857
|
+
" assert hasattr(result, 'confidence')"
|
|
858
|
+
)
|
|
859
|
+
|
|
860
|
+
def _generate_generic_agent_method_test(
|
|
861
|
+
self, class_name: str, method_name: str
|
|
862
|
+
) -> str:
|
|
863
|
+
"""Generate generic agent method test."""
|
|
864
|
+
return (
|
|
865
|
+
" @pytest.mark.asyncio\n"
|
|
866
|
+
f" async def test_{class_name.lower()}_{method_name}(self, {class_name.lower()}_instance):\n"
|
|
867
|
+
f' """Test {class_name}.{method_name} method."""\n'
|
|
868
|
+
" try:\n"
|
|
869
|
+
f" method = getattr({class_name.lower()}_instance, "
|
|
870
|
+
f'"{method_name}", None)\n'
|
|
871
|
+
f" assert method is not None, "
|
|
872
|
+
f'f"Method {method_name} should exist"\n'
|
|
873
|
+
"\n"
|
|
874
|
+
" if asyncio.iscoroutinefunction(method):\n"
|
|
875
|
+
" result = await method()\n"
|
|
876
|
+
" else:\n"
|
|
877
|
+
" result = method()\n"
|
|
878
|
+
"\n"
|
|
879
|
+
" assert result is not None or result is None\n"
|
|
880
|
+
" except (TypeError, NotImplementedError):\n"
|
|
881
|
+
f' pytest.skip(f"Method {method_name} requires specific arguments")\n'
|
|
882
|
+
" except Exception as e:\n"
|
|
883
|
+
f' pytest.fail(f"Unexpected error in {method_name}: {{e}}")'
|
|
884
|
+
)
|
|
885
|
+
|
|
886
|
+
def _generate_async_method_test(self, class_name: str, method_name: str) -> str:
|
|
887
|
+
"""Generate async method test."""
|
|
888
|
+
return (
|
|
889
|
+
" @pytest.mark.asyncio\n"
|
|
890
|
+
f" async def test_{class_name.lower()}_{method_name}(self, {class_name.lower()}_instance):\n"
|
|
891
|
+
f' """Test {class_name}.{method_name} method."""\n'
|
|
892
|
+
" try:\n"
|
|
893
|
+
f" method = getattr({class_name.lower()}_instance, "
|
|
894
|
+
f'"{method_name}", None)\n'
|
|
895
|
+
f" assert method is not None, "
|
|
896
|
+
f'f"Method {method_name} should exist"\n'
|
|
897
|
+
"\n"
|
|
898
|
+
" if asyncio.iscoroutinefunction(method):\n"
|
|
899
|
+
" result = await method()\n"
|
|
900
|
+
" else:\n"
|
|
901
|
+
" result = method()\n"
|
|
902
|
+
"\n"
|
|
903
|
+
" assert result is not None or result is None\n"
|
|
904
|
+
"\n"
|
|
905
|
+
" except (TypeError, NotImplementedError):\n"
|
|
906
|
+
f' pytest.skip(f"Method {method_name} requires specific arguments or implementation")\n'
|
|
907
|
+
" except Exception as e:\n"
|
|
908
|
+
f' pytest.fail(f"Unexpected error in {method_name}: {{e}}")'
|
|
909
|
+
)
|
|
910
|
+
|
|
911
|
+
def _generate_default_method_test(self, class_name: str, method_name: str) -> str:
|
|
912
|
+
"""Generate default method test."""
|
|
913
|
+
return (
|
|
914
|
+
f" def test_{class_name.lower()}_{method_name}(self, {class_name.lower()}_instance):\n"
|
|
915
|
+
f' """Test {class_name}.{method_name} method."""\n'
|
|
916
|
+
" try:\n"
|
|
917
|
+
f" method = getattr({class_name.lower()}_instance, "
|
|
918
|
+
f'"{method_name}", None)\n'
|
|
919
|
+
f" assert method is not None, "
|
|
920
|
+
f'f"Method {method_name} should exist"\n'
|
|
921
|
+
"\n"
|
|
922
|
+
" result = method()\n"
|
|
923
|
+
" assert result is not None or result is None\n"
|
|
924
|
+
"\n"
|
|
925
|
+
" except (TypeError, NotImplementedError):\n"
|
|
926
|
+
f' pytest.skip(f"Method {method_name} requires specific arguments or implementation")\n'
|
|
927
|
+
" except Exception as e:\n"
|
|
928
|
+
f' pytest.fail(f"Unexpected error in {method_name}: {{e}}")'
|
|
929
|
+
)
|
|
930
|
+
|
|
931
|
+
async def _generate_class_property_test(
|
|
932
|
+
self, cls: dict[str, Any], module_category: str
|
|
933
|
+
) -> str:
|
|
934
|
+
"""Generate class property test."""
|
|
935
|
+
class_name = cls["name"]
|
|
936
|
+
|
|
937
|
+
if module_category not in ("service", "manager", "agent"):
|
|
938
|
+
return ""
|
|
939
|
+
|
|
940
|
+
test_template = (
|
|
941
|
+
f" def test_{class_name.lower()}_properties(self, {class_name.lower()}_instance):\n"
|
|
942
|
+
f' """Test {class_name} properties and attributes."""\n'
|
|
943
|
+
"\n"
|
|
944
|
+
f" assert hasattr({class_name.lower()}_instance, '__dict__') or \\\n"
|
|
945
|
+
f" hasattr({class_name.lower()}_instance, '__slots__')\n"
|
|
946
|
+
"\n"
|
|
947
|
+
f" str_repr = str({class_name.lower()}_instance)\n"
|
|
948
|
+
" assert len(str_repr) > 0\n"
|
|
949
|
+
f' assert "{class_name}" in str_repr or "{class_name.lower()}" in \\\n'
|
|
950
|
+
" str_repr.lower()"
|
|
951
|
+
)
|
|
952
|
+
|
|
953
|
+
return test_template
|
|
954
|
+
|
|
955
|
+
async def _generate_integration_tests(
|
|
956
|
+
self,
|
|
957
|
+
module_file: Path,
|
|
958
|
+
functions: list[dict[str, Any]],
|
|
959
|
+
classes: list[dict[str, Any]],
|
|
960
|
+
module_category: str,
|
|
961
|
+
) -> str:
|
|
962
|
+
"""Generate integration tests."""
|
|
963
|
+
if module_category not in ("service", "manager", "core"):
|
|
964
|
+
return ""
|
|
965
|
+
|
|
966
|
+
if len(functions) < 3 and len(classes) < 2:
|
|
967
|
+
return ""
|
|
968
|
+
|
|
969
|
+
integration_tests = (
|
|
970
|
+
"\n\n"
|
|
971
|
+
" @pytest.mark.integration\n"
|
|
972
|
+
f" def test_{module_file.stem}_integration(self):\n"
|
|
973
|
+
f' """Integration test for {module_file.stem} module functionality."""\n'
|
|
974
|
+
"\n"
|
|
975
|
+
' pytest.skip("Integration test needs manual implementation")\n'
|
|
976
|
+
"\n"
|
|
977
|
+
" @pytest.mark.integration\n"
|
|
978
|
+
" @pytest.mark.asyncio\n"
|
|
979
|
+
f" async def test_{module_file.stem}_async_integration(self):\n"
|
|
980
|
+
f' """Async integration test for {module_file.stem} module."""\n'
|
|
981
|
+
"\n"
|
|
982
|
+
' pytest.skip("Async integration test needs manual implementation")\n'
|
|
983
|
+
"\n"
|
|
984
|
+
" @pytest.mark.performance\n"
|
|
985
|
+
f" def test_{module_file.stem}_performance(self):\n"
|
|
986
|
+
f' """Basic performance test for {module_file.stem} module."""\n'
|
|
987
|
+
"\n"
|
|
988
|
+
' pytest.skip("Performance test needs manual implementation")'
|
|
989
|
+
)
|
|
990
|
+
|
|
991
|
+
return integration_tests
|
|
992
|
+
|
|
993
|
+
def _generate_default_args(self, args: list[str]) -> str:
|
|
994
|
+
"""Generate default arguments."""
|
|
995
|
+
if not args or args == ["self"]:
|
|
996
|
+
return ""
|
|
997
|
+
|
|
998
|
+
filtered_args = [arg for arg in args if arg != "self"]
|
|
999
|
+
if not filtered_args:
|
|
1000
|
+
return ""
|
|
1001
|
+
|
|
1002
|
+
placeholders = []
|
|
1003
|
+
for arg in filtered_args:
|
|
1004
|
+
if "path" in arg.lower():
|
|
1005
|
+
placeholders.append('Path("test")')
|
|
1006
|
+
elif "str" in arg.lower() or "name" in arg.lower():
|
|
1007
|
+
placeholders.append('"test"')
|
|
1008
|
+
elif "int" in arg.lower() or "count" in arg.lower():
|
|
1009
|
+
placeholders.append("1")
|
|
1010
|
+
elif "bool" in arg.lower():
|
|
1011
|
+
placeholders.append("True")
|
|
1012
|
+
else:
|
|
1013
|
+
placeholders.append("None")
|
|
1014
|
+
|
|
1015
|
+
return ", ".join(placeholders)
|
|
1016
|
+
|
|
1017
|
+
def _categorize_module(self, relative_path: str) -> str:
|
|
1018
|
+
"""Categorize module by path."""
|
|
1019
|
+
if "managers/" in relative_path:
|
|
1020
|
+
return "manager"
|
|
1021
|
+
elif "services/" in relative_path:
|
|
1022
|
+
return "service"
|
|
1023
|
+
elif "core/" in relative_path:
|
|
1024
|
+
return "core"
|
|
1025
|
+
elif "agents/" in relative_path:
|
|
1026
|
+
return "agent"
|
|
1027
|
+
elif "models/" in relative_path:
|
|
1028
|
+
return "model"
|
|
1029
|
+
elif "executors/" in relative_path:
|
|
1030
|
+
return "executor"
|
|
1031
|
+
return "utility"
|